]> granicus.if.org Git - esp-idf/commitdiff
wpa_supplicant:move part of codes to IDF
authorDeng Xin <dengxin@espressif.com>
Fri, 20 Apr 2018 03:33:04 +0000 (11:33 +0800)
committerDeng Xin <dengxin@espressif.com>
Wed, 9 May 2018 02:44:35 +0000 (10:44 +0800)
64 files changed:
components/esp32/include/esp_wpa2.h
components/esp32/lib
components/wpa_supplicant/component.mk
components/wpa_supplicant/include/wpa/list.h
components/wpa_supplicant/include/wpa/wpa.h
components/wpa_supplicant/include/wpa/wpa_debug.h
components/wpa_supplicant/include/wpa2/eap_peer/eap.h
components/wpa_supplicant/include/wpa2/eap_peer/eap_config.h
components/wpa_supplicant/include/wpa2/eap_peer/eap_i.h
components/wpa_supplicant/include/wpa2/eap_peer/eap_methods.h [new file with mode: 0644]
components/wpa_supplicant/include/wpa2/eap_peer/eap_peap_common.h [new file with mode: 0644]
components/wpa_supplicant/include/wpa2/eap_peer/eap_tlv_common.h [new file with mode: 0644]
components/wpa_supplicant/include/wpa2/eap_peer/eap_ttls.h [new file with mode: 0644]
components/wpa_supplicant/include/wpa2/eap_peer/mschapv2.h [new file with mode: 0644]
components/wpa_supplicant/include/wpa2/tls/libtommath.h
components/wpa_supplicant/include/wpa2/utils/base64.h
components/wpa_supplicant/include/wpa2/utils/ext_password.h
components/wpa_supplicant/include/wps/utils/uuid.h [new file with mode: 0644]
components/wpa_supplicant/include/wps/wps.h [new file with mode: 0644]
components/wpa_supplicant/include/wps/wps_attr_parse.h [new file with mode: 0644]
components/wpa_supplicant/include/wps/wps_defs.h [new file with mode: 0644]
components/wpa_supplicant/include/wps/wps_dev_attr.h [new file with mode: 0644]
components/wpa_supplicant/include/wps/wps_i.h [new file with mode: 0644]
components/wpa_supplicant/port/include/os.h
components/wpa_supplicant/src/wpa2/eap_peer/chap.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/eap_peer/eap.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/eap_peer/eap_common.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/eap_peer/eap_mschapv2.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/eap_peer/eap_peap.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/eap_peer/eap_peap_common.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/eap_peer/eap_tls.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/eap_peer/eap_tls_common.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/eap_peer/eap_ttls.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/eap_peer/mschapv2.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/tls/asn1.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/tls/bignum.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/tls/pkcs1.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/tls/pkcs5.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/tls/pkcs8.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/tls/rsa.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/tls/tls_internal.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/tls/tlsv1_client.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/tls/tlsv1_client_read.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/tls/tlsv1_client_write.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/tls/tlsv1_common.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/tls/tlsv1_cred.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/tls/tlsv1_record.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/tls/tlsv1_server.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/tls/tlsv1_server_read.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/tls/tlsv1_server_write.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/tls/x509v3.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/utils/base64.c [new file with mode: 0644]
components/wpa_supplicant/src/wpa2/utils/ext_password.c [new file with mode: 0644]
components/wpa_supplicant/src/wps/eap_common.c [new file with mode: 0644]
components/wpa_supplicant/src/wps/uuid.c [new file with mode: 0644]
components/wpa_supplicant/src/wps/wps.c [new file with mode: 0644]
components/wpa_supplicant/src/wps/wps_attr_build.c [new file with mode: 0644]
components/wpa_supplicant/src/wps/wps_attr_parse.c [new file with mode: 0644]
components/wpa_supplicant/src/wps/wps_attr_process.c [new file with mode: 0644]
components/wpa_supplicant/src/wps/wps_common.c [new file with mode: 0644]
components/wpa_supplicant/src/wps/wps_dev_attr.c [new file with mode: 0644]
components/wpa_supplicant/src/wps/wps_enrollee.c [new file with mode: 0644]
components/wpa_supplicant/src/wps/wps_registrar.c [new file with mode: 0644]
components/wpa_supplicant/src/wps/wps_validate.c [new file with mode: 0644]

index fa73dbe28702c0df97e26d92dac27b35e0a73d81..1b2dfa5103875b8b795847c16eaf3619d2cdb902 100644 (file)
@@ -15,6 +15,8 @@
 #ifndef ESP_WPA2_H
 #define ESP_WPA2_H
 
+#include <stdbool.h>
+
 #include "esp_err.h"
 #include "esp_wifi_crypto_types.h"
 
@@ -121,7 +123,7 @@ void esp_wifi_sta_wpa2_ent_clear_password(void);
   * @attention 1. The API only passes the parameter password to the global pointer variable in wpa2 enterprise module.
   * @attention 2. The new password is used to substitute the old password when eap-mschapv2 failure request message with error code ERROR_PASSWD_EXPIRED is received.
   *
-  * @param  password: point to address where stores the password;
+  * @param  new_password: point to address where stores the password;
   * @param  len: length of password
   *
   * @return
@@ -130,7 +132,7 @@ void esp_wifi_sta_wpa2_ent_clear_password(void);
   *    - ESP_ERR_NO_MEM: fail(internal memory malloc fail)
   */
 
-esp_err_t esp_wifi_sta_wpa2_ent_set_new_password(const unsigned char *password, int len);
+esp_err_t esp_wifi_sta_wpa2_ent_set_new_password(const unsigned char *new_password, int len);
 
 /**
   * @brief  Clear new password for MSCHAPv2 method..
@@ -144,12 +146,12 @@ void esp_wifi_sta_wpa2_ent_clear_new_password(void);
   * @attention 2. The ca_cert should be zero terminated.
   *
   * @param  ca_cert: point to address where stores the CA certificate;
-  * @param  len: length of ca_cert
+  * @param  ca_cert_len: length of ca_cert
   *
   * @return
   *    - ESP_OK: succeed
   */
-esp_err_t esp_wifi_sta_wpa2_ent_set_ca_cert(const unsigned char *ca_cert, int len);
+esp_err_t esp_wifi_sta_wpa2_ent_set_ca_cert(const unsigned char *ca_cert, int ca_cert_len);
 
 /**
   * @brief  Clear CA certificate for PEAP/TTLS method.
index f46327a4c68b1e4e6a3fb3febd3ba3312df16ff1..92a091649f7c8476580177e2a769b15d9c4c191f 160000 (submodule)
@@ -1 +1 @@
-Subproject commit f46327a4c68b1e4e6a3fb3febd3ba3312df16ff1
+Subproject commit 92a091649f7c8476580177e2a769b15d9c4c191f
index f34cb95f319dce64a37f6f79eff51f8eb0a1cff3..d2120dbc3996f8c987acef5521aad561c81da7b6 100644 (file)
@@ -1,4 +1,4 @@
 COMPONENT_ADD_INCLUDEDIRS := include port/include ../esp32/include
-COMPONENT_SRCDIRS := src/crypto port src/fast_crypto
+COMPONENT_SRCDIRS := src/crypto port src/fast_crypto src/wpa2/eap_peer src/wpa2/tls src/wpa2/utils src/wps
 
-CFLAGS += -DEMBEDDED_SUPP -D__ets__ -Wno-strict-aliasing
+CFLAGS += -DEMBEDDED_SUPP -DIEEE8021X_EAPOL -DEAP_PEER_METHOD -DEAP_MSCHAPv2 -DEAP_TTLS -DEAP_TLS -DEAP_PEAP -DUSE_WPA2_TASK -DCONFIG_WPS2 -DCONFIG_WPS_PIN -DUSE_WPS_TASK -DESPRESSIF_USE -DESP32_WORKAROUND -D__ets__ -Wno-strict-aliasing
index c8dccee83ddc5a7dccf57a0bd986e40cbc50c357..a67a04966c634d1a4997ce8c2f882f94f0b4971c 100644 (file)
@@ -15,6 +15,8 @@
 #ifndef LIST_H
 #define LIST_H
 
+#include <stddef.h>
+
 /**
  * struct dl_list - Doubly-linked list
  */
index 2a1adfc56712259f596ad98f8d0ea98765e3e443..9d50bb1ba573ea19406ecd170ab6c5f558296a17 100644 (file)
 #ifndef WPA_H
 #define WPA_H
 
-#include "c_types.h"
-#include "os_type.h"
+#include "rom/ets_sys.h"
 #include "common.h"
-#include "ets_sys.h"
 #include "wpa/defs.h"
 #include "wpa/wpa_common.h"
 
-//#include "net80211/ieee80211_var.h"
-//#include "net80211/ieee80211_node.h"
 
 #define WPA_SM_STATE(_sm) ((_sm)->wpa_state)
 
@@ -51,10 +47,6 @@ struct wpa_sm {
     u8 pmk[PMK_LEN];
     size_t pmk_len;
 
-//    char *passphrase;  //wlan password
-//    u8 *ssid;               //wlan network name
-//    size_t ssid_len;
-
     struct wpa_ptk ptk, tptk;
     int ptk_set, tptk_set;
     u8 snonce[WPA_NONCE_LEN];
@@ -64,8 +56,6 @@ struct wpa_sm {
     int rx_replay_counter_set;
     u8 request_counter[WPA_REPLAY_COUNTER_LEN];
 
-//    void *network_ctx;
-
     unsigned int pairwise_cipher;
     unsigned int group_cipher;
     unsigned int key_mgmt;
@@ -74,7 +64,7 @@ struct wpa_sm {
     int rsn_enabled; /* Whether RSN is enabled in configuration */
 
     int countermeasures; /*TKIP countermeasures state flag, 1:in countermeasures state*/
-    os_timer_t  cm_timer;
+    ETSTimer  cm_timer;
 
     u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */
     size_t assoc_wpa_ie_len;
@@ -95,21 +85,17 @@ struct wpa_sm {
     struct install_key install_ptk;
     struct install_key install_gtk;
     int  key_entry_valid;   //present current avaliable entry for bssid, for pairkey:0,5,10,15,20, gtk: pairkey_no+i (i:1~4)
-       
-//    char *msg;   //send eapol msg buff
-//    size_t msg_len;  //msg length:6 + sizeof(eth) + data_len
 
-//    struct netif *ifp;
     struct pbuf *pb;
 
     void (* sendto) (struct pbuf *pb);
-    void (*config_assoc_ie) (uint8 proto, u8 *assoc_buf, u32 assoc_wpa_ie_len);
-    void (*install_ppkey) (enum wpa_alg alg, uint8 *addr, int key_idx, int set_tx,
-               uint8 *seq, size_t seq_len, uint8 *key, size_t key_len, int key_entry_valid);
-    void (*wpa_deauthenticate)(uint8 reason_code);
+    void (*config_assoc_ie) (u8 proto, u8 *assoc_buf, u32 assoc_wpa_ie_len);
+    void (*install_ppkey) (enum wpa_alg alg, u8 *addr, int key_idx, int set_tx,
+               u8 *seq, unsigned int seq_len, u8 *key, unsigned int key_len, int key_entry_valid);
+    void (*wpa_deauthenticate)(u8 reason_code);
     void (*wpa_neg_complete)();
     struct wpa_gtk_data gd; //used for calllback save param
-    uint16 key_info;   //used for txcallback param    
+    u16 key_info;      //used for txcallback param    
 };
 
 struct l2_ethhdr {
@@ -185,9 +171,9 @@ struct l2_ethhdr {
 
 #define KEYENTRY_TABLE_MAP(key_entry_valid)  ((key_entry_valid)%5) 
 
-void pp_michael_mic_failure(uint16 isunicast);
-
 void wpa_sm_set_state(enum wpa_states state);
 
+char * dup_binstr(const void *src, size_t len);
+
 #endif /* WPA_H */
 
index b78a657e05c1245a77bc2d7016d96ade86df32d1..10fe928c3eb8add95b73a8e0a64b63006858dcea 100644 (file)
 #ifndef WPA_DEBUG_H
 #define WPA_DEBUG_H
 
+#include "wpabuf.h"
+#include "esp_log.h"
 
+#ifdef ESPRESSIF_USE
+
+#define TAG "wpa"
+
+#define MSG_ERROR ESP_LOG_ERROR
+#define MSG_WARNING ESP_LOG_WARN
+#define MSG_INFO ESP_LOG_INFO
+#define MSG_DEBUG ESP_LOG_DEBUG
+#define MSG_MSGDUMP ESP_LOG_VERBOSE
+
+#else 
 enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR };
+#endif
 
 /** EAP authentication completed successfully */
 #define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS "
@@ -44,8 +58,8 @@ void wpa_debug_print_timestamp(void);
  *
  * Note: New line '\n' is added to the end of the text when printing to stdout.
  */
-//#define  DEBUG_PRINT
-//#define   MSG_PRINT
+#define  DEBUG_PRINT
+#define   MSG_PRINT
 
 /**
  * wpa_hexdump - conditional hex dump
@@ -59,7 +73,7 @@ void wpa_debug_print_timestamp(void);
  * configuration. The contents of buf is printed out has hex dump.
  */
 #ifdef DEBUG_PRINT
-#define wpa_printf(level,fmt, args...) ets_printf(fmt,## args)
+#define wpa_printf(level,fmt, args...) ESP_LOG_LEVEL_LOCAL(level, TAG, fmt, ##args)
 
 static inline void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len)
 {
index e2cd2dd81d52acf4e39995bd4306b19b7c1b3a8e..9e1c3efa94f826993ecb907b03e33d7e90e3a4e8 100644 (file)
 #define EAP_H
 
 #include "wpa/defs.h"
-#include "eap/eap_defs.h"
+#include "wpa2/eap_peer/eap_defs.h"
 
 struct eap_sm;
 
 struct eap_method_type {
        int vendor;
-       u32 method;
+       EapType method;
 };
 
+u8 *g_wpa_anonymous_identity;
+int g_wpa_anonymous_identity_len;
+u8 *g_wpa_username;
+int g_wpa_username_len;
+const u8 *g_wpa_client_cert;
+int g_wpa_client_cert_len;
+const u8 *g_wpa_private_key;
+int g_wpa_private_key_len;
+const u8 *g_wpa_private_key_passwd;
+int g_wpa_private_key_passwd_len;
+
+const u8 *g_wpa_ca_cert;
+int g_wpa_ca_cert_len;
+
+u8 *g_wpa_password;
+int g_wpa_password_len;
+
+u8 *g_wpa_new_password;
+int g_wpa_new_password_len;
+
 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);
+int eap_peer_blob_init(struct eap_sm *sm);
+void eap_peer_blob_deinit(struct eap_sm *sm);
+int eap_peer_config_init(
+       struct eap_sm *sm, u8 *private_key_passwd,
+       int private_key_passwd_len);
+void eap_peer_config_deinit(struct eap_sm *sm);
+void eap_sm_abort(struct eap_sm *sm);
+int eap_peer_register_methods(void);
 
 #endif /* EAP_H */
index f35cbf43d7687f622880c2815dbe740a04e8843b..f95dcda3a120d918c8775e745b89e0c742dc78a9 100644 (file)
@@ -26,6 +26,10 @@ struct eap_peer_config {
         */
        size_t identity_len;
 
+       u8 *anonymous_identity;
+
+       size_t anonymous_identity_len;
+
        /**
         * password - Password string for EAP
         *
@@ -139,8 +143,29 @@ struct eap_peer_config {
         */
        u8 *private_key_passwd;
 
+       /**
+        * Phase 2
+        */
+       u8 *ca_cert2;
+
+       u8 *ca_path2;
+
+       u8 *client_cert2;
+
+       u8 *private_key2;
+
+       u8 *private_key2_password;
+
+       /**
+        * eap_methods - Allowed EAP methods
+        */
+       struct eap_method_type *eap_methods;
+
+
        char *phase1;
 
+       char *phase2;
+
        /**
         * pin - PIN for USIM, GSM SIM, and smartcards
         *
@@ -152,6 +177,10 @@ struct eap_peer_config {
         */
        char *pin;
 
+       int mschapv2_retry;
+       u8 *new_password;
+       size_t new_password_len;
+
        /**
         * fragment_size - Maximum EAP fragment size in bytes (default 1398)
         *
@@ -204,7 +233,7 @@ struct wpa_config_blob {
        /**
         * data - Pointer to binary data
         */
-       u8 *data;
+       const u8 *data;
 
        /**
         * len - Length of binary data
index a4779d13f825325100780744f5fb9d9d95b27a90..6204f46538529a3fe9d28d48501019c5c949319e 100644 (file)
@@ -13,6 +13,7 @@
 #include "eap.h"
 #include "eap_common.h"
 #include "eap_config.h"
+#include "esp_wpa2.h"
 
 /* RFC 4137 - EAP Peer state machine */
 
@@ -54,11 +55,48 @@ struct eap_method_ret {
        Boolean allowNotifications;
 };
 
+struct eap_sm;
+
+struct eap_method {
+       /**
+        * vendor -EAP Vendor-ID
+        */
+       int vendor;
+
+       /**
+        * method - EAP type number
+        */
+       EapType method; 
+
+       /**
+        * name - Name of the method (e.g., "TLS")
+        */
+       const char *name;
+
+       struct eap_method *next;
+
+       void * (*init)(struct eap_sm *sm);
+       void (*deinit)(struct eap_sm *sm, void *priv);
+       struct wpabuf * (*process)(struct eap_sm *sm, void *priv,
+                                  struct eap_method_ret *ret,
+                                  const struct wpabuf *reqData);
+       bool (*isKeyAvailable)(struct eap_sm *sm, void *priv);
+       u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len);
+       int (*get_status)(struct eap_sm *sm, void *priv, char *buf,
+                         size_t buflen, int verbose);
+       const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len);
+       void (*free)(struct eap_method *method);
+       bool (*has_reauth_data)(struct eap_sm *sm, void *priv);
+       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);
+};
+
 #define CLIENT_CERT_NAME       "CLC"
 #define CA_CERT_NAME           "CAC"
 #define PRIVATE_KEY_NAME       "PVK"
 #define BLOB_NAME_LEN          3
-#define BLOB_NUM               2
+#define BLOB_NUM               3
 
 /**
  * struct eap_sm - EAP state machine data
@@ -80,9 +118,26 @@ struct eap_sm {
        u8 wpa2_sig_cnt[SIG_WPA2_NUM];
 #endif
        u8 finish_state;
+
+       int init_phase2;
+       bool peap_done;
+
+       u8 *eapKeyData;
+       size_t eapKeyDataLen;
+       struct wpabuf *lastRespData;
+       const struct eap_method *m;
 };
 
+wpa2_crypto_funcs_t wpa2_crypto_funcs;
+
+const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len);
+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);
 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);
+
+struct wpabuf * eap_sm_build_identity_resp(struct eap_sm *sm, u8 id, int encrypted);
 
 #endif /* EAP_I_H */
diff --git a/components/wpa_supplicant/include/wpa2/eap_peer/eap_methods.h b/components/wpa_supplicant/include/wpa2/eap_peer/eap_methods.h
new file mode 100644 (file)
index 0000000..baa493d
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * EAP peer: Method registration
+ * 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_METHODS_H
+#define EAP_METHODS_H
+
+#include "eap_defs.h"
+
+const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method);
+const struct eap_method * eap_peer_get_methods(size_t *count);
+
+u32 eap_get_phase2_type(const char *name, int *vendor);
+struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config,
+                                             size_t *count);
+
+struct eap_method * eap_peer_method_alloc(int verdor, EapType method,
+                                         const char *name);
+
+void eap_peer_method_free(struct eap_method *method);
+int eap_peer_method_register(struct eap_method *method);
+
+void eap_peer_unregister_methods(void);
+
+//int eap_peer_md5_register(void);
+int eap_peer_tls_register(void);
+int eap_peer_peap_register(void);
+int eap_peer_ttls_register(void);
+int eap_peer_mschapv2_register(void);
+
+#endif /* EAP_METHODS_H */
diff --git a/components/wpa_supplicant/include/wpa2/eap_peer/eap_peap_common.h b/components/wpa_supplicant/include/wpa2/eap_peer/eap_peap_common.h
new file mode 100644 (file)
index 0000000..c0dc6ed
--- /dev/null
@@ -0,0 +1,16 @@
+/*\r
+ * EAP-PEAP common routines\r
+ * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>\r
+ *\r
+ * This software may be distributed under the terms of the BSD license.\r
+ * See README for more details.\r
+ */\r
+\r
+#ifndef EAP_PEAP_COMMON_H\r
+#define EAP_PEAP_COMMON_H\r
+\r
+int peap_prfplus(int version, const u8 *key, size_t key_len,\r
+                const char *label, const u8 *seed, size_t seed_len,\r
+                u8 *buf, size_t buf_len);\r
+\r
+#endif /* EAP_PEAP_COMMON_H */\r
diff --git a/components/wpa_supplicant/include/wpa2/eap_peer/eap_tlv_common.h b/components/wpa_supplicant/include/wpa2/eap_peer/eap_tlv_common.h
new file mode 100644 (file)
index 0000000..8f0f83f
--- /dev/null
@@ -0,0 +1,112 @@
+/*\r
+ * EAP-TLV definitions (draft-josefsson-pppext-eap-tls-eap-10.txt)\r
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>\r
+ *\r
+ * This software may be distributed under the terms of the BSD license.\r
+ * See README for more details.\r
+ */\r
+\r
+#ifndef EAP_TLV_COMMON_H\r
+#define EAP_TLV_COMMON_H\r
+\r
+/* EAP-TLV TLVs (draft-josefsson-ppext-eap-tls-eap-10.txt) */\r
+#define EAP_TLV_RESULT_TLV 3 /* Acknowledged Result */\r
+#define EAP_TLV_NAK_TLV 4\r
+#define EAP_TLV_ERROR_CODE_TLV 5\r
+#define EAP_TLV_CONNECTION_BINDING_TLV 6\r
+#define EAP_TLV_VENDOR_SPECIFIC_TLV 7\r
+#define EAP_TLV_URI_TLV 8\r
+#define EAP_TLV_EAP_PAYLOAD_TLV 9\r
+#define EAP_TLV_INTERMEDIATE_RESULT_TLV 10\r
+#define EAP_TLV_PAC_TLV 11 /* RFC 5422, Section 4.2 */\r
+#define EAP_TLV_CRYPTO_BINDING_TLV 12\r
+#define EAP_TLV_CALLING_STATION_ID_TLV 13\r
+#define EAP_TLV_CALLED_STATION_ID_TLV 14\r
+#define EAP_TLV_NAS_PORT_TYPE_TLV 15\r
+#define EAP_TLV_SERVER_IDENTIFIER_TLV 16\r
+#define EAP_TLV_IDENTITY_TYPE_TLV 17\r
+#define EAP_TLV_SERVER_TRUSTED_ROOT_TLV 18\r
+#define EAP_TLV_REQUEST_ACTION_TLV 19\r
+#define EAP_TLV_PKCS7_TLV 20\r
+\r
+#define EAP_TLV_RESULT_SUCCESS 1\r
+#define EAP_TLV_RESULT_FAILURE 2\r
+\r
+#define EAP_TLV_TYPE_MANDATORY 0x8000\r
+#define EAP_TLV_TYPE_MASK 0x3fff\r
+\r
+#ifdef _MSC_VER\r
+#pragma pack(push, 1)\r
+#endif /* _MSC_VER */\r
+\r
+struct eap_tlv_hdr {\r
+       be16 tlv_type;\r
+       be16 length;\r
+} STRUCT_PACKED;\r
+\r
+struct eap_tlv_nak_tlv {\r
+       be16 tlv_type;\r
+       be16 length;\r
+       be32 vendor_id;\r
+       be16 nak_type;\r
+} STRUCT_PACKED;\r
+\r
+struct eap_tlv_result_tlv {\r
+       be16 tlv_type;\r
+       be16 length;\r
+       be16 status;\r
+} STRUCT_PACKED;\r
+\r
+/* RFC 4851, Section 4.2.7 - Intermediate-Result TLV */\r
+struct eap_tlv_intermediate_result_tlv {\r
+       be16 tlv_type;\r
+       be16 length;\r
+       be16 status;\r
+       /* Followed by optional TLVs */\r
+} STRUCT_PACKED;\r
+\r
+/* RFC 4851, Section 4.2.8 - Crypto-Binding TLV */\r
+struct eap_tlv_crypto_binding_tlv {\r
+       be16 tlv_type;\r
+       be16 length;\r
+       u8 reserved;\r
+       u8 version;\r
+       u8 received_version;\r
+       u8 subtype;\r
+       u8 nonce[32];\r
+       u8 compound_mac[20];\r
+} STRUCT_PACKED;\r
+\r
+struct eap_tlv_pac_ack_tlv {\r
+       be16 tlv_type;\r
+       be16 length;\r
+       be16 pac_type;\r
+       be16 pac_len;\r
+       be16 result;\r
+} STRUCT_PACKED;\r
+\r
+/* RFC 4851, Section 4.2.9 - Request-Action TLV */\r
+struct eap_tlv_request_action_tlv {\r
+       be16 tlv_type;\r
+       be16 length;\r
+       be16 action;\r
+} STRUCT_PACKED;\r
+\r
+/* RFC 5422, Section 4.2.6 - PAC-Type TLV */\r
+struct eap_tlv_pac_type_tlv {\r
+       be16 tlv_type; /* PAC_TYPE_PAC_TYPE */\r
+       be16 length;\r
+       be16 pac_type;\r
+} STRUCT_PACKED;\r
+\r
+#ifdef _MSC_VER\r
+#pragma pack(pop)\r
+#endif /* _MSC_VER */\r
+\r
+#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST 0\r
+#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE 1\r
+\r
+#define EAP_TLV_ACTION_PROCESS_TLV 1\r
+#define EAP_TLV_ACTION_NEGOTIATE_EAP 2\r
+\r
+#endif /* EAP_TLV_COMMON_H */\r
diff --git a/components/wpa_supplicant/include/wpa2/eap_peer/eap_ttls.h b/components/wpa_supplicant/include/wpa2/eap_peer/eap_ttls.h
new file mode 100644 (file)
index 0000000..568038d
--- /dev/null
@@ -0,0 +1,65 @@
+/*\r
+ * EAP server/peer: EAP-TTLS (RFC 5281)\r
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>\r
+ *\r
+ * This software may be distributed under the terms of the BSD license.\r
+ * See README for more details.\r
+ */\r
+\r
+#ifndef EAP_TTLS_H\r
+#define EAP_TTLS_H\r
+\r
+struct ttls_avp {\r
+       be32 avp_code;\r
+       be32 avp_length; /* 8-bit flags, 24-bit length;\r
+                         * length includes AVP header */\r
+       /* optional 32-bit Vendor-ID */\r
+       /* Data */\r
+};\r
+\r
+struct ttls_avp_vendor {\r
+       be32 avp_code;\r
+       be32 avp_length; /* 8-bit flags, 24-bit length;\r
+                         * length includes AVP header */\r
+       be32 vendor_id;\r
+       /* Data */\r
+};\r
+\r
+#define AVP_FLAGS_VENDOR 0x80\r
+#define AVP_FLAGS_MANDATORY 0x40\r
+\r
+#define AVP_PAD(start, pos) \\r
+do { \\r
+       int __pad; \\r
+       __pad = (4 - (((pos) - (start)) & 3)) & 3; \\r
+       os_memset((pos), 0, __pad); \\r
+       pos += __pad; \\r
+} while (0)\r
+\r
+\r
+/* RFC 2865 */\r
+#define RADIUS_ATTR_USER_NAME 1\r
+#define RADIUS_ATTR_USER_PASSWORD 2\r
+#define RADIUS_ATTR_CHAP_PASSWORD 3\r
+#define RADIUS_ATTR_REPLY_MESSAGE 18\r
+#define RADIUS_ATTR_CHAP_CHALLENGE 60\r
+#define RADIUS_ATTR_EAP_MESSAGE 79\r
+\r
+/* RFC 2548 */\r
+#define RADIUS_VENDOR_ID_MICROSOFT 311\r
+#define RADIUS_ATTR_MS_CHAP_RESPONSE 1\r
+#define RADIUS_ATTR_MS_CHAP_ERROR 2\r
+#define RADIUS_ATTR_MS_CHAP_NT_ENC_PW 6\r
+#define RADIUS_ATTR_MS_CHAP_CHALLENGE 11\r
+#define RADIUS_ATTR_MS_CHAP2_RESPONSE 25\r
+#define RADIUS_ATTR_MS_CHAP2_SUCCESS 26\r
+#define RADIUS_ATTR_MS_CHAP2_CPW 27\r
+\r
+#define EAP_TTLS_MSCHAPV2_CHALLENGE_LEN 16\r
+#define EAP_TTLS_MSCHAPV2_RESPONSE_LEN 50\r
+#define EAP_TTLS_MSCHAP_CHALLENGE_LEN 8\r
+#define EAP_TTLS_MSCHAP_RESPONSE_LEN 50\r
+#define EAP_TTLS_CHAP_CHALLENGE_LEN 16\r
+#define EAP_TTLS_CHAP_PASSWORD_LEN 16\r
+\r
+#endif /* EAP_TTLS_H */\r
diff --git a/components/wpa_supplicant/include/wpa2/eap_peer/mschapv2.h b/components/wpa_supplicant/include/wpa2/eap_peer/mschapv2.h
new file mode 100644 (file)
index 0000000..ef75081
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * MSCHAPV2
+ */
+
+
+#ifndef MSCHAPV2_H
+#define MSCHAPV2_H
+
+#define MSCHAPV2_CHAL_LEN              16
+#define MSCHAPV2_NT_RESPONSE_LEN       24
+#define MSCHAPV2_AUTH_RESPONSE_LEN     20
+#define MSCHAPV2_MASTER_KEY_LEN                16
+
+const u8 * mschapv2_remove_domain(const u8 *username, size_t *len);
+int mschapv2_derive_response(const u8 *username, size_t username_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);
+int mschapv2_verify_auth_response(const u8 *auth_response,
+                                 const u8 *buf, size_t buf_len);
+#endif /* MSCHAPV2_H */
index c0409b5e33f49b70f62918d008f5a709d7669fd5..9be311e86a66671ac6ff43181cfeb4d7acbd5916 100644 (file)
@@ -13,7 +13,6 @@
  * If CONFIG_INTERNAL_LIBTOMMATH is defined, bignum.c includes this
  * libtommath.c file instead of using the external LibTomMath library.
  */
-#include "c_types.h"
 #include "os.h"
 #include "stdarg.h"
 
@@ -193,7 +192,7 @@ static int mp_mul_d (mp_int * a, mp_digit b, mp_int * c);
 
 
 /* reverse an array, used for radix code */
-static void ICACHE_FLASH_ATTR
+static void
 bn_reverse (unsigned char *s, int len)
 {
   int     ix, iy;
@@ -212,7 +211,7 @@ bn_reverse (unsigned char *s, int len)
 
 
 /* low level addition, based on HAC pp.594, Algorithm 14.7 */
-static int ICACHE_FLASH_ATTR
+static int
 s_mp_add (mp_int * a, mp_int * b, mp_int * c)
 {
   mp_int *x;
@@ -301,7 +300,7 @@ s_mp_add (mp_int * a, mp_int * b, mp_int * c)
 
 
 /* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */
-static int ICACHE_FLASH_ATTR
+static int
 s_mp_sub (mp_int * a, mp_int * b, mp_int * c)
 {
   int     olduse, res, min, max;
@@ -369,7 +368,7 @@ s_mp_sub (mp_int * a, mp_int * b, mp_int * c)
 
 
 /* init a new mp_int */
-static int ICACHE_FLASH_ATTR
+static int
 mp_init (mp_int * a)
 {
   int i;
@@ -396,7 +395,7 @@ mp_init (mp_int * a)
 
 
 /* clear one (frees)  */
-static void ICACHE_FLASH_ATTR
+static void
 mp_clear (mp_int * a)
 {
   int i;
@@ -420,7 +419,7 @@ mp_clear (mp_int * a)
 
 
 /* high level addition (handles signs) */
-static int ICACHE_FLASH_ATTR
+static int
 mp_add (mp_int * a, mp_int * b, mp_int * c)
 {
   int     sa, sb, res;
@@ -453,7 +452,7 @@ mp_add (mp_int * a, mp_int * b, mp_int * c)
 
 
 /* high level subtraction (handles signs) */
-static int ICACHE_FLASH_ATTR
+static int
 mp_sub (mp_int * a, mp_int * b, mp_int * c)
 {
   int     sa, sb, res;
@@ -491,7 +490,7 @@ mp_sub (mp_int * a, mp_int * b, mp_int * c)
 
 
 /* high level multiplication (handles sign) */
-static int ICACHE_FLASH_ATTR
+static int
 mp_mul (mp_int * a, mp_int * b, mp_int * c)
 {
   int     res, neg;
@@ -539,7 +538,7 @@ mp_mul (mp_int * a, mp_int * b, mp_int * c)
 
 
 /* d = a * b (mod c) */
-static int ICACHE_FLASH_ATTR
+static int
 mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
 {
   int     res;
@@ -560,7 +559,7 @@ mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
 
 
 /* c = a mod b, 0 <= c < b */
-static int ICACHE_FLASH_ATTR
+static int
 mp_mod (mp_int * a, mp_int * b, mp_int * c)
 {
   mp_int  t;
@@ -592,10 +591,12 @@ mp_mod (mp_int * a, mp_int * b, mp_int * c)
  * embedded in the normal function but that wasted a lot of stack space
  * for nothing (since 99% of the time the Montgomery code would be called)
  */
-static int ICACHE_FLASH_ATTR
+static int
 mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y)
 {
-  int dr;
+#if defined(BN_MP_DR_IS_MODULUS_C)||defined(BN_MP_REDUCE_IS_2K_C)||defined(BN_MP_EXPTMOD_FAST_C)
+  int dr = 0;
+#endif
 
   /* modulus P must be positive */
   if (P->sign == MP_NEG) {
@@ -652,9 +653,6 @@ mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y)
 #ifdef BN_MP_DR_IS_MODULUS_C
   /* is it a DR modulus? */
   dr = mp_dr_is_modulus(P);
-#else
-  /* default to no */
-  dr = 0;
 #endif
 
 #ifdef BN_MP_REDUCE_IS_2K_C
@@ -685,7 +683,7 @@ mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y)
 
 
 /* compare two ints (signed)*/
-static int ICACHE_FLASH_ATTR
+static int
 mp_cmp (mp_int * a, mp_int * b)
 {
   /* compare based on sign */
@@ -708,7 +706,7 @@ mp_cmp (mp_int * a, mp_int * b)
 
 
 /* compare a digit */
-static int ICACHE_FLASH_ATTR
+static int
 mp_cmp_d(mp_int * a, mp_digit b)
 {
   /* compare based on sign */
@@ -734,7 +732,7 @@ mp_cmp_d(mp_int * a, mp_digit b)
 
 #ifndef LTM_NO_NEG_EXP
 /* hac 14.61, pp608 */
-static int ICACHE_FLASH_ATTR
+static int
 mp_invmod (mp_int * a, mp_int * b, mp_int * c)
 {
   /* b cannot be negative */
@@ -764,7 +762,7 @@ mp_invmod (mp_int * a, mp_int * b, mp_int * c)
 
 
 /* get the size for an unsigned equivalent */
-static int ICACHE_FLASH_ATTR
+static int
 mp_unsigned_bin_size (mp_int * a)
 {
   int     size = mp_count_bits (a);
@@ -774,7 +772,7 @@ mp_unsigned_bin_size (mp_int * a)
 
 #ifndef LTM_NO_NEG_EXP
 /* hac 14.61, pp608 */
-static int ICACHE_FLASH_ATTR
+static int
 mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c)
 {
   mp_int  x, y, u, v, A, B, C, D;
@@ -931,7 +929,7 @@ LBL_ERR:mp_clear_multi (&x, &y, &u, &v, &A, &B, &C, &D, NULL);
 
 
 /* compare maginitude of two ints (unsigned) */
-static int ICACHE_FLASH_ATTR
+static int
 mp_cmp_mag (mp_int * a, mp_int * b)
 {
   int     n;
@@ -967,7 +965,7 @@ mp_cmp_mag (mp_int * a, mp_int * b)
 
 
 /* reads a unsigned char array, assumes the msb is stored first [big endian] */
-static int ICACHE_FLASH_ATTR
+static int
 mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c)
 {
   int     res;
@@ -1003,7 +1001,7 @@ mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c)
 
 
 /* store in unsigned [big endian] format */
-static int ICACHE_FLASH_ATTR
+static int
 mp_to_unsigned_bin (mp_int * a, unsigned char *b)
 {
   int     x, res;
@@ -1032,7 +1030,7 @@ mp_to_unsigned_bin (mp_int * a, unsigned char *b)
 
 
 /* shift right by a certain bit count (store quotient in c, optional remainder in d) */
-static int ICACHE_FLASH_ATTR
+static int
 mp_div_2d (mp_int * a, int b, mp_int * c, mp_int * d)
 {
   mp_digit D, r, rr;
@@ -1109,7 +1107,7 @@ mp_div_2d (mp_int * a, int b, mp_int * c, mp_int * d)
 }
 
 
-static int ICACHE_FLASH_ATTR
+static int
 mp_init_copy (mp_int * a, mp_int * b)
 {
   int     res;
@@ -1122,7 +1120,7 @@ mp_init_copy (mp_int * a, mp_int * b)
 
 
 /* set to zero */
-static void ICACHE_FLASH_ATTR
+static void
 mp_zero (mp_int * a)
 {
   int       n;
@@ -1139,7 +1137,7 @@ mp_zero (mp_int * a)
 
 
 /* copy, b = a */
-static int ICACHE_FLASH_ATTR
+static int
 mp_copy (mp_int * a, mp_int * b)
 {
   int     res, n;
@@ -1187,7 +1185,7 @@ mp_copy (mp_int * a, mp_int * b)
 
 
 /* shift right a certain amount of digits */
-static void ICACHE_FLASH_ATTR
+static void
 mp_rshd (mp_int * a, int b)
 {
   int     x;
@@ -1242,7 +1240,7 @@ mp_rshd (mp_int * a, int b)
 /* swap the elements of two integers, for cases where you can't simply swap the 
  * mp_int pointers around
  */
-static void ICACHE_FLASH_ATTR
+static void
 mp_exch (mp_int * a, mp_int * b)
 {
   mp_int  t;
@@ -1260,7 +1258,7 @@ mp_exch (mp_int * a, mp_int * b)
  * Typically very fast.  Also fixes the sign if there
  * are no more leading digits
  */
-static void ICACHE_FLASH_ATTR
+static void
 mp_clamp (mp_int * a)
 {
   /* decrease used while the most significant digit is
@@ -1278,7 +1276,7 @@ mp_clamp (mp_int * a)
 
 
 /* grow as required */
-static int ICACHE_FLASH_ATTR
+static int
 mp_grow (mp_int * a, int size)
 {
   int     i;
@@ -1320,7 +1318,7 @@ mp_grow (mp_int * a, int size)
  *
  * Simple function copies the input and fixes the sign to positive
  */
-static int ICACHE_FLASH_ATTR
+static int
 mp_abs (mp_int * a, mp_int * b)
 {
   int     res;
@@ -1341,7 +1339,7 @@ mp_abs (mp_int * a, mp_int * b)
 
 
 /* set to a digit */
-static void ICACHE_FLASH_ATTR
+static void
 mp_set (mp_int * a, mp_digit b)
 {
   mp_zero (a);
@@ -1352,7 +1350,7 @@ mp_set (mp_int * a, mp_digit b)
 
 #ifndef LTM_NO_NEG_EXP
 /* b = a/2 */
-static int ICACHE_FLASH_ATTR
+static int
 mp_div_2(mp_int * a, mp_int * b)
 {
   int     x, res, oldused;
@@ -1402,7 +1400,7 @@ mp_div_2(mp_int * a, mp_int * b)
 
 
 /* shift left by a certain bit count */
-static int ICACHE_FLASH_ATTR
+static int
 mp_mul_2d (mp_int * a, int b, mp_int * c)
 {
   mp_digit d;
@@ -1468,7 +1466,7 @@ mp_mul_2d (mp_int * a, int b, mp_int * c)
 
 
 #ifdef BN_MP_INIT_MULTI_C
-static int ICACHE_FLASH_ATTR
+static int
 mp_init_multi(mp_int *mp, ...) 
 {
     mp_err res = MP_OKAY;      /* Assume ok until proven otherwise */
@@ -1508,7 +1506,7 @@ mp_init_multi(mp_int *mp, ...)
 
 
 #ifdef BN_MP_CLEAR_MULTI_C
-static void ICACHE_FLASH_ATTR
+static void
 mp_clear_multi(mp_int *mp, ...) 
 {
     mp_int* next_mp = mp;
@@ -1524,7 +1522,7 @@ mp_clear_multi(mp_int *mp, ...)
 
 
 /* shift left a certain amount of digits */
-static int ICACHE_FLASH_ATTR
+static int
 mp_lshd (mp_int * a, int b)
 {
   int     x, res;
@@ -1572,7 +1570,7 @@ mp_lshd (mp_int * a, int b)
 
 
 /* returns the number of bits in an int */
-static int ICACHE_FLASH_ATTR
+static int
 mp_count_bits (mp_int * a)
 {
   int     r;
@@ -1597,7 +1595,7 @@ mp_count_bits (mp_int * a)
 
 
 /* calc a value mod 2**b */
-static int ICACHE_FLASH_ATTR
+static int
 mp_mod_2d (mp_int * a, int b, mp_int * c)
 {
   int     x, res;
@@ -1634,7 +1632,7 @@ mp_mod_2d (mp_int * a, int b, mp_int * c)
 #ifdef BN_MP_DIV_SMALL
 
 /* slower bit-bang division... also smaller */
-static int ICACHE_FLASH_ATTR
+static int
 mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d)
 {
    mp_int ta, tb, tq, q;
@@ -1717,7 +1715,7 @@ LBL_ERR:
  * The overall algorithm is as described as 
  * 14.20 from HAC but fixed to treat these cases.
 */
-static int ICACHE_FLASH_ATTR
+static int
 mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
 {
   mp_int  q, x, y, t1, t2;
@@ -1910,7 +1908,7 @@ LBL_Q:mp_clear (&q);
    #define TAB_SIZE 256
 #endif
 
-static int ICACHE_FLASH_ATTR
+static int
 s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode)
 {
   mp_int  M[TAB_SIZE], res, mu;
@@ -2139,7 +2137,7 @@ LBL_M:
 
 
 /* computes b = a*a */
-static int ICACHE_FLASH_ATTR
+static int
 mp_sqr (mp_int * a, mp_int * b)
 {
   int     res;
@@ -2181,7 +2179,7 @@ if (a->used >= KARATSUBA_SQR_CUTOFF) {
    This differs from reduce_2k since "d" can be larger
    than a single digit.
 */
-static int ICACHE_FLASH_ATTR
+static int
 mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d)
 {
    mp_int q;
@@ -2220,7 +2218,7 @@ ERR:
 
 
 /* determines the setup value */
-static int ICACHE_FLASH_ATTR
+static int
 mp_reduce_2k_setup_l(mp_int *a, mp_int *d)
 {
    int    res;
@@ -2249,7 +2247,7 @@ ERR:
  * Simple algorithm which zeroes the int, grows it then just sets one bit
  * as required.
  */
-static int ICACHE_FLASH_ATTR
+static int
 mp_2expt (mp_int * a, int b)
 {
   int     res;
@@ -2275,7 +2273,7 @@ mp_2expt (mp_int * a, int b)
 /* pre-calculate the value required for Barrett reduction
  * For a given modulus "b" it calulates the value required in "a"
  */
-static int ICACHE_FLASH_ATTR
+static int
 mp_reduce_setup (mp_int * a, mp_int * b)
 {
   int     res;
@@ -2291,7 +2289,7 @@ mp_reduce_setup (mp_int * a, mp_int * b)
  * precomputed via mp_reduce_setup.
  * From HAC pp.604 Algorithm 14.42
  */
-static int ICACHE_FLASH_ATTR
+static int
 mp_reduce (mp_int * x, mp_int * m, mp_int * mu)
 {
   mp_int  q;
@@ -2375,7 +2373,7 @@ CLEANUP:
  * HAC pp. 595, Algorithm 14.12  Modified so you can control how 
  * many digits of output are created.
  */
-static int ICACHE_FLASH_ATTR
+static int
 s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
 {
   mp_int  t;
@@ -2458,7 +2456,7 @@ s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
  * Based on Algorithm 14.12 on pp.595 of HAC.
  *
  */
-static int ICACHE_FLASH_ATTR
+static int
 fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
 {
   int     olduse, res, pa, ix, iz;
@@ -2531,7 +2529,7 @@ fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
 
 
 /* init an mp_init for a given size */
-static int ICACHE_FLASH_ATTR
+static int
 mp_init_size (mp_int * a, int size)
 {
   int x;
@@ -2560,7 +2558,7 @@ mp_init_size (mp_int * a, int size)
 
 
 /* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */
-static int ICACHE_FLASH_ATTR
+static int
 s_mp_sqr (mp_int * a, mp_int * b)
 {
   mp_int  t;
@@ -2627,7 +2625,7 @@ s_mp_sqr (mp_int * a, mp_int * b)
 /* multiplies |a| * |b| and does not compute the lower digs digits
  * [meant to get the higher part of the product]
  */
-static int ICACHE_FLASH_ATTR
+static int
 s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
 {
   mp_int  t;
@@ -2687,7 +2685,7 @@ s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
 
 #ifdef BN_MP_MONTGOMERY_SETUP_C
 /* setups the montgomery reduction stuff */
-static int ICACHE_FLASH_ATTR
+static int
 mp_montgomery_setup (mp_int * n, mp_digit * rho)
 {
   mp_digit x, b;
@@ -2735,7 +2733,7 @@ mp_montgomery_setup (mp_int * n, mp_digit * rho)
  *
  * Based on Algorithm 14.32 on pp.601 of HAC.
 */
-int ICACHE_FLASH_ATTR
+int
 fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho)
 {
   int     ix, res, olduse;
@@ -2883,7 +2881,7 @@ fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho)
 
 #ifdef BN_MP_MUL_2_C
 /* b = a*2 */
-static int ICACHE_FLASH_ATTR
+static int
 mp_mul_2(mp_int * a, mp_int * b)
 {
   int     x, res, oldused;
@@ -2953,7 +2951,7 @@ mp_mul_2(mp_int * a, mp_int * b)
  * The method is slightly modified to shift B unconditionally up to just under
  * the leading bit of b.  This saves a lot of multiple precision shifting.
  */
-static int ICACHE_FLASH_ATTR
+static int
 mp_montgomery_calc_normalization (mp_int * a, mp_int * b)
 {
   int     x, bits, res;
@@ -2997,7 +2995,7 @@ mp_montgomery_calc_normalization (mp_int * a, mp_int * b)
  * Uses Montgomery or Diminished Radix reduction [whichever appropriate]
  */
 
-static int ICACHE_FLASH_ATTR
+static int
 mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode)
 {
   mp_int  M[TAB_SIZE], res;
@@ -3296,7 +3294,7 @@ LBL_M:
 After that loop you do the squares and add them in.
 */
 
-static int ICACHE_FLASH_ATTR
+static int
 fast_s_mp_sqr (mp_int * a, mp_int * b)
 {
   int       olduse, res, pa, ix, iz;
@@ -3384,7 +3382,7 @@ fast_s_mp_sqr (mp_int * a, mp_int * b)
 
 #ifdef BN_MP_MUL_D_C
 /* multiply by a digit */
-static int ICACHE_FLASH_ATTR
+static int
 mp_mul_d (mp_int * a, mp_digit b, mp_int * c)
 {
   mp_digit u, *tmpa, *tmpc;
index 91eb87419850f872d54437e0d88a34c9791d778d..aa21fd0fc1b76e66f5b9c636cfa03c55b74b8e28 100644 (file)
@@ -9,9 +9,9 @@
 #ifndef BASE64_H
 #define BASE64_H
 
-unsigned char * _base64_encode(const unsigned char *src, size_t len,
+unsigned char * base64_encode(const unsigned char *src, size_t len,
                              size_t *out_len);
-unsigned char * _base64_decode(const unsigned char *src, size_t len,
+unsigned char * base64_decode(const unsigned char *src, size_t len,
                              size_t *out_len);
 
 #endif /* BASE64_H */
index e3e46ea0843e282861718925dc81d9bf02ec4b30..dfe8e6f0e700b3d179eddeebdbf5700230efc051 100644 (file)
@@ -23,10 +23,10 @@ void ext_password_free(struct wpabuf *pw);
 
 #else /* CONFIG_EXT_PASSWORD */
 
-#define ext_password_init(b, p) ((void *) 1)
-#define ext_password_deinit(d) do { } while (0)
-#define ext_password_get(d, n) (NULL)
-#define ext_password_free(p) do { } while (0)
+#define ext_password_init(b, p)
+#define ext_password_deinit(d)
+#define ext_password_get(d, n)
+#define ext_password_free(p)
 
 #endif /* CONFIG_EXT_PASSWORD */
 
diff --git a/components/wpa_supplicant/include/wps/utils/uuid.h b/components/wpa_supplicant/include/wps/utils/uuid.h
new file mode 100644 (file)
index 0000000..5e860cb
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Universally Unique IDentifier (UUID)
+ * 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.
+ */
+
+#ifndef UUID_H
+#define UUID_H
+
+#define UUID_LEN 16
+
+int uuid_str2bin(const char *str, u8 *bin);
+int uuid_bin2str(const u8 *bin, char *str, size_t max_len);
+int is_nil_uuid(const u8 *uuid);
+
+#endif /* UUID_H */
diff --git a/components/wpa_supplicant/include/wps/wps.h b/components/wpa_supplicant/include/wps/wps.h
new file mode 100644 (file)
index 0000000..c69aedb
--- /dev/null
@@ -0,0 +1,1067 @@
+/*
+ * Wi-Fi Protected Setup
+ * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPS_H
+#define WPS_H
+
+#include "rom/ets_sys.h"
+#include "wps_defs.h"
+#include "esp_wifi_types.h"
+
+/**
+ * enum wsc_op_code - EAP-WSC OP-Code values
+ */
+enum wsc_op_code {
+       WSC_UPnP = 0 /* No OP Code in UPnP transport */,
+       WSC_Start = 0x01,
+       WSC_ACK = 0x02,
+       WSC_NACK = 0x03,
+       WSC_MSG = 0x04,
+       WSC_Done = 0x05,
+       WSC_FRAG_ACK = 0x06
+};
+
+struct wps_registrar;
+//struct upnp_wps_device_sm;
+struct wps_er;
+struct wps_parse_attr;
+
+/**
+ * struct wps_credential - WPS Credential
+ * @ssid: SSID
+ * @ssid_len: Length of SSID
+ * @auth_type: Authentication Type (WPS_WIFI_AUTH_OPEN, .. flags)
+ * @encr_type: Encryption Type (WPS_ENCR_NONE, .. flags)
+ * @key_idx: Key index
+ * @key: Key
+ * @key_len: Key length in octets
+ * @mac_addr: MAC address of the Credential receiver
+ * @cred_attr: Unparsed Credential attribute data (used only in cred_cb());
+ *     this may be %NULL, if not used
+ * @cred_attr_len: Length of cred_attr in octets
+ * @ap_channel: AP channel
+ */
+struct wps_credential {
+       u8 ssid[32];
+       size_t ssid_len;
+       u16 auth_type;
+       u16 encr_type;
+       u8 key_idx;
+       u8 key[64];
+       size_t key_len;
+       u8 mac_addr[ETH_ALEN];
+       const u8 *cred_attr;
+       size_t cred_attr_len;
+       u16 ap_channel;
+};
+
+#define WPS_DEV_TYPE_LEN 8
+#define WPS_DEV_TYPE_BUFSIZE 21
+#define WPS_SEC_DEV_TYPE_MAX_LEN 128
+/* maximum number of advertised WPS vendor extension attributes */
+#define MAX_WPS_VENDOR_EXTENSIONS 10
+/* maximum size of WPS Vendor extension attribute */
+#define WPS_MAX_VENDOR_EXT_LEN 1024
+/* maximum number of parsed WPS vendor extension attributes */
+#define MAX_WPS_PARSE_VENDOR_EXT 10
+
+/**
+ * struct wps_device_data - WPS Device Data
+ * @mac_addr: Device MAC address
+ * @device_name: Device Name (0..32 octets encoded in UTF-8)
+ * @manufacturer: Manufacturer (0..64 octets encoded in UTF-8)
+ * @model_name: Model Name (0..32 octets encoded in UTF-8)
+ * @model_number: Model Number (0..32 octets encoded in UTF-8)
+ * @serial_number: Serial Number (0..32 octets encoded in UTF-8)
+ * @pri_dev_type: Primary Device Type
+ * @sec_dev_type: Array of secondary device types
+ * @num_sec_dev_type: Number of secondary device types
+ * @os_version: OS Version
+ * @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ flags)
+ * @p2p: Whether the device is a P2P device
+ */
+struct wps_device_data {
+       u8 mac_addr[ETH_ALEN];
+       char *device_name;
+       char *manufacturer;
+       char *model_name;
+       char *model_number;
+       char *serial_number;
+       u8 pri_dev_type[WPS_DEV_TYPE_LEN];
+#define WPS_SEC_DEVICE_TYPES 5
+       u8 sec_dev_type[WPS_SEC_DEVICE_TYPES][WPS_DEV_TYPE_LEN];
+       u8 num_sec_dev_types;
+       u32 os_version;
+       u8 rf_bands;
+       u16 config_methods;
+       struct wpabuf *vendor_ext_m1;
+       struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
+
+       int p2p;
+};
+
+/**
+ * struct wps_config - WPS configuration for a single registration protocol run
+ */
+struct wps_config {
+       /**
+        * wps - Pointer to long term WPS context
+        */
+       struct wps_context *wps;
+
+       /**
+        * registrar - Whether this end is a Registrar
+        */
+       int registrar;
+
+       /**
+        * pin - Enrollee Device Password (%NULL for Registrar or PBC)
+        */
+       const u8 *pin;
+
+       /**
+        * pin_len - Length on pin in octets
+        */
+       size_t pin_len;
+
+       /**
+        * pbc - Whether this is protocol run uses PBC
+        */
+       int pbc;
+
+       /**
+        * assoc_wps_ie: (Re)AssocReq WPS IE (in AP; %NULL if not AP)
+        */
+       const struct wpabuf *assoc_wps_ie;
+
+       /**
+        * new_ap_settings - New AP settings (%NULL if not used)
+        *
+        * This parameter provides new AP settings when using a wireless
+        * stations as a Registrar to configure the AP. %NULL means that AP
+        * will not be reconfigured, i.e., the station will only learn the
+        * current AP settings by using AP PIN.
+        */
+       const struct wps_credential *new_ap_settings;
+
+       /**
+        * peer_addr: MAC address of the peer in AP; %NULL if not AP
+        */
+       const u8 *peer_addr;
+
+       /**
+        * use_psk_key - Use PSK format key in Credential
+        *
+        * Force PSK format to be used instead of ASCII passphrase when
+        * building Credential for an Enrollee. The PSK value is set in
+        * struct wpa_context::psk.
+        */
+       int use_psk_key;
+
+       /**
+        * dev_pw_id - Device Password ID for Enrollee when PIN is used
+        */
+       u16 dev_pw_id;
+
+       /**
+        * p2p_dev_addr - P2P Device Address from (Re)Association Request
+        *
+        * On AP/GO, this is set to the P2P Device Address of the associating
+        * P2P client if a P2P IE is included in the (Re)Association Request
+        * frame and the P2P Device Address is included. Otherwise, this is set
+        * to %NULL to indicate the station does not have a P2P Device Address.
+        */
+       const u8 *p2p_dev_addr;
+
+       /**
+        * pbc_in_m1 - Do not remove PushButton config method in M1 (AP)
+        *
+        * This can be used to enable a workaround to allow Windows 7 to use
+        * PBC with the AP.
+        */
+       int pbc_in_m1;
+};
+
+/* Bssid of the discard AP which is discarded for not select reg or other reason */
+struct discard_ap_list_t{
+       u8 bssid[6];
+};
+
+//struct wps_data * wps_init(const struct wps_config *cfg);
+struct wps_data * wps_init(void);
+
+//void wps_deinit(struct wps_data *data);
+void wps_deinit(void);
+
+/**
+ * enum wps_process_res - WPS message processing result
+ */
+enum wps_process_res {
+       /**
+        * WPS_DONE - Processing done
+        */
+       WPS_DONE,
+
+       /**
+        * WPS_CONTINUE - Processing continues
+        */
+       WPS_CONTINUE,
+
+       /**
+        * WPS_FAILURE - Processing failed
+        */
+       WPS_FAILURE,
+
+       /**
+        * WPS_PENDING - Processing continues, but waiting for an external
+        *      event (e.g., UPnP message from an external Registrar)
+        */
+       WPS_PENDING,
+       WPS_IGNORE,     /* snake, ignore the re-packge */
+
+       WPS_FRAGMENT    /* Tim, send wsc fragment ack */
+};
+enum wps_process_res wps_process_msg(struct wps_data *wps,
+                                    enum wsc_op_code op_code,
+                                    const struct wpabuf *msg);
+
+struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code);
+
+int wps_is_selected_pbc_registrar(const struct wpabuf *msg, u8 *bssid);
+#ifdef CONFIG_WPS_PIN
+int wps_is_selected_pin_registrar(const struct wpabuf *msg, u8 *bssid);
+#endif
+int wps_ap_priority_compar(const struct wpabuf *wps_a,
+                          const struct wpabuf *wps_b);
+int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr,
+                          int ver1_compat);
+const u8 * wps_get_uuid_e(const struct wpabuf *msg);
+int wps_is_20(const struct wpabuf *msg);
+
+struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type);
+struct wpabuf * wps_build_assoc_resp_ie(void);
+struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev,
+                                      const u8 *uuid,
+                                      enum wps_request_type req_type,
+                                      unsigned int num_req_dev_types,
+                                      const u8 *req_dev_types);
+
+
+/**
+ * struct wps_registrar_config - WPS Registrar configuration
+ */
+struct wps_registrar_config {
+       /**
+        * new_psk_cb - Callback for new PSK
+        * @ctx: Higher layer context data (cb_ctx)
+        * @mac_addr: MAC address of the Enrollee
+        * @psk: The new PSK
+        * @psk_len: The length of psk in octets
+        * Returns: 0 on success, -1 on failure
+        *
+        * This callback is called when a new per-device PSK is provisioned.
+        */
+       int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk,
+                         size_t psk_len);
+
+       /**
+        * set_ie_cb - Callback for WPS IE changes
+        * @ctx: Higher layer context data (cb_ctx)
+        * @beacon_ie: WPS IE for Beacon
+        * @probe_resp_ie: WPS IE for Probe Response
+        * Returns: 0 on success, -1 on failure
+        *
+        * This callback is called whenever the WPS IE in Beacon or Probe
+        * Response frames needs to be changed (AP only). Callee is responsible
+        * for freeing the buffers.
+        */
+       int (*set_ie_cb)(void *ctx, struct wpabuf *beacon_ie,
+                        struct wpabuf *probe_resp_ie);
+
+       /**
+        * pin_needed_cb - Callback for requesting a PIN
+        * @ctx: Higher layer context data (cb_ctx)
+        * @uuid_e: UUID-E of the unknown Enrollee
+        * @dev: Device Data from the unknown Enrollee
+        *
+        * This callback is called whenever an unknown Enrollee requests to use
+        * PIN method and a matching PIN (Device Password) is not found in
+        * Registrar data.
+        */
+       void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
+                             const struct wps_device_data *dev);
+
+       /**
+        * reg_success_cb - Callback for reporting successful registration
+        * @ctx: Higher layer context data (cb_ctx)
+        * @mac_addr: MAC address of the Enrollee
+        * @uuid_e: UUID-E of the Enrollee
+        * @dev_pw: Device Password (PIN) used during registration
+        * @dev_pw_len: Length of dev_pw in octets
+        *
+        * This callback is called whenever an Enrollee completes registration
+        * successfully.
+        */
+       void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
+                              const u8 *uuid_e, const u8 *dev_pw,
+                              size_t dev_pw_len);
+
+       /**
+        * set_sel_reg_cb - Callback for reporting selected registrar changes
+        * @ctx: Higher layer context data (cb_ctx)
+        * @sel_reg: Whether the Registrar is selected
+        * @dev_passwd_id: Device Password ID to indicate with method or
+        *      specific password the Registrar intends to use
+        * @sel_reg_config_methods: Bit field of active config methods
+        *
+        * This callback is called whenever the Selected Registrar state
+        * changes (e.g., a new PIN becomes available or PBC is invoked). This
+        * callback is only used by External Registrar implementation;
+        * set_ie_cb() is used by AP implementation in similar caes, but it
+        * provides the full WPS IE data instead of just the minimal Registrar
+        * state information.
+        */
+       void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id,
+                              u16 sel_reg_config_methods);
+
+       /**
+        * enrollee_seen_cb - Callback for reporting Enrollee based on ProbeReq
+        * @ctx: Higher layer context data (cb_ctx)
+        * @addr: MAC address of the Enrollee
+        * @uuid_e: UUID of the Enrollee
+        * @pri_dev_type: Primary device type
+        * @config_methods: Config Methods
+        * @dev_password_id: Device Password ID
+        * @request_type: Request Type
+        * @dev_name: Device Name (if available)
+        */
+       void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e,
+                                const u8 *pri_dev_type, u16 config_methods,
+                                u16 dev_password_id, u8 request_type,
+                                const char *dev_name);
+
+       /**
+        * cb_ctx: Higher layer context data for Registrar callbacks
+        */
+       void *cb_ctx;
+
+       /**
+        * skip_cred_build: Do not build credential
+        *
+        * This option can be used to disable internal code that builds
+        * Credential attribute into M8 based on the current network
+        * configuration and Enrollee capabilities. The extra_cred data will
+        * then be used as the Credential(s).
+        */
+       int skip_cred_build;
+
+       /**
+        * extra_cred: Additional Credential attribute(s)
+        *
+        * This optional data (set to %NULL to disable) can be used to add
+        * Credential attribute(s) for other networks into M8. If
+        * skip_cred_build is set, this will also override the automatically
+        * generated Credential attribute.
+        */
+       const u8 *extra_cred;
+
+       /**
+        * extra_cred_len: Length of extra_cred in octets
+        */
+       size_t extra_cred_len;
+
+       /**
+        * disable_auto_conf - Disable auto-configuration on first registration
+        *
+        * By default, the AP that is started in not configured state will
+        * generate a random PSK and move to configured state when the first
+        * registration protocol run is completed successfully. This option can
+        * be used to disable this functionality and leave it up to an external
+        * program to take care of configuration. This requires the extra_cred
+        * to be set with a suitable Credential and skip_cred_build being used.
+        */
+       int disable_auto_conf;
+
+       /**
+        * static_wep_only - Whether the BSS supports only static WEP
+        */
+       int static_wep_only;
+
+       /**
+        * dualband - Whether this is a concurrent dualband AP
+        */
+       int dualband;
+};
+
+
+/**
+ * enum wps_event - WPS event types
+ */
+enum wps_event {
+       /**
+        * WPS_EV_M2D - M2D received (Registrar did not know us)
+        */
+       WPS_EV_M2D,
+
+       /**
+        * WPS_EV_FAIL - Registration failed
+        */
+       WPS_EV_FAIL,
+
+       /**
+        * WPS_EV_SUCCESS - Registration succeeded
+        */
+       WPS_EV_SUCCESS,
+
+       /**
+        * WPS_EV_PWD_AUTH_FAIL - Password authentication failed
+        */
+       WPS_EV_PWD_AUTH_FAIL,
+
+       /**
+        * WPS_EV_PBC_OVERLAP - PBC session overlap detected
+        */
+       WPS_EV_PBC_OVERLAP,
+
+       /**
+        * WPS_EV_PBC_TIMEOUT - PBC walktime expired before protocol run start
+        */
+       WPS_EV_PBC_TIMEOUT,
+
+       /**
+        * WPS_EV_ER_AP_ADD - ER: AP added
+        */
+       WPS_EV_ER_AP_ADD,
+
+       /**
+        * WPS_EV_ER_AP_REMOVE - ER: AP removed
+        */
+       WPS_EV_ER_AP_REMOVE,
+
+       /**
+        * WPS_EV_ER_ENROLLEE_ADD - ER: Enrollee added
+        */
+       WPS_EV_ER_ENROLLEE_ADD,
+
+       /**
+        * WPS_EV_ER_ENROLLEE_REMOVE - ER: Enrollee removed
+        */
+       WPS_EV_ER_ENROLLEE_REMOVE,
+
+       /**
+        * WPS_EV_ER_AP_SETTINGS - ER: AP Settings learned
+        */
+       WPS_EV_ER_AP_SETTINGS,
+
+       /**
+        * WPS_EV_ER_SET_SELECTED_REGISTRAR - ER: SetSelectedRegistrar event
+        */
+       WPS_EV_ER_SET_SELECTED_REGISTRAR,
+
+       /**
+        * WPS_EV_AP_PIN_SUCCESS - External Registrar used correct AP PIN
+        */
+       WPS_EV_AP_PIN_SUCCESS
+};
+
+/**
+ * union wps_event_data - WPS event data
+ */
+union wps_event_data {
+       /**
+        * struct wps_event_m2d - M2D event data
+        */
+       struct wps_event_m2d {
+               u16 config_methods;
+               const u8 *manufacturer;
+               size_t manufacturer_len;
+               const u8 *model_name;
+               size_t model_name_len;
+               const u8 *model_number;
+               size_t model_number_len;
+               const u8 *serial_number;
+               size_t serial_number_len;
+               const u8 *dev_name;
+               size_t dev_name_len;
+               const u8 *primary_dev_type; /* 8 octets */
+               u16 config_error;
+               u16 dev_password_id;
+       } m2d;
+
+       /**
+        * struct wps_event_fail - Registration failure information
+        * @msg: enum wps_msg_type
+        */
+       struct wps_event_fail {
+               int msg;
+               u16 config_error;
+               u16 error_indication;
+       } fail;
+
+       struct wps_event_pwd_auth_fail {
+               int enrollee;
+               int part;
+       } pwd_auth_fail;
+
+       struct wps_event_er_ap {
+               const u8 *uuid;
+               const u8 *mac_addr;
+               const char *friendly_name;
+               const char *manufacturer;
+               const char *manufacturer_url;
+               const char *model_description;
+               const char *model_name;
+               const char *model_number;
+               const char *model_url;
+               const char *serial_number;
+               const char *upc;
+               const u8 *pri_dev_type;
+               u8 wps_state;
+       } ap;
+
+       struct wps_event_er_enrollee {
+               const u8 *uuid;
+               const u8 *mac_addr;
+               int m1_received;
+               u16 config_methods;
+               u16 dev_passwd_id;
+               const u8 *pri_dev_type;
+               const char *dev_name;
+               const char *manufacturer;
+               const char *model_name;
+               const char *model_number;
+               const char *serial_number;
+       } enrollee;
+
+       struct wps_event_er_ap_settings {
+               const u8 *uuid;
+               const struct wps_credential *cred;
+       } ap_settings;
+
+       struct wps_event_er_set_selected_registrar {
+               const u8 *uuid;
+               int sel_reg;
+               u16 dev_passwd_id;
+               u16 sel_reg_config_methods;
+               enum {
+                       WPS_ER_SET_SEL_REG_START,
+                       WPS_ER_SET_SEL_REG_DONE,
+                       WPS_ER_SET_SEL_REG_FAILED
+               } state;
+       } set_sel_reg;
+};
+#ifdef CONFIG_WPS_UPNP
+/**
+ * struct upnp_pending_message - Pending PutWLANResponse messages
+ * @next: Pointer to next pending message or %NULL
+ * @addr: NewWLANEventMAC
+ * @msg: NewMessage
+ * @type: Message Type
+ */
+struct upnp_pending_message {
+       struct upnp_pending_message *next;
+       u8 addr[ETH_ALEN];
+       struct wpabuf *msg;
+       enum wps_msg_type type;
+};
+void wps_free_pending_msgs(struct upnp_pending_message *msgs);
+#endif
+/**
+ * struct wps_context - Long term WPS context data
+ *
+ * This data is stored at the higher layer Authenticator or Supplicant data
+ * structures and it is maintained over multiple registration protocol runs.
+ */
+struct wps_context {
+       /**
+        * ap - Whether the local end is an access point
+        */
+       int ap;
+
+       /**
+        * registrar - Pointer to WPS registrar data from wps_registrar_init()
+        */
+       struct wps_registrar *registrar;
+
+       /**
+        * wps_state - Current WPS state
+        */
+       enum wps_state wps_state;
+
+       /**
+        * ap_setup_locked - Whether AP setup is locked (only used at AP)
+        */
+       int ap_setup_locked;
+
+       /**
+        * uuid - Own UUID
+        */
+       u8 uuid[16];
+
+       /**
+        * ssid - SSID
+        *
+        * This SSID is used by the Registrar to fill in information for
+        * Credentials. In addition, AP uses it when acting as an Enrollee to
+        * notify Registrar of the current configuration.
+        */
+       u8 ssid[32];
+
+       /**
+        * ssid_len - Length of ssid in octets
+        */
+       size_t ssid_len;
+
+       /**
+        * dev - Own WPS device data
+        */
+       struct wps_device_data dev;
+
+       /**
+        * dh_ctx - Context data for Diffie-Hellman operation
+        */
+       void *dh_ctx;
+
+       /**
+        * dh_privkey - Diffie-Hellman private key
+        */
+       struct wpabuf *dh_privkey;
+
+       /**
+        * dh_pubkey_oob - Diffie-Hellman public key
+        */
+       struct wpabuf *dh_pubkey;
+
+       /**
+        * config_methods - Enabled configuration methods
+        *
+        * Bit field of WPS_CONFIG_*
+        */
+       u16 config_methods;
+
+       /**
+        * encr_types - Enabled encryption types (bit field of WPS_ENCR_*)
+        */
+       u16 encr_types;
+
+       /**
+        * auth_types - Authentication types (bit field of WPS_AUTH_*)
+        */
+       u16 auth_types;
+
+       /**
+        * network_key - The current Network Key (PSK) or %NULL to generate new
+        *
+        * If %NULL, Registrar will generate per-device PSK. In addition, AP
+        * uses this when acting as an Enrollee to notify Registrar of the
+        * current configuration.
+        *
+        * When using WPA/WPA2-Person, this key can be either the ASCII
+        * passphrase (8..63 characters) or the 32-octet PSK (64 hex
+        * characters). When this is set to the ASCII passphrase, the PSK can
+        * be provided in the psk buffer and used per-Enrollee to control which
+        * key type is included in the Credential (e.g., to reduce calculation
+        * need on low-powered devices by provisioning PSK while still allowing
+        * other devices to get the passphrase).
+        */
+       u8 *network_key;
+
+       /**
+        * network_key_len - Length of network_key in octets
+        */
+       size_t network_key_len;
+
+       /**
+        * psk - The current network PSK
+        *
+        * This optional value can be used to provide the current PSK if
+        * network_key is set to the ASCII passphrase.
+        */
+       u8 psk[32];
+
+       /**
+        * psk_set - Whether psk value is set
+        */
+       int psk_set;
+
+       /**
+        * ap_settings - AP Settings override for M7 (only used at AP)
+        *
+        * If %NULL, AP Settings attributes will be generated based on the
+        * current network configuration.
+        */
+       u8 *ap_settings;
+
+       /**
+        * ap_settings_len - Length of ap_settings in octets
+        */
+       size_t ap_settings_len;
+
+       /**
+        * friendly_name - Friendly Name (required for UPnP)
+        */
+       char *friendly_name;
+
+       /**
+        * manufacturer_url - Manufacturer URL (optional for UPnP)
+        */
+       char *manufacturer_url;
+
+       /**
+        * model_description - Model Description (recommended for UPnP)
+        */
+       char *model_description;
+
+       /**
+        * model_url - Model URL (optional for UPnP)
+        */
+       char *model_url;
+
+       /**
+        * upc - Universal Product Code (optional for UPnP)
+        */
+       char *upc;
+
+       /**
+        * cred_cb - Callback to notify that new Credentials were received
+        * @ctx: Higher layer context data (cb_ctx)
+        * @cred: The received Credential
+        * Return: 0 on success, -1 on failure
+        */
+       int (*cred_cb)(void *ctx, const struct wps_credential *cred);
+
+       /**
+        * event_cb - Event callback (state information about progress)
+        * @ctx: Higher layer context data (cb_ctx)
+        * @event: Event type
+        * @data: Event data
+        */
+       void (*event_cb)(void *ctx, enum wps_event event,
+                        union wps_event_data *data);
+
+       /**
+        * cb_ctx: Higher layer context data for callbacks
+        */
+       void *cb_ctx;
+
+       //struct upnp_wps_device_sm *wps_upnp;
+
+       /* Pending messages from UPnP PutWLANResponse */
+       //struct upnp_pending_message *upnp_msgs;
+#ifdef CONFIG_WPS_NFC
+
+       u16 ap_nfc_dev_pw_id;
+       struct wpabuf *ap_nfc_dh_pubkey;
+       struct wpabuf *ap_nfc_dh_privkey;
+       struct wpabuf *ap_nfc_dev_pw;
+#endif
+};
+
+struct wps_registrar *
+wps_registrar_init(struct wps_context *wps,
+                  const struct wps_registrar_config *cfg);
+void wps_registrar_deinit(struct wps_registrar *reg);
+#ifdef CONFIG_WPS_PIN
+
+int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr,
+                         const u8 *uuid, const u8 *pin, size_t pin_len,
+                         int timeout);
+int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid);
+int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid);
+#endif
+int wps_registrar_wps_cancel(struct wps_registrar *reg);
+
+int wps_registrar_button_pushed(struct wps_registrar *reg,
+                               const u8 *p2p_dev_addr);
+void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e,
+                           const u8 *dev_pw, size_t dev_pw_len);
+void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
+                               const struct wpabuf *wps_data,
+                               int p2p_wildcard);
+int wps_registrar_update_ie(struct wps_registrar *reg);
+int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr,
+                          char *buf, size_t buflen);
+int wps_registrar_config_ap(struct wps_registrar *reg,
+                           struct wps_credential *cred);
+#ifdef CONFIG_WPS_NFC
+
+int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
+                                  const u8 *pubkey_hash, u16 pw_id,
+                                  const u8 *dev_pw, size_t dev_pw_len);
+int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
+                                        const u8 *oob_dev_pw,
+                                        size_t oob_dev_pw_len);
+#endif
+int wps_build_credential_wrap(struct wpabuf *msg,
+                             const struct wps_credential *cred);
+#ifdef CONFIG_WPS_PIN
+
+unsigned int wps_pin_checksum(unsigned int pin);
+unsigned int wps_pin_valid(unsigned int pin);
+unsigned int wps_generate_pin(void);
+int wps_pin_str_valid(const char *pin);
+#endif
+
+#ifdef CONFIG_WPS_OOB
+
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps);
+int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr);
+#endif
+int wps_attr_text(struct wpabuf *data, char *buf, char *end);
+
+struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname,
+                           const char *filter);
+void wps_er_refresh(struct wps_er *er);
+void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx);
+void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
+                       u16 sel_reg_config_methods);
+int wps_er_pbc(struct wps_er *er, const u8 *uuid);
+int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin,
+                size_t pin_len);
+int wps_er_set_config(struct wps_er *er, const u8 *uuid,
+                     const struct wps_credential *cred);
+int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin,
+                 size_t pin_len, const struct wps_credential *cred);
+#ifdef CONFIG_WPS_NFC
+
+struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid);
+
+#endif
+
+int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN]);
+char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
+                           size_t buf_len);
+void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid);
+u16 wps_config_methods_str2bin(const char *str);
+
+#ifdef CONFIG_WPS_NFC
+
+struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id,
+                                      const struct wpabuf *pubkey,
+                                      const struct wpabuf *dev_pw);
+struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
+                                 struct wpabuf **privkey,
+                                 struct wpabuf **dev_pw);
+#endif
+
+/* ndef.c */
+struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf);
+struct wpabuf * ndef_build_wifi(const struct wpabuf *buf);
+struct wpabuf * ndef_build_wifi_hr(void);
+
+#ifdef CONFIG_WPS_STRICT
+int wps_validate_beacon(const struct wpabuf *wps_ie);
+int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, int probe,
+                                  const u8 *addr);
+int wps_validate_probe_req(const struct wpabuf *wps_ie, const u8 *addr);
+int wps_validate_assoc_req(const struct wpabuf *wps_ie);
+int wps_validate_assoc_resp(const struct wpabuf *wps_ie);
+int wps_validate_m1(const struct wpabuf *tlvs);
+int wps_validate_m2(const struct wpabuf *tlvs);
+int wps_validate_m2d(const struct wpabuf *tlvs);
+int wps_validate_m3(const struct wpabuf *tlvs);
+int wps_validate_m4(const struct wpabuf *tlvs);
+int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2);
+int wps_validate_m5(const struct wpabuf *tlvs);
+int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2);
+int wps_validate_m6(const struct wpabuf *tlvs);
+int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2);
+int wps_validate_m7(const struct wpabuf *tlvs);
+int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, int wps2);
+int wps_validate_m8(const struct wpabuf *tlvs);
+int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, int wps2);
+int wps_validate_wsc_ack(const struct wpabuf *tlvs);
+int wps_validate_wsc_nack(const struct wpabuf *tlvs);
+int wps_validate_wsc_done(const struct wpabuf *tlvs);
+int wps_validate_upnp_set_selected_registrar(const struct wpabuf *tlvs);
+#else /* CONFIG_WPS_STRICT */
+static inline int wps_validate_beacon(const struct wpabuf *wps_ie){
+       return 0;
+}
+
+static inline int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie,
+                                                int probe, const u8 *addr)
+{
+       return 0;
+}
+
+static inline int wps_validate_probe_req(const struct wpabuf *wps_ie,
+                                        const u8 *addr)
+{
+       return 0;
+}
+
+static inline int wps_validate_assoc_req(const struct wpabuf *wps_ie)
+{
+       return 0;
+}
+
+static inline int wps_validate_assoc_resp(const struct wpabuf *wps_ie)
+{
+       return 0;
+}
+
+static inline int wps_validate_m1(const struct wpabuf *tlvs)
+{
+       return 0;
+}
+
+static inline int wps_validate_m2(const struct wpabuf *tlvs)
+{
+       return 0;
+}
+
+static inline int wps_validate_m2d(const struct wpabuf *tlvs)
+{
+       return 0;
+}
+
+static inline int wps_validate_m3(const struct wpabuf *tlvs)
+{
+       return 0;
+}
+
+static inline int wps_validate_m4(const struct wpabuf *tlvs)
+{
+       return 0;
+}
+
+static inline int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2)
+{
+       return 0;
+}
+
+static inline int wps_validate_m5(const struct wpabuf *tlvs)
+{
+       return 0;
+}
+
+static inline int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2)
+{
+       return 0;
+}
+
+static inline int wps_validate_m6(const struct wpabuf *tlvs)
+{
+       return 0;
+}
+
+static inline int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2)
+{
+       return 0;
+}
+
+static inline int wps_validate_m7(const struct wpabuf *tlvs)
+{
+       return 0;
+}
+
+static inline int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap,
+                                      int wps2)
+{
+       return 0;
+}
+
+static inline int wps_validate_m8(const struct wpabuf *tlvs)
+{
+       return 0;
+}
+
+static inline int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap,
+                                      int wps2)
+{
+       return 0;
+}
+
+static inline int wps_validate_wsc_ack(const struct wpabuf *tlvs)
+{
+       return 0;
+}
+
+static inline int wps_validate_wsc_nack(const struct wpabuf *tlvs)
+{
+       return 0;
+}
+
+static inline int wps_validate_wsc_done(const struct wpabuf *tlvs)
+{
+       return 0;
+}
+
+static inline int wps_validate_upnp_set_selected_registrar(
+       const struct wpabuf *tlvs)
+{
+       return 0;
+}
+#endif /* CONFIG_WPS_STRICT */
+
+enum wps_cb_status {
+       WPS_CB_ST_SUCCESS = 0,
+       WPS_CB_ST_FAILED,
+       WPS_CB_ST_TIMEOUT,
+       WPS_CB_ST_WEP,
+       WPS_CB_ST_SCAN_ERR,
+};
+
+typedef void (*wps_st_cb_t)(int status);
+
+#ifdef USE_WPS_TASK
+#define SIG_WPS_START  0
+#define        SIG_WPS_RX      1
+#define        SIG_WPS_NUM     2
+#endif
+
+#define WPS_EAP_EXT_VENDOR_TYPE "WFA-SimpleConfig-Enrollee-1-0"
+#define WPS_OUTBUF_SIZE 500
+struct wps_sm {
+    struct wps_config *wps_cfg;
+    struct wps_context *wps_ctx;
+    struct wps_data *wps;
+    char identity[32];
+    u8 identity_len;
+    u8 ownaddr[ETH_ALEN];
+    u8 bssid[ETH_ALEN];
+    u8 ssid[32];
+    u8 ssid_len;
+    struct wps_device_data *dev;
+    u8 uuid[16];
+    u8 eapol_version;
+    char key[64];
+    u8 key_len;
+    ETSTimer wps_timeout_timer;
+    ETSTimer wps_msg_timeout_timer;
+    ETSTimer wps_scan_timer;
+    ETSTimer wps_success_cb_timer;
+    ETSTimer wps_eapol_start_timer;
+    wps_st_cb_t st_cb;
+    u8 current_identifier;
+    bool is_wps_scan;
+    u8 channel;
+    u8 scan_cnt;
+#ifdef USE_WPS_TASK
+    u8 wps_sig_cnt[SIG_WPS_NUM];
+#endif
+    u8 discover_ssid_cnt;
+    bool ignore_sel_reg;
+    struct discard_ap_list_t dis_ap_list[WPS_MAX_DIS_AP_NUM];
+    u8 discard_ap_cnt;
+    wifi_sta_config_t config;
+};
+
+#define    IEEE80211_CAPINFO_PRIVACY        0x0010
+
+struct wps_sm *wps_sm_get(void);
+int wps_ssid_save(u8 *ssid, u8 ssid_len);
+int wps_key_save(char *key, u8 key_len);
+int wps_station_wps_register_cb(wps_st_cb_t cb);
+int wps_station_wps_unregister_cb(void); 
+int wps_start_pending(void);
+int wps_sm_rx_eapol(u8 *src_addr, u8 *buf, u32 len);
+
+int wps_dev_deinit(struct wps_device_data *dev);
+#endif /* WPS_H */
diff --git a/components/wpa_supplicant/include/wps/wps_attr_parse.h b/components/wpa_supplicant/include/wps/wps_attr_parse.h
new file mode 100644 (file)
index 0000000..86061ae
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Wi-Fi Protected Setup - attribute parsing
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPS_ATTR_PARSE_H
+#define WPS_ATTR_PARSE_H
+
+#include "wps/wps.h"
+
+struct wps_parse_attr {
+       /* fixed length fields */
+       const u8 *version; /* 1 octet */
+       const u8 *version2; /* 1 octet */
+       const u8 *msg_type; /* 1 octet */
+       const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */
+       const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */
+       const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */
+       const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */
+       const u8 *auth_type_flags; /* 2 octets */
+       const u8 *encr_type_flags; /* 2 octets */
+       const u8 *conn_type_flags; /* 1 octet */
+       const u8 *config_methods; /* 2 octets */
+       const u8 *sel_reg_config_methods; /* 2 octets */
+       const u8 *primary_dev_type; /* 8 octets */
+       const u8 *rf_bands; /* 1 octet */
+       const u8 *assoc_state; /* 2 octets */
+       const u8 *config_error; /* 2 octets */
+       const u8 *dev_password_id; /* 2 octets */
+       const u8 *os_version; /* 4 octets */
+       const u8 *wps_state; /* 1 octet */
+       const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */
+       const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */
+       const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */
+       const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */
+       const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */
+       const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
+       const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
+       const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
+       const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
+       const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */
+       const u8 *auth_type; /* 2 octets */
+       const u8 *encr_type; /* 2 octets */
+       const u8 *network_idx; /* 1 octet */
+       const u8 *network_key_idx; /* 1 octet */
+       const u8 *mac_addr; /* ETH_ALEN (6) octets */
+       const u8 *key_prov_auto; /* 1 octet (Bool) */
+       const u8 *dot1x_enabled; /* 1 octet (Bool) */
+       const u8 *selected_registrar; /* 1 octet (Bool) */
+       const u8 *request_type; /* 1 octet */
+       const u8 *response_type; /* 1 octet */
+       const u8 *ap_setup_locked; /* 1 octet */
+       const u8 *settings_delay_time; /* 1 octet */
+       const u8 *network_key_shareable; /* 1 octet (Bool) */
+       const u8 *request_to_enroll; /* 1 octet (Bool) */
+       const u8 *ap_channel; /* 2 octets */
+
+       /* variable length fields */
+       const u8 *manufacturer;
+       size_t manufacturer_len;
+       const u8 *model_name;
+       size_t model_name_len;
+       const u8 *model_number;
+       size_t model_number_len;
+       const u8 *serial_number;
+       size_t serial_number_len;
+       const u8 *dev_name;
+       size_t dev_name_len;
+       const u8 *public_key;
+       size_t public_key_len;
+       const u8 *encr_settings;
+       size_t encr_settings_len;
+       const u8 *ssid; /* <= 32 octets */
+       size_t ssid_len;
+       const u8 *network_key; /* <= 64 octets */
+       size_t network_key_len;
+       const u8 *eap_type; /* <= 8 octets */
+       size_t eap_type_len;
+       const u8 *eap_identity; /* <= 64 octets */
+       size_t eap_identity_len;
+       const u8 *authorized_macs; /* <= 30 octets */
+       size_t authorized_macs_len;
+       const u8 *sec_dev_type_list; /* <= 128 octets */
+       size_t sec_dev_type_list_len;
+       const u8 *oob_dev_password; /* 38..54 octets */
+       size_t oob_dev_password_len;
+
+       /* attributes that can occur multiple times */
+#define MAX_CRED_COUNT 10
+       const u8 *cred[MAX_CRED_COUNT];
+       size_t cred_len[MAX_CRED_COUNT];
+       size_t num_cred;
+
+#define MAX_REQ_DEV_TYPE_COUNT 10
+       const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
+       size_t num_req_dev_type;
+
+       const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT];
+       size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT];
+       size_t num_vendor_ext;
+};
+
+int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
+
+#endif /* WPS_ATTR_PARSE_H */
diff --git a/components/wpa_supplicant/include/wps/wps_defs.h b/components/wpa_supplicant/include/wps/wps_defs.h
new file mode 100644 (file)
index 0000000..d100202
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ * Wi-Fi Protected Setup - message definitions
+ * 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.
+ */
+
+#ifndef WPS_DEFS_H
+#define WPS_DEFS_H
+
+#ifdef CONFIG_WPS_TESTING
+
+extern int wps_version_number;
+extern int wps_testing_dummy_cred;
+#define WPS_VERSION wps_version_number
+
+#else /* CONFIG_WPS_TESTING */
+
+#ifdef CONFIG_WPS2
+#define WPS_VERSION 0x20
+#else /* CONFIG_WPS2 */
+#define WPS_VERSION 0x10
+#endif /* CONFIG_WPS2 */
+
+#endif /* CONFIG_WPS_TESTING */
+
+#define CONFIG_WPS_STRICT
+
+/* Diffie-Hellman 1536-bit MODP Group; RFC 3526, Group 5 */
+#define WPS_DH_GROUP 5
+
+#define WPS_UUID_LEN 16
+#define WPS_NONCE_LEN 16
+#define WPS_AUTHENTICATOR_LEN 8
+#define WPS_AUTHKEY_LEN 32
+#define WPS_KEYWRAPKEY_LEN 16
+#define WPS_EMSK_LEN 32
+#define WPS_PSK_LEN 16
+#define WPS_SECRET_NONCE_LEN 16
+#define WPS_HASH_LEN 32
+#define WPS_KWA_LEN 8
+#define WPS_MGMTAUTHKEY_LEN 32
+#define WPS_MGMTENCKEY_LEN 16
+#define WPS_MGMT_KEY_ID_LEN 16
+#define WPS_OOB_DEVICE_PASSWORD_MIN_LEN 16
+#define WPS_OOB_DEVICE_PASSWORD_LEN 32
+#define WPS_OOB_PUBKEY_HASH_LEN 20
+
+/* Attribute Types */
+enum wps_attribute {
+       ATTR_AP_CHANNEL = 0x1001,
+       ATTR_ASSOC_STATE = 0x1002,
+       ATTR_AUTH_TYPE = 0x1003,
+       ATTR_AUTH_TYPE_FLAGS = 0x1004,
+       ATTR_AUTHENTICATOR = 0x1005,
+       ATTR_CONFIG_METHODS = 0x1008,
+       ATTR_CONFIG_ERROR = 0x1009,
+       ATTR_CONFIRM_URL4 = 0x100a,
+       ATTR_CONFIRM_URL6 = 0x100b,
+       ATTR_CONN_TYPE = 0x100c,
+       ATTR_CONN_TYPE_FLAGS = 0x100d,
+       ATTR_CRED = 0x100e,
+       ATTR_ENCR_TYPE = 0x100f,
+       ATTR_ENCR_TYPE_FLAGS = 0x1010,
+       ATTR_DEV_NAME = 0x1011,
+       ATTR_DEV_PASSWORD_ID = 0x1012,
+       ATTR_E_HASH1 = 0x1014,
+       ATTR_E_HASH2 = 0x1015,
+       ATTR_E_SNONCE1 = 0x1016,
+       ATTR_E_SNONCE2 = 0x1017,
+       ATTR_ENCR_SETTINGS = 0x1018,
+       ATTR_ENROLLEE_NONCE = 0x101a,
+       ATTR_FEATURE_ID = 0x101b,
+       ATTR_IDENTITY = 0x101c,
+       ATTR_IDENTITY_PROOF = 0x101d,
+       ATTR_KEY_WRAP_AUTH = 0x101e,
+       ATTR_KEY_ID = 0x101f,
+       ATTR_MAC_ADDR = 0x1020,
+       ATTR_MANUFACTURER = 0x1021,
+       ATTR_MSG_TYPE = 0x1022,
+       ATTR_MODEL_NAME = 0x1023,
+       ATTR_MODEL_NUMBER = 0x1024,
+       ATTR_NETWORK_INDEX = 0x1026,
+       ATTR_NETWORK_KEY = 0x1027,
+       ATTR_NETWORK_KEY_INDEX = 0x1028,
+       ATTR_NEW_DEVICE_NAME = 0x1029,
+       ATTR_NEW_PASSWORD = 0x102a,
+       ATTR_OOB_DEVICE_PASSWORD = 0x102c,
+       ATTR_OS_VERSION = 0x102d,
+       ATTR_POWER_LEVEL = 0x102f,
+       ATTR_PSK_CURRENT = 0x1030,
+       ATTR_PSK_MAX = 0x1031,
+       ATTR_PUBLIC_KEY = 0x1032,
+       ATTR_RADIO_ENABLE = 0x1033,
+       ATTR_REBOOT = 0x1034,
+       ATTR_REGISTRAR_CURRENT = 0x1035,
+       ATTR_REGISTRAR_ESTABLISHED = 0x1036,
+       ATTR_REGISTRAR_LIST = 0x1037,
+       ATTR_REGISTRAR_MAX = 0x1038,
+       ATTR_REGISTRAR_NONCE = 0x1039,
+       ATTR_REQUEST_TYPE = 0x103a,
+       ATTR_RESPONSE_TYPE = 0x103b,
+       ATTR_RF_BANDS = 0x103c,
+       ATTR_R_HASH1 = 0x103d,
+       ATTR_R_HASH2 = 0x103e,
+       ATTR_R_SNONCE1 = 0x103f,
+       ATTR_R_SNONCE2 = 0x1040,
+       ATTR_SELECTED_REGISTRAR = 0x1041,
+       ATTR_SERIAL_NUMBER = 0x1042,
+       ATTR_WPS_STATE = 0x1044,
+       ATTR_SSID = 0x1045,
+       ATTR_TOTAL_NETWORKS = 0x1046,
+       ATTR_UUID_E = 0x1047,
+       ATTR_UUID_R = 0x1048,
+       ATTR_VENDOR_EXT = 0x1049,
+       ATTR_VERSION = 0x104a,
+       ATTR_X509_CERT_REQ = 0x104b,
+       ATTR_X509_CERT = 0x104c,
+       ATTR_EAP_IDENTITY = 0x104d,
+       ATTR_MSG_COUNTER = 0x104e,
+       ATTR_PUBKEY_HASH = 0x104f,
+       ATTR_REKEY_KEY = 0x1050,
+       ATTR_KEY_LIFETIME = 0x1051,
+       ATTR_PERMITTED_CFG_METHODS = 0x1052,
+       ATTR_SELECTED_REGISTRAR_CONFIG_METHODS = 0x1053,
+       ATTR_PRIMARY_DEV_TYPE = 0x1054,
+       ATTR_SECONDARY_DEV_TYPE_LIST = 0x1055,
+       ATTR_PORTABLE_DEV = 0x1056,
+       ATTR_AP_SETUP_LOCKED = 0x1057,
+       ATTR_APPLICATION_EXT = 0x1058,
+       ATTR_EAP_TYPE = 0x1059,
+       ATTR_IV = 0x1060,
+       ATTR_KEY_PROVIDED_AUTO = 0x1061,
+       ATTR_802_1X_ENABLED = 0x1062,
+       ATTR_APPSESSIONKEY = 0x1063,
+       ATTR_WEPTRANSMITKEY = 0x1064,
+       ATTR_REQUESTED_DEV_TYPE = 0x106a,
+       ATTR_EXTENSIBILITY_TEST = 0x10fa /* _NOT_ defined in the spec */
+};
+
+#define WPS_VENDOR_ID_WFA 14122
+
+/* WFA Vendor Extension subelements */
+enum {
+       WFA_ELEM_VERSION2 = 0x00,
+       WFA_ELEM_AUTHORIZEDMACS = 0x01,
+       WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02,
+       WFA_ELEM_REQUEST_TO_ENROLL = 0x03,
+       WFA_ELEM_SETTINGS_DELAY_TIME = 0x04
+};
+
+/* Device Password ID */
+enum wps_dev_password_id {
+       DEV_PW_DEFAULT = 0x0000,
+       DEV_PW_USER_SPECIFIED = 0x0001,
+       DEV_PW_MACHINE_SPECIFIED = 0x0002,
+       DEV_PW_REKEY = 0x0003,
+       DEV_PW_PUSHBUTTON = 0x0004,
+       DEV_PW_REGISTRAR_SPECIFIED = 0x0005
+};
+
+/* WPS message flag */
+enum wps_msg_flag {
+       WPS_MSG_FLAG_MORE = 0x01,
+       WPS_MSG_FLAG_LEN = 0x02
+};
+
+/* Message Type */
+enum wps_msg_type {
+       WPS_Beacon = 0x01,
+       WPS_ProbeRequest = 0x02,
+       WPS_ProbeResponse = 0x03,
+       WPS_M1 = 0x04,
+       WPS_M2 = 0x05,
+       WPS_M2D = 0x06,
+       WPS_M3 = 0x07,
+       WPS_M4 = 0x08,
+       WPS_M5 = 0x09,
+       WPS_M6 = 0x0a,
+       WPS_M7 = 0x0b,
+       WPS_M8 = 0x0c,
+       WPS_WSC_ACK = 0x0d,
+       WPS_WSC_NACK = 0x0e,
+       WPS_WSC_DONE = 0x0f
+};
+
+/* Authentication Type Flags */
+#define WPS_WIFI_AUTH_OPEN 0x0001
+#define WPS_AUTH_WPAPSK 0x0002
+#define WPS_AUTH_SHARED 0x0004
+#define WPS_AUTH_WPA 0x0008
+#define WPS_AUTH_WPA2 0x0010
+#define WPS_AUTH_WPA2PSK 0x0020
+#define WPS_AUTH_TYPES (WPS_WIFI_AUTH_OPEN | WPS_AUTH_WPAPSK | WPS_AUTH_SHARED | \
+                       WPS_AUTH_WPA | WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)
+
+/* Encryption Type Flags */
+#define WPS_ENCR_NONE 0x0001
+#define WPS_ENCR_WEP 0x0002
+#define WPS_ENCR_TKIP 0x0004
+#define WPS_ENCR_AES 0x0008
+#define WPS_ENCR_TYPES (WPS_ENCR_NONE | WPS_ENCR_WEP | WPS_ENCR_TKIP | \
+                       WPS_ENCR_AES)
+
+/* Configuration Error */
+enum wps_config_error {
+       WPS_CFG_NO_ERROR = 0,
+       WPS_CFG_OOB_IFACE_READ_ERROR = 1,
+       WPS_CFG_DECRYPTION_CRC_FAILURE = 2,
+       WPS_CFG_24_CHAN_NOT_SUPPORTED = 3,
+       WPS_CFG_50_CHAN_NOT_SUPPORTED = 4,
+       WPS_CFG_SIGNAL_TOO_WEAK = 5,
+       WPS_CFG_NETWORK_AUTH_FAILURE = 6,
+       WPS_CFG_NETWORK_ASSOC_FAILURE = 7,
+       WPS_CFG_NO_DHCP_RESPONSE = 8,
+       WPS_CFG_FAILED_DHCP_CONFIG = 9,
+       WPS_CFG_IP_ADDR_CONFLICT = 10,
+       WPS_CFG_NO_CONN_TO_REGISTRAR = 11,
+       WPS_CFG_MULTIPLE_PBC_DETECTED = 12,
+       WPS_CFG_ROGUE_SUSPECTED = 13,
+       WPS_CFG_DEVICE_BUSY = 14,
+       WPS_CFG_SETUP_LOCKED = 15,
+       WPS_CFG_MSG_TIMEOUT = 16,
+       WPS_CFG_REG_SESS_TIMEOUT = 17,
+       WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18
+};
+
+/* Vendor specific Error Indication for WPS event messages */
+enum wps_error_indication {
+       WPS_EI_NO_ERROR,
+       WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED,
+       WPS_EI_SECURITY_WEP_PROHIBITED,
+       NUM_WPS_EI_VALUES
+};
+
+/* RF Bands */
+#define WPS_RF_24GHZ 0x01
+#define WPS_RF_50GHZ 0x02
+
+/* Config Methods */
+#define WPS_CONFIG_USBA 0x0001
+#define WPS_CONFIG_ETHERNET 0x0002
+#define WPS_CONFIG_LABEL 0x0004
+#define WPS_CONFIG_DISPLAY 0x0008
+#define WPS_CONFIG_EXT_NFC_TOKEN 0x0010
+#define WPS_CONFIG_INT_NFC_TOKEN 0x0020
+#define WPS_CONFIG_NFC_INTERFACE 0x0040
+#define WPS_CONFIG_PUSHBUTTON 0x0080
+#define WPS_CONFIG_KEYPAD 0x0100
+#ifdef CONFIG_WPS2
+#define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280
+#define WPS_CONFIG_PHY_PUSHBUTTON 0x0480
+#define WPS_CONFIG_VIRT_DISPLAY 0x2008
+#define WPS_CONFIG_PHY_DISPLAY 0x4008
+#endif /* CONFIG_WPS2 */
+
+/* Connection Type Flags */
+#define WPS_CONN_ESS 0x01
+#define WPS_CONN_IBSS 0x02
+
+/* Wi-Fi Protected Setup State */
+enum wps_state {
+       WPS_STATE_NOT_CONFIGURED = 1,
+       WPS_STATE_CONFIGURED = 2
+};
+
+/* Association State */
+enum wps_assoc_state {
+       WPS_ASSOC_NOT_ASSOC = 0,
+       WPS_ASSOC_CONN_SUCCESS = 1,
+       WPS_ASSOC_CFG_FAILURE = 2,
+       WPS_ASSOC_FAILURE = 3,
+       WPS_ASSOC_IP_FAILURE = 4
+};
+
+
+#define WPS_DEV_OUI_WFA 0x0050f204
+
+enum wps_dev_categ {
+       WPS_DEV_COMPUTER = 1,
+       WPS_DEV_INPUT = 2,
+       WPS_DEV_PRINTER = 3,
+       WPS_DEV_CAMERA = 4,
+       WPS_DEV_STORAGE = 5,
+       WPS_DEV_NETWORK_INFRA = 6,
+       WPS_DEV_DISPLAY = 7,
+       WPS_DEV_MULTIMEDIA = 8,
+       WPS_DEV_GAMING = 9,
+       WPS_DEV_PHONE = 10
+};
+
+enum wps_dev_subcateg {
+       WPS_DEV_COMPUTER_PC = 1,
+       WPS_DEV_COMPUTER_SERVER = 2,
+       WPS_DEV_COMPUTER_MEDIA_CENTER = 3,
+       WPS_DEV_PRINTER_PRINTER = 1,
+       WPS_DEV_PRINTER_SCANNER = 2,
+       WPS_DEV_CAMERA_DIGITAL_STILL_CAMERA = 1,
+       WPS_DEV_STORAGE_NAS = 1,
+       WPS_DEV_NETWORK_INFRA_AP = 1,
+       WPS_DEV_NETWORK_INFRA_ROUTER = 2,
+       WPS_DEV_NETWORK_INFRA_SWITCH = 3,
+       WPS_DEV_DISPLAY_TV = 1,
+       WPS_DEV_DISPLAY_PICTURE_FRAME = 2,
+       WPS_DEV_DISPLAY_PROJECTOR = 3,
+       WPS_DEV_MULTIMEDIA_DAR = 1,
+       WPS_DEV_MULTIMEDIA_PVR = 2,
+       WPS_DEV_MULTIMEDIA_MCX = 3,
+       WPS_DEV_GAMING_XBOX = 1,
+       WPS_DEV_GAMING_XBOX360 = 2,
+       WPS_DEV_GAMING_PLAYSTATION = 3,
+       WPS_DEV_PHONE_WINDOWS_MOBILE = 1
+};
+
+
+/* Request Type */
+enum wps_request_type {
+       WPS_REQ_ENROLLEE_INFO = 0,
+       WPS_REQ_ENROLLEE = 1,
+       WPS_REQ_REGISTRAR = 2,
+       WPS_REQ_WLAN_MANAGER_REGISTRAR = 3
+};
+
+/* Response Type */
+enum wps_response_type {
+       WPS_RESP_ENROLLEE_INFO = 0,
+       WPS_RESP_ENROLLEE = 1,
+       WPS_RESP_REGISTRAR = 2,
+       WPS_RESP_AP = 3
+};
+
+/* Walk Time for push button configuration (in seconds) */
+#define WPS_PBC_WALK_TIME 120
+
+#define WPS_MAX_AUTHORIZED_MACS 5
+
+#define WPS_IGNORE_SEL_REG_MAX_CNT     4
+
+#define WPS_MAX_DIS_AP_NUM     10
+
+#endif /* WPS_DEFS_H */
diff --git a/components/wpa_supplicant/include/wps/wps_dev_attr.h b/components/wpa_supplicant/include/wps/wps_dev_attr.h
new file mode 100644 (file)
index 0000000..200c9c4
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Wi-Fi Protected Setup - device attributes
+ * 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.
+ */
+
+#ifndef WPS_DEV_ATTR_H
+#define WPS_DEV_ATTR_H
+
+struct wps_parse_attr;
+
+int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_primary_dev_type(struct wps_device_data *dev,
+                              struct wpabuf *msg);
+int wps_build_secondary_dev_type(struct wps_device_data *dev,
+                                struct wpabuf *msg);
+int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_process_device_attrs(struct wps_device_data *dev,
+                            struct wps_parse_attr *attr);
+int wps_process_os_version(struct wps_device_data *dev, const u8 *ver);
+int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands);
+void wps_device_data_dup(struct wps_device_data *dst,
+                        const struct wps_device_data *src);
+void wps_device_data_free(struct wps_device_data *dev);
+int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg,
+                          unsigned int num_req_dev_types,
+                          const u8 *req_dev_types);
+
+#endif /* WPS_DEV_ATTR_H */
diff --git a/components/wpa_supplicant/include/wps/wps_i.h b/components/wpa_supplicant/include/wps/wps_i.h
new file mode 100644 (file)
index 0000000..c20d5ef
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * Wi-Fi Protected Setup - internal definitions
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPS_I_H
+#define WPS_I_H
+
+#include "wps.h"
+#include "wps_attr_parse.h"
+#include "esp_wifi_crypto_types.h"
+
+#ifdef CONFIG_WPS_NFC
+struct wps_nfc_pw_token;
+#endif
+/**
+ * struct wps_data - WPS registration protocol data
+ *
+ * This data is stored at the EAP-WSC server/peer method and it is kept for a
+ * single registration protocol run.
+ */
+struct wps_data {
+       /**
+        * wps - Pointer to long term WPS context
+        */
+       struct wps_context *wps;
+
+       /**
+        * registrar - Whether this end is a Registrar
+        */
+       int registrar;
+
+       /**
+        * er - Whether the local end is an external registrar
+        */
+       int er;
+
+       enum {
+               /* Enrollee states */
+               SEND_M1, RECV_M2, SEND_M3, RECV_M4, SEND_M5, RECV_M6, SEND_M7,
+               RECV_M8, RECEIVED_M2D, WPS_MSG_DONE, RECV_ACK, WPS_FINISHED,
+               SEND_WSC_NACK,
+
+               /* Registrar states */
+               RECV_M1, SEND_M2, RECV_M3, SEND_M4, RECV_M5, SEND_M6,
+               RECV_M7, SEND_M8, RECV_DONE, SEND_M2D, RECV_M2D_ACK
+       } state;
+
+       u8 uuid_e[WPS_UUID_LEN];
+       u8 uuid_r[WPS_UUID_LEN];
+       u8 mac_addr_e[ETH_ALEN];
+       u8 nonce_e[WPS_NONCE_LEN];
+       u8 nonce_r[WPS_NONCE_LEN];
+       u8 psk1[WPS_PSK_LEN];
+       u8 psk2[WPS_PSK_LEN];
+       u8 snonce[2 * WPS_SECRET_NONCE_LEN];
+       u8 peer_hash1[WPS_HASH_LEN];
+       u8 peer_hash2[WPS_HASH_LEN];
+
+       struct wpabuf *dh_privkey;
+       struct wpabuf *dh_pubkey_e;
+       struct wpabuf *dh_pubkey_r;
+       u8 authkey[WPS_AUTHKEY_LEN];
+       u8 keywrapkey[WPS_KEYWRAPKEY_LEN];
+       u8 emsk[WPS_EMSK_LEN];
+
+       struct wpabuf *last_msg;
+
+       u8 *dev_password;
+       size_t dev_password_len;
+       u16 dev_pw_id;
+       int pbc;
+
+       /**
+        * request_type - Request Type attribute from (Re)AssocReq
+        */
+       u8 request_type;
+
+       /**
+        * encr_type - Available encryption types
+        */
+       u16 encr_type;
+
+       /**
+        * auth_type - Available authentication types
+        */
+       u16 auth_type;
+
+       u8 *new_psk;
+       size_t new_psk_len;
+
+       int wps_pin_revealed;
+       struct wps_credential cred;
+
+       struct wps_device_data peer_dev;
+
+       /**
+        * config_error - Configuration Error value to be used in NACK
+        */
+       u16 config_error;
+       u16 error_indication;
+
+       int ext_reg;
+       int int_reg;
+
+       struct wps_credential *new_ap_settings;
+
+       void *dh_ctx;
+
+       void (*ap_settings_cb)(void *ctx, const struct wps_credential *cred);
+       void *ap_settings_cb_ctx;
+
+       struct wps_credential *use_cred;
+
+       int use_psk_key;
+       u8 p2p_dev_addr[ETH_ALEN]; /* P2P Device Address of the client or
+                                   * 00:00:00:00:00:00 if not a P2p client */
+       int pbc_in_m1;
+#ifdef CONFIG_WPS_NFC
+       struct wps_nfc_pw_token *nfc_pw_token;
+#endif
+};
+
+wps_crypto_funcs_t wps_crypto_funcs;
+
+/* wps_common.c */
+void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
+            const char *label, u8 *res, size_t res_len);
+int wps_derive_keys(struct wps_data *wps);
+void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
+                   size_t dev_passwd_len);
+struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
+                                         size_t encr_len);
+void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
+                   u16 config_error, u16 error_indication);
+void wps_success_event(struct wps_context *wps);
+void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part);
+void wps_pbc_overlap_event(struct wps_context *wps);
+void wps_pbc_timeout_event(struct wps_context *wps);
+
+struct wpabuf * wps_build_wsc_ack(struct wps_data *wps);
+struct wpabuf * wps_build_wsc_nack(struct wps_data *wps);
+
+typedef enum wps_calc_key_mode {
+       WPS_CALC_KEY_NORMAL = 0,
+       WPS_CALC_KEY_NO_CALC,
+       WPS_CALC_KEY_PRE_CALC,
+       WPS_CALC_KEY_MAX,
+} wps_key_mode_t;
+
+/* wps_attr_build.c */
+int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg, wps_key_mode_t mode);
+int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type);
+int wps_build_resp_type(struct wpabuf *msg, enum wps_response_type type);
+int wps_build_config_methods(struct wpabuf *msg, u16 methods);
+int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid);
+int wps_build_dev_password_id(struct wpabuf *msg, u16 id);
+int wps_build_config_error(struct wpabuf *msg, u16 err);
+int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
+                           struct wpabuf *plain);
+int wps_build_version(struct wpabuf *msg);
+int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
+                     const u8 *auth_macs, size_t auth_macs_count);
+int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type);
+int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id,
+                        const struct wpabuf *pubkey, const u8 *dev_pw,
+                        size_t dev_pw_len);
+struct wpabuf * wps_ie_encapsulate(struct wpabuf *data);
+
+/* wps_attr_process.c */
+int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
+                             const struct wpabuf *msg);
+int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg,
+                             const u8 *key_wrap_auth);
+int wps_process_cred(struct wps_parse_attr *attr,
+                    struct wps_credential *cred);
+int wps_process_ap_settings(struct wps_parse_attr *attr,
+                           struct wps_credential *cred);
+
+/* wps_enrollee.c */
+struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps,
+                                    enum wsc_op_code *op_code);
+enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
+                                             enum wsc_op_code op_code,
+                                             const struct wpabuf *msg);
+
+/* wps_registrar.c */
+struct wpabuf * wps_registrar_get_msg(struct wps_data *wps,
+                                     enum wsc_op_code *op_code);
+enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
+                                              enum wsc_op_code op_code,
+                                              const struct wpabuf *msg);
+int wps_build_cred(struct wps_data *wps, struct wpabuf *msg);
+int wps_device_store(struct wps_registrar *reg,
+                    struct wps_device_data *dev, const u8 *uuid);
+void wps_registrar_selected_registrar_changed(struct wps_registrar *reg);
+const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count);
+int wps_registrar_pbc_overlap(struct wps_registrar *reg,
+                             const u8 *addr, const u8 *uuid_e);
+#ifdef CONFIG_WPS_NFC
+
+void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg,
+                                      struct wps_nfc_pw_token *token);
+#endif
+
+#endif /* WPS_I_H */
index 468d54ffe826e984b3febc0e9f7a9cb9a3f92cba..48f7ab85e4116c3543ea39d845619cce0abcd213 100644 (file)
@@ -18,6 +18,7 @@
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include "esp_err.h"
 #include "rom/ets_sys.h"
 
 typedef long os_time_t;
@@ -201,6 +202,10 @@ char * os_readfile(const char *name, size_t *len);
 #define os_free(p) free((p))
 #endif
 
+#ifndef os_bzero
+#define os_bzero(s, n) bzero(s, n)
+#endif 
+
 
 #ifndef os_strdup
 #ifdef _MSC_VER
diff --git a/components/wpa_supplicant/src/wpa2/eap_peer/chap.c b/components/wpa_supplicant/src/wpa2/eap_peer/chap.c
new file mode 100644 (file)
index 0000000..eb613ca
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * CHAP-MD5
+ *
+ */
+#ifdef CHAP_MD5
+
+#include "wpa/includes.h"
+#include "wpa/common.h"
+#include "crypto/crypto.h"
+#include "wpa2/eap_peer/chap.h"
+
+int chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge,
+            size_t challenge_len, u8 *response)
+{
+       const u8 *addr[3];
+       size_t len[3];
+
+       addr[0] = &id;
+       len[0] = 1;
+       addr[1] = secret;
+       len[1] = secret_len;
+       addr[2] = challenge;
+       len[2] = challenge_len;
+       return md5_vector(3, addr, len, response);
+}
+
+#endif /* CHAP_MD5 */
diff --git a/components/wpa_supplicant/src/wpa2/eap_peer/eap.c b/components/wpa_supplicant/src/wpa2/eap_peer/eap.c
new file mode 100644 (file)
index 0000000..d492831
--- /dev/null
@@ -0,0 +1,731 @@
+/*
+ * EAP peer state machines (RFC 4137)
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file implements the Peer State Machine as defined in RFC 4137. The used
+ * states and state transitions match mostly with the RFC. However, there are
+ * couple of additional transitions for working around small issues noticed
+ * during testing. These exceptions are explained in comments within the
+ * functions in this file. The method functions, m.func(), are similar to the
+ * ones used in RFC 4137, but some small changes have used here to optimize
+ * operations and to add functionality needed for fast re-authentication
+ * (session resumption).
+ */
+#include <string.h>
+     
+#include "esp_err.h"
+
+#include "wpa/includes.h"
+#include "wpa/common.h"
+#include "wpa/wpa_debug.h"
+#include "wpa/eapol_common.h"
+#include "wpa/ieee802_11_defs.h"
+#include "wpa/state_machine.h"
+#include "wpa/wpa.h"
+
+#include "crypto/crypto.h"
+
+#include "wpa2/utils/ext_password.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/eap_peer/eap_i.h"
+#include "wpa2/eap_peer/eap_config.h"
+#include "wpa2/eap_peer/eap.h"
+#include "wpa2/eap_peer/eap_tls.h"
+#ifdef EAP_PEER_METHOD
+#include "wpa2/eap_peer/eap_methods.h"
+#endif
+
+
+static bool gl_disable_time_check = true;
+void eap_peer_config_deinit(struct eap_sm *sm);
+void eap_peer_blob_deinit(struct eap_sm *sm);
+void eap_deinit_prev_method(struct eap_sm *sm, const char *txt);
+
+extern bool ieee80211_unregister_wpa2_cb(void);
+
+#ifdef EAP_PEER_METHOD
+static struct eap_method *eap_methods = NULL;
+
+const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method)
+{
+       struct eap_method *m;
+       for (m = eap_methods; m; m = m->next) {
+               if (m->vendor == vendor && m->method == method)
+                       return m;
+       }
+       return NULL;
+}
+
+const struct eap_method * eap_peer_get_methods(size_t *count)
+{
+       int c = 0;
+       struct eap_method *m;
+
+       for (m = eap_methods; m; m = m->next)
+               c++;
+
+       *count = c;
+       return eap_methods;
+}
+
+EapType eap_peer_get_type(const char *name, int *vendor)
+{
+       struct eap_method *m;
+       for (m = eap_methods; m; m = m->next) {
+               if (os_strcmp(m->name, name) == 0) {
+                       *vendor = m->vendor;
+                       return m->method;
+               }
+       }
+       *vendor = EAP_VENDOR_IETF;
+       return EAP_TYPE_NONE;
+}
+
+static int 
+eap_allowed_phase2_type(int vendor, int type)
+{
+       if (vendor != EAP_VENDOR_IETF)
+               return 0;
+       return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS &&
+               type != EAP_TYPE_FAST;
+}
+
+u32 eap_get_phase2_type(const char *name, int *vendor)
+{
+       int v;
+       u8 type = eap_peer_get_type(name, &v);
+       if (eap_allowed_phase2_type(v, type)) {
+               *vendor = v;
+               return type;
+       }
+       *vendor = EAP_VENDOR_IETF;
+       return EAP_TYPE_NONE;
+}
+
+struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config,
+                    size_t *count)
+{
+       struct eap_method_type *buf;
+       u32 method;
+       int vendor;
+       size_t mcount;
+       const struct eap_method *methods, *m;
+
+       methods = eap_peer_get_methods(&mcount);
+       if (methods == NULL)
+               return NULL;
+       *count = 0;
+       buf = os_malloc(mcount * sizeof(struct eap_method_type));
+       if (buf == NULL)
+               return NULL;
+
+       for (m = methods; m; m = m->next) {
+               vendor = m->vendor;
+               method = m->method;
+               if (eap_allowed_phase2_type(vendor, method)) {
+                       if (vendor == EAP_VENDOR_IETF &&
+                           method == EAP_TYPE_TLS && config &&
+                           config->private_key2 == NULL)
+                               continue;
+                       buf[*count].vendor = vendor;
+                       buf[*count].method = method;
+                       (*count)++;
+               }
+       }
+
+       return buf;
+}
+
+struct eap_method * eap_peer_method_alloc(int vendor, EapType method,
+                     const char *name)
+{
+       struct eap_method *eap;
+       eap = (struct eap_method *)os_zalloc(sizeof(*eap));
+       if (eap == NULL)
+               return NULL;
+       eap->vendor = vendor;
+       eap->method = method;
+       eap->name = name;
+       return eap;
+}
+
+void eap_peer_method_free(struct eap_method *method)
+{
+       os_free(method);
+}
+
+int eap_peer_method_register(struct eap_method *method)
+{
+       struct eap_method *m, *last = NULL;
+
+       if (method == NULL || method->name == NULL)
+               return -1;
+       for (m = eap_methods; m; m = m->next) {
+               if (m->vendor == method->vendor &&
+                   m->method == method->method &&
+                   os_strcmp(m->name, method->name))
+                       return -2;
+               last = m;
+       }
+       if (last)
+               last->next = method;
+       else
+               eap_methods = method;
+       return 0;
+}
+
+void eap_peer_unregister_methods(void)
+{
+       struct eap_method *m;
+       while (eap_methods) {
+               m = eap_methods;
+               eap_methods = eap_methods->next;
+
+               if (m->free)
+                       m->free(m);
+               else
+                       eap_peer_method_free(m);
+       }
+}
+
+int eap_peer_register_methods(void)
+{
+       int ret = 0;
+
+#ifdef EAP_MD5
+       if (ret == 0)
+               ret = eap_peer_md5_register();
+#endif
+
+#ifdef EAP_TLS
+       if (ret == 0)
+               ret = eap_peer_tls_register();
+#endif
+
+#ifdef EAP_MSCHAPv2
+       if (ret == 0)
+               ret = eap_peer_mschapv2_register();
+#endif
+
+#ifdef EAP_PEAP
+       if (ret == 0)
+               ret = eap_peer_peap_register();
+#endif
+
+#ifdef EAP_TTLS
+       if (ret == 0)
+               ret = eap_peer_ttls_register();
+#endif
+
+       return ret;
+}
+
+void eap_deinit_prev_method(struct eap_sm *sm, const char *txt)
+{
+       if (sm->m == NULL || sm->eap_method_priv == NULL)
+               return;
+       sm->m->deinit(sm, sm->eap_method_priv);
+       sm->eap_method_priv = NULL;
+       sm->m = NULL;
+}
+
+struct wpabuf * eap_sm_build_identity_resp(struct eap_sm *sm, u8 id, int encrypted)
+{
+       const u8 *identity;
+       size_t identity_len;
+       struct wpabuf *eap_buf = NULL;
+       struct eap_peer_config *config = eap_get_config(sm);
+
+       if (config == NULL) {
+        wpa_printf(MSG_ERROR, "EAP: Build Identity Resp-> configuration was not available\n");
+               return NULL;
+       }
+
+       if (sm->m && sm->m->get_identity) {
+               identity = sm->m->get_identity(sm,
+                                       sm->eap_method_priv,
+                                       &identity_len);
+       } else if (!encrypted && config->anonymous_identity) {
+               identity = config->anonymous_identity;
+               identity_len = config->anonymous_identity_len;
+       } else {
+               identity = config->identity;
+               identity_len = config->identity_len;
+       }
+
+       if (identity == NULL) {
+        wpa_printf(MSG_ERROR, "EAP: Build Identity Resp-> identity was not available\n");
+               return NULL;
+       }
+
+       eap_buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY,
+                               identity_len, EAP_CODE_RESPONSE, id);
+       if (!eap_buf) {
+        return NULL;       
+    }
+
+       wpabuf_put_data(eap_buf, identity, identity_len);
+       return eap_buf;
+}
+
+struct wpabuf * eap_sm_build_nak(struct eap_sm *sm, EapType type, u8 id)
+{
+       size_t count = 0;
+       int found = 0;
+       struct wpabuf *resp;
+       const struct eap_method *methods, *m;
+
+       methods = eap_peer_get_methods(&count);
+       if (methods == NULL)
+               return NULL;
+
+       if (type == EAP_TYPE_EXPANDED) {
+               /*Build Expanded NAK*/
+               resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EXPANDED,
+                                    8 + 8 * (count + 1), EAP_CODE_RESPONSE, id);
+               if (resp == NULL)
+                       return NULL;
+               wpabuf_put_be24(resp, EAP_VENDOR_IETF);
+               wpabuf_put_be32(resp, EAP_TYPE_NAK);
+       } else {
+               resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK,
+                                    sizeof(struct eap_hdr) + 1 + count + 1,
+                                    EAP_CODE_RESPONSE, id);
+               if (resp == NULL)
+                       return NULL;
+               wpabuf_put(resp, 0);
+       }
+
+       for (m = methods; m; m = m->next) {
+               if (type == EAP_TYPE_EXPANDED) {
+                       wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
+                       wpabuf_put_be24(resp, m->vendor);
+                       wpabuf_put_be32(resp, m->method);
+               } else
+                       wpabuf_put_u8(resp, EAP_TYPE_NONE);
+               found++;
+       }
+       if (!found) {
+               if (type == EAP_TYPE_EXPANDED) {
+                       wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
+                       wpabuf_put_be24(resp, EAP_VENDOR_IETF);
+                       wpabuf_put_be32(resp, EAP_TYPE_NONE);
+               } else
+                       wpabuf_put_u8(resp, EAP_TYPE_NONE);
+       }
+       eap_update_len(resp);
+       return resp;
+}
+#endif
+
+int eap_peer_config_init(
+       struct eap_sm *sm, u8 *private_key_passwd,
+       int private_key_passwd_len)
+{
+       if (!sm)
+               return -1;
+
+       sm->config.anonymous_identity = NULL;
+       sm->config.identity = NULL;
+       sm->config.password = NULL;
+       sm->config.new_password = NULL;
+
+       sm->config.private_key_passwd = private_key_passwd;
+       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 */
+
+       /* anonymous identity */
+       if (g_wpa_anonymous_identity && g_wpa_anonymous_identity_len > 0) {
+           sm->config.anonymous_identity_len = g_wpa_anonymous_identity_len;
+           sm->config.anonymous_identity = (u8 *)os_zalloc(sm->config.anonymous_identity_len);
+           if (sm->config.anonymous_identity == NULL)
+                   return -2;
+           os_memcpy(sm->config.anonymous_identity, g_wpa_anonymous_identity, g_wpa_anonymous_identity_len);
+       }
+
+       /* Configre identity */
+       if (g_wpa_username && g_wpa_username_len > 0) {
+               sm->config.identity_len = g_wpa_username_len;
+               sm->config.identity = (u8 *)os_zalloc(sm->config.identity_len);
+               if (sm->config.identity == NULL) {
+                       return -2;
+               }
+               os_memcpy(sm->config.identity, g_wpa_username, g_wpa_username_len);
+       }
+
+       if (g_wpa_password && g_wpa_password_len) {
+               sm->config.password_len = g_wpa_password_len;
+               sm->config.password = (u8 *)os_zalloc(sm->config.password_len);
+               if (sm->config.password == NULL)
+                       return -2;
+               os_memcpy(sm->config.password, g_wpa_password, sm->config.password_len);
+       }
+
+       if (g_wpa_new_password && g_wpa_new_password_len) {
+               sm->config.new_password_len = g_wpa_new_password_len;
+               sm->config.new_password = (u8 *)os_zalloc(sm->config.new_password_len);
+               if (sm->config.new_password == NULL)
+                       return -2;
+               os_memcpy(sm->config.new_password, g_wpa_new_password,
+                         sm->config.new_password_len);
+       }
+
+       return 0;
+       
+}
+
+void eap_peer_config_deinit(struct eap_sm *sm)
+{
+       if (!sm)
+               return;
+
+       os_free(sm->config.anonymous_identity);
+       os_free(sm->config.identity);
+       os_free(sm->config.password);
+       os_free(sm->config.new_password);
+       os_bzero(&sm->config, sizeof(struct eap_peer_config));
+}
+
+int eap_peer_blob_init(struct eap_sm *sm)
+{
+       int i, ret;
+
+       if (!sm)
+               return -1;
+
+       if (g_wpa_client_cert && g_wpa_client_cert_len) {
+               sm->blob[0].name = (char *)os_zalloc(BLOB_NAME_LEN+1);
+               if (sm->blob[0].name == NULL) {
+                       ret = -2;
+                       goto _out;
+               }
+               os_strncpy(sm->blob[0].name, CLIENT_CERT_NAME, BLOB_NAME_LEN);
+               sm->blob[0].len = g_wpa_client_cert_len;
+               sm->blob[0].data = g_wpa_client_cert;
+       }
+
+       if (g_wpa_private_key && g_wpa_private_key_len) {
+               sm->blob[1].name = (char *)os_zalloc(BLOB_NAME_LEN+1);
+               if (sm->blob[1].name == NULL) {
+                       ret = -2;
+                       goto _out;
+               }
+               os_strncpy(sm->blob[1].name, PRIVATE_KEY_NAME, BLOB_NAME_LEN);
+               sm->blob[1].len = g_wpa_private_key_len;
+               sm->blob[1].data = g_wpa_private_key;
+       }
+
+       if (g_wpa_ca_cert && g_wpa_ca_cert_len) {
+               sm->blob[2].name = (char *)os_zalloc(BLOB_NAME_LEN+1);
+               if (sm->blob[2].name == NULL) {
+                       ret = -2;
+                       goto _out;
+               }
+               os_strncpy(sm->blob[2].name, CA_CERT_NAME, BLOB_NAME_LEN);
+               sm->blob[2].len = g_wpa_ca_cert_len;
+               sm->blob[2].data = g_wpa_ca_cert;
+       }
+
+       return 0;
+_out:
+       for (i = 0; i < BLOB_NUM; i++) {
+               if (sm->blob[i].name) {
+                       os_free(sm->blob[i].name);
+                       sm->blob[i].name = NULL;
+               }
+       }
+       os_bzero(&sm->blob[0], sizeof(struct wpa_config_blob)*BLOB_NUM);
+
+       return ret;
+}
+
+void eap_peer_blob_deinit(struct eap_sm *sm)
+{
+       int i;
+       for (i = 0; i < BLOB_NUM; i++) {
+               if (sm->blob[i].name) {
+                       os_free(sm->blob[i].name);
+                       sm->blob[i].name = NULL;
+               }
+       }
+       os_bzero(&sm->blob[0], sizeof(struct wpa_config_blob)*BLOB_NUM);
+
+       sm->config.client_cert = NULL;
+       sm->config.private_key = NULL;
+       sm->config.ca_cert = NULL;
+}
+
+void eap_sm_abort(struct eap_sm *sm)
+{
+       wpabuf_free(sm->lastRespData);
+       sm->lastRespData = NULL;
+       //os_free(sm->eapKeyData);
+       //sm->eapKeyData = NULL;
+}
+
+/**
+ * eap_get_config - Get current network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to the current network configuration or %NULL if not found
+ *
+ * EAP peer methods should avoid using this function if they can use other
+ * access functions, like eap_get_config_identity() and
+ * eap_get_config_password(), that do not require direct access to
+ * struct eap_peer_config.
+ */
+struct eap_peer_config * eap_get_config(struct eap_sm *sm)
+{
+       return &sm->config;
+}
+
+const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+       if (config == NULL)
+               return NULL;
+       *len = config->identity_len;
+       return config->identity;
+}
+
+const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+       if (config == NULL)
+               return NULL;
+       *len = config->password_len;
+       return config->password;
+}
+
+const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+       if (config == NULL)
+               return NULL;
+
+       *len = config->password_len;
+       if (hash)
+               *hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH);
+       return config->password;
+}
+
+const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+       if (config == NULL)
+               return NULL;
+       *len = config->new_password_len;
+       return config->new_password;
+}
+/**
+ * eap_get_config_blob - Get a named configuration blob
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @name: Name of the blob
+ * Returns: Pointer to blob data or %NULL if not found
+ */
+const struct wpa_config_blob * eap_get_config_blob(struct eap_sm *sm,
+                                                  const char *name)
+{
+       int i;
+
+       if (!sm)
+               return NULL;
+
+       for (i = 0; i < BLOB_NUM; i++) {
+               if (sm->blob[i].name == NULL)
+                       continue;
+               if (os_strncmp(name, sm->blob[i].name, BLOB_NAME_LEN) == 0) {
+                       return &sm->blob[i];
+               }
+       }
+       return NULL;
+}
+
+esp_err_t esp_wifi_sta_wpa2_ent_set_cert_key(const unsigned char *client_cert, int client_cert_len, const unsigned char *private_key, int private_key_len, const unsigned char *private_key_passwd, int private_key_passwd_len)
+{
+       if (client_cert && client_cert_len > 0) {
+               g_wpa_client_cert = client_cert;
+               g_wpa_client_cert_len = client_cert_len;
+       }
+       if (private_key && private_key_len > 0) {
+               g_wpa_private_key = private_key;
+               g_wpa_private_key_len = private_key_len;
+       } 
+       if (private_key_passwd && private_key_passwd_len > 0) {
+               g_wpa_private_key_passwd = private_key_passwd;
+               g_wpa_private_key_passwd_len = private_key_passwd_len;
+       }
+
+       return ESP_OK;
+}
+
+void esp_wifi_sta_wpa2_ent_clear_cert_key(void)
+{
+       ieee80211_unregister_wpa2_cb();
+
+    g_wpa_client_cert = NULL;
+    g_wpa_client_cert_len = 0;
+    g_wpa_private_key = NULL;
+    g_wpa_private_key_len = 0;
+    g_wpa_private_key_passwd = NULL;
+    g_wpa_private_key_passwd_len = 0;
+}
+
+esp_err_t esp_wifi_sta_wpa2_ent_set_ca_cert(const unsigned char *ca_cert, int ca_cert_len)
+{
+       if (ca_cert && ca_cert_len > 0) {
+               g_wpa_ca_cert = ca_cert;
+               g_wpa_ca_cert_len = ca_cert_len;        
+       }
+
+       return ESP_OK;
+}
+
+void esp_wifi_sta_wpa2_ent_clear_ca_cert(void)
+{
+       g_wpa_ca_cert = NULL;
+       g_wpa_ca_cert_len = 0;
+}
+
+#define ANONYMOUS_ID_LEN_MAX 128
+esp_err_t esp_wifi_sta_wpa2_ent_set_identity(const unsigned char *identity, int len)
+{
+       if (len <= 0 || len > ANONYMOUS_ID_LEN_MAX) {
+               return ESP_ERR_INVALID_ARG;
+       }
+
+       if (g_wpa_anonymous_identity) {
+               os_free(g_wpa_anonymous_identity);
+               g_wpa_anonymous_identity = NULL;
+       }
+
+       g_wpa_anonymous_identity = (u8 *)os_zalloc(len);
+       if (g_wpa_anonymous_identity == NULL) {
+               return ESP_ERR_NO_MEM;
+       }
+
+       os_memcpy(g_wpa_anonymous_identity, identity, len);
+       g_wpa_anonymous_identity_len = len;
+
+       return ESP_OK;
+}
+
+void esp_wifi_sta_wpa2_ent_clear_identity(void)
+{
+       if (g_wpa_anonymous_identity)
+               os_free(g_wpa_anonymous_identity);
+
+       g_wpa_anonymous_identity = NULL;
+       g_wpa_anonymous_identity_len = 0;
+}
+
+#define USERNAME_LEN_MAX 128
+esp_err_t esp_wifi_sta_wpa2_ent_set_username(const unsigned char *username, int len)
+{
+       if (len <= 0 || len > USERNAME_LEN_MAX)
+               return ESP_ERR_INVALID_ARG;
+
+       if (g_wpa_username) {
+               os_free(g_wpa_username);
+               g_wpa_username = NULL;
+       }
+
+       g_wpa_username = (u8 *)os_zalloc(len);
+       if (g_wpa_username == NULL)
+               return ESP_ERR_NO_MEM;
+
+       os_memcpy(g_wpa_username, username, len);
+       g_wpa_username_len = len;
+
+       return ESP_OK;
+}
+
+void esp_wifi_sta_wpa2_ent_clear_username(void)
+{
+       if (g_wpa_username)
+               os_free(g_wpa_username);
+
+       g_wpa_username = NULL;
+       g_wpa_username_len = 0;
+}
+
+esp_err_t esp_wifi_sta_wpa2_ent_set_password(const unsigned char *password, int len)
+{
+       if (len <= 0)
+               return ESP_ERR_INVALID_ARG;
+
+       if (g_wpa_password) {
+               os_free(g_wpa_password);
+               g_wpa_password = NULL;
+       }
+
+       g_wpa_password = (u8 *)os_zalloc(len);
+       if (g_wpa_password == NULL)
+               return ESP_ERR_NO_MEM;
+
+       os_memcpy(g_wpa_password, password, len);
+       g_wpa_password_len = len;
+
+       return ESP_OK;
+}
+
+void esp_wifi_sta_wpa2_ent_clear_password(void)
+{
+       if (g_wpa_password)
+               os_free(g_wpa_password);
+       g_wpa_password = NULL;
+       g_wpa_password_len = 0;
+}
+
+esp_err_t esp_wifi_sta_wpa2_ent_set_new_password(const unsigned char *new_password, int len)
+{
+       if (len <= 0)
+               return ESP_ERR_INVALID_ARG;
+
+       if (g_wpa_new_password) {
+               os_free(g_wpa_new_password);
+               g_wpa_new_password = NULL;
+       }
+
+       g_wpa_new_password = (u8 *)os_zalloc(len);
+       if (g_wpa_new_password == NULL)
+               return ESP_ERR_NO_MEM;
+
+       os_memcpy(g_wpa_new_password, new_password, len);
+       g_wpa_password_len = len;
+
+       return ESP_OK;
+}
+
+void esp_wifi_sta_wpa2_ent_clear_new_password(void)
+{
+       if (g_wpa_new_password)
+               os_free(g_wpa_new_password);
+       g_wpa_new_password = NULL;
+       g_wpa_new_password_len = 0;
+}
+
+esp_err_t esp_wifi_sta_wpa2_ent_set_disable_time_check(bool disable)
+{
+       gl_disable_time_check = disable;
+       return ESP_OK;
+}
+
+bool wifi_sta_get_enterprise_disable_time_check(void)
+{
+       return gl_disable_time_check;
+}
+
+esp_err_t esp_wifi_sta_wpa2_ent_get_disable_time_check(bool *disable)
+{
+       *disable = wifi_sta_get_enterprise_disable_time_check();
+       return ESP_OK;
+}
+
diff --git a/components/wpa_supplicant/src/wpa2/eap_peer/eap_common.c b/components/wpa_supplicant/src/wpa2/eap_peer/eap_common.c
new file mode 100644 (file)
index 0000000..a1748b1
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * EAP common peer/server definitions
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wpa2/eap_peer/eap_defs.h"
+#include "wpa2/eap_peer/eap_common.h"
+
+/**
+ * eap_hdr_len_valid - Validate EAP header length field
+ * @msg: EAP frame (starting with EAP header)
+ * @min_payload: Minimum payload length needed
+ * Returns: 1 for valid header, 0 for invalid
+ *
+ * This is a helper function that does minimal validation of EAP messages. The
+ * length field is verified to be large enough to include the header and not
+ * too large to go beyond the end of the buffer.
+ */
+int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload)
+{
+       const struct eap_hdr *hdr;
+       size_t len;
+
+       if (msg == NULL)
+               return 0;
+
+       hdr = wpabuf_head(msg);
+
+       if (wpabuf_len(msg) < sizeof(*hdr)) {
+               wpa_printf(MSG_INFO, "EAP: Too short EAP frame");
+               return 0;
+       }
+
+       len = be_to_host16(hdr->length);
+       if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) {
+               wpa_printf(MSG_INFO, "EAP: Invalid EAP length");
+               return 0;
+       }
+
+       return 1;
+}
+
+
+/**
+ * eap_hdr_validate - Validate EAP header
+ * @vendor: Expected EAP Vendor-Id (0 = IETF)
+ * @eap_type: Expected EAP type number
+ * @msg: EAP frame (starting with EAP header)
+ * @plen: Pointer to variable to contain the returned payload length
+ * Returns: Pointer to EAP payload (after type field), or %NULL on failure
+ *
+ * This is a helper function for EAP method implementations. This is usually
+ * called in the beginning of struct eap_method::process() function to verify
+ * that the received EAP request packet has a valid header. This function is
+ * able to process both legacy and expanded EAP headers and in most cases, the
+ * caller can just use the returned payload pointer (into *plen) for processing
+ * the payload regardless of whether the packet used the expanded EAP header or
+ * not.
+ */
+const u8 * eap_hdr_validate(int vendor, EapType eap_type,
+                           const struct wpabuf *msg, size_t *plen)
+{
+       const struct eap_hdr *hdr;
+       const u8 *pos;
+       size_t len;
+
+       if (!eap_hdr_len_valid(msg, 1))
+               return NULL;
+
+       hdr = wpabuf_head(msg);
+       len = be_to_host16(hdr->length);
+       pos = (const u8 *) (hdr + 1);
+
+       if (*pos == EAP_TYPE_EXPANDED) {
+               int exp_vendor;
+               u32 exp_type;
+               if (len < sizeof(*hdr) + 8) {
+                       wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP "
+                                  "length");
+                       return NULL;
+               }
+               pos++;
+               exp_vendor = WPA_GET_BE24(pos);
+               pos += 3;
+               exp_type = WPA_GET_BE32(pos);
+               pos += 4;
+               if (exp_vendor != vendor || exp_type != (u32) eap_type) {
+                       wpa_printf(MSG_INFO, "EAP: Invalid expanded frame "
+                                  "type");
+                       return NULL;
+               }
+
+               *plen = len - sizeof(*hdr) - 8;
+               return pos;
+       } else {
+               if (vendor != EAP_VENDOR_IETF || *pos != eap_type) {
+                       wpa_printf(MSG_INFO, "EAP: Invalid frame type");
+                       return NULL;
+               }
+               *plen = len - sizeof(*hdr) - 1;
+               return pos + 1;
+       }
+}
+
+
+/**
+ * eap_msg_alloc - Allocate a buffer for an EAP message
+ * @vendor: Vendor-Id (0 = IETF)
+ * @type: EAP type
+ * @payload_len: Payload length in bytes (data after Type)
+ * @code: Message Code (EAP_CODE_*)
+ * @identifier: Identifier
+ * Returns: Pointer to the allocated message buffer or %NULL on error
+ *
+ * This function can be used to allocate a buffer for an EAP message and fill
+ * in the EAP header. This function is automatically using expanded EAP header
+ * if the selected Vendor-Id is not IETF. In other words, most EAP methods do
+ * not need to separately select which header type to use when using this
+ * function to allocate the message buffers. The returned buffer has room for
+ * payload_len bytes and has the EAP header and Type field already filled in.
+ */
+struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
+                             u8 code, u8 identifier)
+{
+       struct wpabuf *buf;
+       struct eap_hdr *hdr;
+       size_t len;
+
+       len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) +
+               payload_len;
+       buf = wpabuf_alloc(len);
+       if (buf == NULL)
+               return NULL;
+
+       hdr = wpabuf_put(buf, sizeof(*hdr));
+       hdr->code = code;
+       hdr->identifier = identifier;
+       hdr->length = host_to_be16(len);
+
+       if (vendor == EAP_VENDOR_IETF) {
+               wpabuf_put_u8(buf, type);
+       } else {
+               wpabuf_put_u8(buf, EAP_TYPE_EXPANDED);
+               wpabuf_put_be24(buf, vendor);
+               wpabuf_put_be32(buf, type);
+       }
+
+       return buf;
+}
+
+
+/**
+ * eap_update_len - Update EAP header length
+ * @msg: EAP message from eap_msg_alloc
+ *
+ * This function updates the length field in the EAP header to match with the
+ * current length for the buffer. This allows eap_msg_alloc() to be used to
+ * allocate a larger buffer than the exact message length (e.g., if exact
+ * message length is not yet known).
+ */
+void eap_update_len(struct wpabuf *msg)
+{
+       struct eap_hdr *hdr;
+       hdr = wpabuf_mhead(msg);
+       if (wpabuf_len(msg) < sizeof(*hdr))
+               return;
+       hdr->length = host_to_be16(wpabuf_len(msg));
+}
+
+
+/**
+ * eap_get_id - Get EAP Identifier from wpabuf
+ * @msg: Buffer starting with an EAP header
+ * Returns: The Identifier field from the EAP header
+ */
+u8 eap_get_id(const struct wpabuf *msg)
+{
+       const struct eap_hdr *eap;
+
+       if (wpabuf_len(msg) < sizeof(*eap))
+               return 0;
+
+       eap = wpabuf_head(msg);
+       return eap->identifier;
+}
+
+
+/**
+ * eap_get_id - Get EAP Type from wpabuf
+ * @msg: Buffer starting with an EAP header
+ * Returns: The EAP Type after the EAP header
+ */
+EapType eap_get_type(const struct wpabuf *msg)
+{
+       if (wpabuf_len(msg) < sizeof(struct eap_hdr) + 1)
+               return EAP_TYPE_NONE;
+
+       return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)];
+}
diff --git a/components/wpa_supplicant/src/wpa2/eap_peer/eap_mschapv2.c b/components/wpa_supplicant/src/wpa2/eap_peer/eap_mschapv2.c
new file mode 100644 (file)
index 0000000..89d7b8f
--- /dev/null
@@ -0,0 +1,671 @@
+/*
+ * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt)
+ * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+
+#ifdef EAP_MSCHAPv2
+
+#include "wpa/wpa.h"
+#include "wpa/includes.h"
+#include "wpa/common.h"
+#include "crypto/random.h"
+#include "crypto/ms_funcs.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/eap_peer/eap_i.h"
+#include "wpa2/eap_peer/eap_defs.h"
+#include "wpa2/eap_peer/eap_tls_common.h"
+#include "wpa2/eap_peer/eap_config.h"
+#include "wpa2/eap_peer/mschapv2.h"
+#include "wpa2/eap_peer/eap_methods.h"
+
+#define MSCHAPV2_OP_CHALLENGE          1
+#define MSCHAPV2_OP_RESPONSE           2
+#define MSCHAPV2_OP_SUCCESS            3
+#define MSCHAPV2_OP_FAILURE            4
+#define MSCHAPV2_OP_CHANGE_PASSWORD    7
+
+#define PASSWD_CHANGE_CHAL_LEN         16
+#define MSCHAPV2_KEY_LEN               16
+
+#define ERROR_RESTRICTED_LOGON_HOURS   646
+#define ERROR_ACCT_DISABLED            647
+#define ERROR_PASSWD_EXPIRED           648
+#define ERROR_NO_DIALIN_PERMISSION     649
+#define ERROR_AUTHENTICATION_FAILURE   691
+#define ERROR_CHANGING_PASSWORD                709
+
+struct eap_mschapv2_hdr {
+       u8 op_code;
+       u8 mschapv2_id;
+       u8 ms_length[2];
+} __packed;
+
+struct ms_response {
+       u8 peer_challenge[MSCHAPV2_CHAL_LEN];
+       u8 reserved[8];
+       u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
+       u8 flags;
+} __packed;
+
+struct ms_change_password {
+       u8 encr_password[516];
+       u8 encr_hash[16];
+       u8 peer_challenge[MSCHAPV2_CHAL_LEN];
+       u8 reserved[8];
+       u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
+       u8 flags[2];
+} __packed;
+
+struct eap_mschapv2_data {
+       u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN];
+       int auth_response_valid;
+
+       int prev_error;
+       u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN];
+       int passwd_change_challenge_valid;
+       int passwd_change_version;
+
+       u8 *peer_challenge;
+       u8 *auth_challenge;
+
+       int phase2;
+       u8 master_key[MSCHAPV2_MASTER_KEY_LEN];
+       int master_key_valid;
+       int success;
+
+       struct wpabuf *prev_challenge;
+};
+
+static void 
+eap_mschapv2_deinit(struct eap_sm *sm, void *priv)
+{
+       struct eap_mschapv2_data *data = priv;
+
+       os_free(data->peer_challenge);
+       os_free(data->auth_challenge);
+       wpabuf_free(data->prev_challenge);
+       os_free(data);
+}
+
+static void * 
+eap_mschapv2_init(struct eap_sm *sm)
+{
+       struct eap_mschapv2_data *data;
+       data = (struct eap_mschapv2_data *)os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+
+       data->phase2 = sm->init_phase2;
+
+       return data;
+}
+
+static struct wpabuf * 
+eap_mschapv2_challenge_reply(
+       struct eap_sm *sm, struct eap_mschapv2_data *data,
+       u8 id, u8 mschapv2_id, const u8 *auth_challenge)
+{
+       struct wpabuf *resp;
+       struct eap_mschapv2_hdr *ms;
+       u8 *peer_challenge;
+       int ms_len;
+       struct ms_response *r;
+       size_t identity_len, password_len;
+       const u8 *identity, *password;
+       int pwhash;
+
+       wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generate Challenge Response\n");
+
+       identity = eap_get_config_identity(sm, &identity_len);
+       password = eap_get_config_password2(sm, &password_len, &pwhash);
+       if (identity == NULL || password == NULL)
+               return NULL;
+
+       ms_len = sizeof(*ms) + 1 + sizeof(*r) + identity_len;
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
+                            ms_len, EAP_CODE_RESPONSE, id);
+       if (resp == NULL)
+               return NULL;
+
+       ms = wpabuf_put(resp, sizeof(*ms));
+       ms->op_code = MSCHAPV2_OP_RESPONSE;
+       ms->mschapv2_id = mschapv2_id;
+       if (data->prev_error)
+               ms->mschapv2_id++;
+       WPA_PUT_BE16(ms->ms_length, ms_len);
+       wpabuf_put_u8(resp, sizeof(*r));
+
+       /* Response */
+       r = wpabuf_put(resp, sizeof(*r));
+       peer_challenge = r->peer_challenge;
+       if (data->peer_challenge) {
+               peer_challenge = data->peer_challenge;
+       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)
+               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)) {
+               wpabuf_free(resp);
+               return NULL;
+       }
+       data->auth_response_valid = 1;
+       data->master_key_valid = 1;
+
+       r->flags = 0;
+
+       wpabuf_put_data(resp, identity, identity_len);
+       return resp;
+}
+
+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)
+{
+       size_t len, challenge_len;
+       const u8 *pos, *challenge;
+
+       if (eap_get_config_identity(sm, &len) == NULL ||
+           eap_get_config_password(sm, &len) == NULL)
+               return NULL;
+
+       if (req_len < sizeof(*req) + 1) {
+               ret->ignore = true;
+               return NULL;
+       }
+       pos = (const u8 *)(req + 1);
+       challenge_len = *pos++;
+       len = req_len - sizeof(*req) - 1;
+       if (challenge_len != MSCHAPV2_CHAL_LEN) {
+               ret->ignore = true;
+               return NULL;
+       }
+
+       if (len < challenge_len) {
+               ret->ignore = true;
+               return NULL;
+       }
+
+       if (data->passwd_change_challenge_valid)
+               challenge = data->passwd_change_challenge;
+       else
+               challenge = pos;
+       pos += challenge_len;
+       len -= challenge_len;
+
+       ret->ignore = false;
+       ret->methodState = METHOD_MAY_CONT;
+       ret->decision = DECISION_FAIL;
+       ret->allowNotifications = true;
+
+       return eap_mschapv2_challenge_reply(sm, data, id, req->mschapv2_id,
+                                           challenge);
+}
+
+static void 
+eap_mschapv2_password_changed(struct eap_sm *sm,
+                             struct eap_mschapv2_data *data)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+       if (config && config->new_password) {
+               data->prev_error = 0;
+               os_free(config->password);
+               if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
+               } else if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) {
+                       config->password = os_malloc(16);
+                       config->password_len = 16;
+                       if (config->password) {
+                               nt_password_hash(config->new_password,
+                                                config->new_password_len,
+                                                config->password);
+                       }
+                       os_free(config->new_password);
+               } else {
+                       config->password = config->new_password;
+                       config->password_len = config->new_password_len;
+               }
+               config->new_password = NULL;
+               config->new_password_len = 0;
+       }
+}
+
+static struct wpabuf *
+eap_mschapv2_success(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 *pos;
+       size_t len;
+
+       len = req_len - sizeof(*req);
+       pos = (const u8 *)(req + 1);
+       if (!data->auth_response_valid ||
+           mschapv2_verify_auth_response(data->auth_response, pos, len)) {
+               ret->methodState = METHOD_NONE;
+               ret->decision = DECISION_FAIL;
+               return NULL;
+       }
+       pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
+       len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
+       while (len > 0 && *pos == ' ') {
+               pos++;
+               len--;
+       }
+
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
+                            EAP_CODE_RESPONSE, id);
+       if (resp == NULL) {
+               ret->ignore = true;
+               return NULL;
+       }
+
+       wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS);
+       ret->methodState = METHOD_DONE;
+       ret->decision = DECISION_UNCOND_SUCC;
+       ret->allowNotifications = false;
+       data->success = 1;
+
+       if (data->prev_error == ERROR_PASSWD_EXPIRED)
+               eap_mschapv2_password_changed(sm, data);
+
+       return resp;
+}
+
+static int 
+eap_mschapv2_failure_txt(struct eap_sm *sm,
+                        struct eap_mschapv2_data *data, char *txt)
+{
+       char *pos; 
+       //char *msg = "";
+       int retry = 1;
+       struct eap_peer_config *config = eap_get_config(sm);
+
+       pos = txt;
+
+       if (pos && os_strncmp(pos, "E=", 2) == 0) {
+               pos += 2;
+               data->prev_error = atoi(pos);
+               pos = (char *)os_strchr(pos, ' ');
+               if (pos)
+                       pos++;
+       }
+
+       if (pos && os_strncmp(pos, "R=", 2) == 0) {
+               pos += 2;
+               retry = atoi(pos);
+               pos = (char *)os_strchr(pos, ' ');
+               if (pos)
+                       pos++;
+       }
+
+       if (pos && os_strncmp(pos, "C=", 2) == 0) {
+               int hex_len;
+               pos += 2;
+               hex_len = (char *)os_strchr(pos, ' ') - (char *)pos;
+               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");
+                       } else {
+                               data->passwd_change_challenge_valid = 1;
+                       }
+               } else {
+                       wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: required challenge field "
+                                 "was not present in failure message\n");
+               }
+       }
+
+       if (pos && os_strncmp(pos, "V=", 2) == 0) {
+               pos += 2;
+               data->passwd_change_version = atoi(pos);
+               pos = (char *)os_strchr(pos, ' ');
+               if (pos)
+                       pos++;
+       }
+
+       if (pos && os_strncmp(pos, "M=", 2) == 0) {
+               pos += 2;
+               //msg = pos;
+       }
+       #if 0
+       wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error %d)",
+                 msg, retry == 1? "" : "not ", data->prev_error);
+       #endif
+       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");
+                       //eap_sm_request_new_password(sm);
+               }
+       } else if (retry == 1 && config) {
+               if (!config->mschapv2_retry)
+                       //eap_sm_request_identity(sm);
+               //eap_sm_request_password(sm);
+               config->mschapv2_retry = 1;
+       } else if (config) {
+               config->mschapv2_retry = 0;
+       }
+
+       return retry == 1;
+}
+
+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)
+{
+       struct wpabuf *resp;
+       int ms_len;
+       const u8 *username, *password, *new_password;
+       size_t username_len, password_len, new_password_len;
+       struct eap_mschapv2_hdr *ms;
+       struct ms_change_password *cp;
+       u8 password_hash[16], password_hash_hash[16];
+       int pwhash;
+
+       username = eap_get_config_identity(sm, &username_len);
+       password = eap_get_config_password2(sm, &password_len, &pwhash);
+       new_password = eap_get_config_new_password(sm, &new_password_len);
+       if (username == NULL || password == NULL || new_password == NULL)
+               return NULL;
+
+       username = mschapv2_remove_domain(username, &username_len);
+
+       ret->ignore = false;
+       ret->methodState = METHOD_MAY_CONT;
+       ret->decision = DECISION_COND_SUCC;
+       ret->allowNotifications = TRUE;
+
+       ms_len = sizeof(*ms) + sizeof(*cp);
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
+                            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));
+
+       if (pwhash) {
+               if (encrypt_pw_block_with_password_hash(
+                       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))
+                       goto fail;
+       }
+
+       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);
+       } else {
+               old_nt_password_hash_encrypted_with_new_nt_password_hash(
+                               new_password, new_password_len,
+                               password, password_len, cp->encr_hash);
+       }
+
+       if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
+               goto fail;
+
+       os_memset(cp->reserved, 0, 8);
+
+       generate_nt_response(data->passwd_change_challenge, cp->peer_challenge,
+                            username, username_len, new_password,
+                            new_password_len, cp->nt_response);
+
+       generate_authenticator_response(new_password, new_password_len,
+                                       cp->peer_challenge,
+                                       data->passwd_change_challenge,
+                                       username, username_len,
+                                       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);
+       data->master_key_valid = 1;
+
+       os_memset(cp->flags, 0, 2);
+
+       return resp;
+
+fail:
+       wpabuf_free(resp);
+       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)
+{
+       struct wpabuf *resp;
+       const u8 *msdata = (const u8 *)(req + 1);
+       char *buf;
+       size_t len = req_len - sizeof(*req);
+       int retry = 0;
+
+       buf = (char *)dup_binstr(msdata, len);
+       if (buf) {
+               retry = eap_mschapv2_failure_txt(sm, data, buf);
+               os_free(buf);
+       }
+
+       ret->ignore = false;
+       ret->methodState = METHOD_DONE;
+       ret->decision = DECISION_FAIL;
+       ret->allowNotifications = false;
+
+       if (data->prev_error == ERROR_PASSWD_EXPIRED &&
+           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);
+               //if (config && config->pending_req_new_password)
+               //      return NULL;
+       } else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
+               return NULL;
+       }
+
+       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);
+       return resp;
+}
+
+static int
+eap_mschapv2_check_config(struct eap_sm *sm)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+
+       if (config == NULL)
+               return -1;
+
+       if (config->identity == NULL ||
+           config->identity_len == 0) {
+               wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: idetity not configured\n");
+               return -1;
+       }
+
+       if (config->password == NULL ||
+           config->password_len == 0) {
+               wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Password not configured\n");
+               return -1;
+       }
+
+       return 0; 
+}
+
+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;
+
+       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);
+               return 0;
+       }
+       wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Invalid header len=%lu ms_len=%lu\n",
+                 (unsigned long)len, (unsigned long)ms_len);
+
+       return -1;
+}
+
+static void 
+eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data,
+                           const struct wpabuf *reqData)
+{
+       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)
+{
+       u8 id;
+       size_t len;
+       const u8 *pos;
+       int using_prev_challenge = 0;
+       const struct eap_mschapv2_hdr *ms;
+       struct eap_mschapv2_data *data = priv;
+       struct eap_peer_config *config = eap_get_config(sm);
+
+       if (eap_mschapv2_check_config(sm)) {
+               ret->ignore = true;
+               return NULL;
+       }
+
+       if (config->mschapv2_retry && data->prev_challenge &&
+           data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
+               reqData = data->prev_challenge;
+               using_prev_challenge = 1;
+               config->mschapv2_retry = 0;
+       }
+
+       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;
+       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);
+       switch (ms->op_code) {
+       case MSCHAPV2_OP_CHALLENGE:
+               if (!using_prev_challenge)
+                       eap_mschapv2_copy_challenge(data, reqData);
+               return eap_mschapv2_challenge(sm, data, ret, ms, len, id);
+       case MSCHAPV2_OP_SUCCESS:
+               return eap_mschapv2_success(sm, data, ret, ms, len, id);
+       case MSCHAPV2_OP_FAILURE:
+               return eap_mschapv2_failure(sm, data, ret, ms, len, id);
+       default:
+               wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Unknow op code %d -ignored\n",
+                         ms->op_code);
+               return NULL;
+       }
+}
+
+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)
+{
+       struct eap_mschapv2_data *data = priv;
+       u8 *key;
+       int key_len;
+
+       if (!data->master_key_valid || !data->success)
+               return NULL;
+
+       key_len = 2 * MSCHAPV2_KEY_LEN;
+
+       key = os_malloc(key_len);
+
+       /* 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);
+       get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
+                               MSCHAPV2_KEY_LEN, 0, 0);
+
+       *len = key_len;
+       return key;
+}
+
+int 
+eap_peer_mschapv2_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
+                                   "MSCHAPV2");
+
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_mschapv2_init;
+       eap->deinit = eap_mschapv2_deinit;
+       eap->process = eap_mschapv2_process;
+       eap->isKeyAvailable = eap_mschapv2_isKeyAvailable;
+       eap->getKey = eap_mschapv2_getKey;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
+
+#endif /* EAP_MSCHAPv2 */
diff --git a/components/wpa_supplicant/src/wpa2/eap_peer/eap_peap.c b/components/wpa_supplicant/src/wpa2/eap_peer/eap_peap.c
new file mode 100644 (file)
index 0000000..42c0685
--- /dev/null
@@ -0,0 +1,1357 @@
+/*\r
+ * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt)\r
+ * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>\r
+ *\r
+ * This software may be distributed under the terms of the BSD license.\r
+ * See README for more details.\r
+ */\r
+#ifdef EAP_PEAP\r
+\r
+#include "wpa/includes.h"\r
+\r
+#include "wpa/common.h"\r
+#include "crypto/sha1.h"\r
+#include "wpa2/tls/tls.h"\r
+#include "wpa2/eap_peer/eap_tlv_common.h"\r
+#include "wpa2/eap_peer/eap_peap_common.h"\r
+#include "wpa2/eap_peer/eap_i.h"\r
+#include "wpa2/eap_peer/eap_tls_common.h"\r
+#include "wpa2/eap_peer/eap_config.h"\r
+#include "wpa2/eap_peer/eap_methods.h"\r
+//#include "tncc.h"\r
+\r
+/* Maximum supported PEAP version\r
+ * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt\r
+ * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt\r
+ * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt\r
+ */\r
+#define EAP_PEAP_VERSION 1\r
+\r
+\r
+static void eap_peap_deinit(struct eap_sm *sm, void *priv);\r
+\r
+\r
+struct eap_peap_data {\r
+       struct eap_ssl_data ssl;\r
+\r
+       int peap_version, force_peap_version, force_new_label;\r
+\r
+       const struct eap_method *phase2_method;\r
+       void *phase2_priv;\r
+       int phase2_success;\r
+       int phase2_eap_success;\r
+       int phase2_eap_started;\r
+\r
+       struct eap_method_type phase2_type;\r
+       struct eap_method_type *phase2_types;\r
+       size_t num_phase2_types;\r
+\r
+       int peap_outer_success; /* 0 = PEAP terminated on Phase 2 inner\r
+                                * EAP-Success\r
+                                * 1 = reply with tunneled EAP-Success to inner\r
+                                * EAP-Success and expect AS to send outer\r
+                                * (unencrypted) EAP-Success after this\r
+                                * 2 = reply with PEAP/TLS ACK to inner\r
+                                * EAP-Success and expect AS to send outer\r
+                                * (unencrypted) EAP-Success after this */\r
+       int resuming; /* starting a resumed session */\r
+       int reauth; /* reauthentication */\r
+       u8 *key_data;\r
+       u8 *session_id;\r
+       size_t id_len;\r
+\r
+       struct wpabuf *pending_phase2_req;\r
+       enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding;\r
+       int crypto_binding_used;\r
+       u8 binding_nonce[32];\r
+       u8 ipmk[40];\r
+       u8 cmk[20];\r
+       int soh; /* Whether IF-TNCCS-SOH (Statement of Health; Microsoft NAP)\r
+                 * is enabled. */\r
+};\r
+\r
+\r
+static int\r
+eap_peap_parse_phase1(struct eap_peap_data *data,\r
+                     const char *phase1)\r
+{\r
+       const char *pos;\r
+\r
+       pos = os_strstr(phase1, "peapver=");\r
+       if (pos) {\r
+               data->force_peap_version = atoi(pos + 8);\r
+               data->peap_version = data->force_peap_version;\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Forced PEAP version %d",\r
+                          data->force_peap_version);\r
+       }\r
+\r
+       if (os_strstr(phase1, "peaplabel=1")) {\r
+               data->force_new_label = 1;\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for key "\r
+                          "derivation");\r
+       }\r
+\r
+       if (os_strstr(phase1, "peap_outer_success=0")) {\r
+               data->peap_outer_success = 0;\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: terminate authentication on "\r
+                          "tunneled EAP-Success");\r
+       } else if (os_strstr(phase1, "peap_outer_success=1")) {\r
+               data->peap_outer_success = 1;\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: send tunneled EAP-Success "\r
+                          "after receiving tunneled EAP-Success");\r
+       } else if (os_strstr(phase1, "peap_outer_success=2")) {\r
+               data->peap_outer_success = 2;\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: send PEAP/TLS ACK after "\r
+                          "receiving tunneled EAP-Success");\r
+       }\r
+\r
+       if (os_strstr(phase1, "crypto_binding=0")) {\r
+               data->crypto_binding = NO_BINDING;\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Do not use cryptobinding");\r
+       } else if (os_strstr(phase1, "crypto_binding=1")) {\r
+               data->crypto_binding = OPTIONAL_BINDING;\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Optional cryptobinding");\r
+       } else if (os_strstr(phase1, "crypto_binding=2")) {\r
+               data->crypto_binding = REQUIRE_BINDING;\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Require cryptobinding");\r
+       }\r
+\r
+#ifdef EAP_TNC\r
+       if (os_strstr(phase1, "tnc=soh2")) {\r
+               data->soh = 2;\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled");\r
+       } else if (os_strstr(phase1, "tnc=soh1")) {\r
+               data->soh = 1;\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 1 enabled");\r
+       } else if (os_strstr(phase1, "tnc=soh")) {\r
+               data->soh = 2;\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled");\r
+       }\r
+#endif /* EAP_TNC */\r
+\r
+       return 0;\r
+}\r
+\r
+\r
+static void *\r
+eap_peap_init(struct eap_sm *sm)\r
+{\r
+       struct eap_peap_data *data;\r
+       struct eap_peer_config *config = eap_get_config(sm);\r
+\r
+       data = (struct eap_peap_data *)os_zalloc(sizeof(*data));\r
+       if (data == NULL)\r
+               return NULL;\r
+       sm->peap_done = FALSE;\r
+       data->peap_version = EAP_PEAP_VERSION;\r
+       data->force_peap_version = -1;\r
+       data->peap_outer_success = 2;\r
+       data->crypto_binding = OPTIONAL_BINDING;\r
+\r
+       if (config && config->phase1 &&\r
+           eap_peap_parse_phase1(data, config->phase1) < 0) {\r
+               eap_peap_deinit(sm, data);\r
+               return NULL;\r
+       }\r
+\r
+       if (eap_peer_select_phase2_methods(config, "auth=",\r
+                                          &data->phase2_types,\r
+                                          &data->num_phase2_types) < 0) {\r
+               eap_peap_deinit(sm, data);\r
+               return NULL;\r
+       }\r
+\r
+       data->phase2_type.vendor = EAP_VENDOR_IETF;\r
+       data->phase2_type.method = EAP_TYPE_NONE;\r
+\r
+       if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_PEAP)) {\r
+               wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL.");\r
+               eap_peap_deinit(sm, data);\r
+               return NULL;\r
+       }\r
+\r
+       return data;\r
+}\r
+\r
+\r
+static void\r
+eap_peap_deinit(struct eap_sm *sm, void *priv)\r
+{\r
+       struct eap_peap_data *data = priv;\r
+       if (data == NULL)\r
+               return;\r
+       if (data->phase2_priv && data->phase2_method)\r
+               data->phase2_method->deinit(sm, data->phase2_priv);\r
+       os_free(data->phase2_types);\r
+       eap_peer_tls_ssl_deinit(sm, &data->ssl);\r
+       os_free(data->key_data);\r
+       os_free(data->session_id);\r
+       wpabuf_free(data->pending_phase2_req);\r
+       os_free(data);\r
+}\r
+\r
+\r
+/**\r
+ * eap_tlv_build_nak - Build EAP-TLV NAK message\r
+ * @id: EAP identifier for the header\r
+ * @nak_type: TLV type (EAP_TLV_*)\r
+ * Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure\r
+ *\r
+ * This function builds an EAP-TLV NAK message. The caller is responsible for\r
+ * freeing the returned buffer.\r
+ */\r
+static struct wpabuf *\r
+eap_tlv_build_nak(int id, u16 nak_type)\r
+{\r
+       struct wpabuf *msg;\r
+\r
+       msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, 10,\r
+                           EAP_CODE_RESPONSE, id);\r
+       if (msg == NULL)\r
+               return NULL;\r
+\r
+       wpabuf_put_u8(msg, 0x80); /* Mandatory */\r
+       wpabuf_put_u8(msg, EAP_TLV_NAK_TLV);\r
+       wpabuf_put_be16(msg, 6); /* Length */\r
+       wpabuf_put_be32(msg, 0); /* Vendor-Id */\r
+       wpabuf_put_be16(msg, nak_type); /* NAK-Type */\r
+\r
+       return msg;\r
+}\r
+\r
+\r
+static int\r
+eap_peap_get_isk(struct eap_sm *sm, struct eap_peap_data *data,\r
+                u8 *isk, size_t isk_len)\r
+{\r
+       u8 *key;\r
+       size_t key_len;\r
+\r
+       os_memset(isk, 0, isk_len);\r
+       if (data->phase2_method == NULL || data->phase2_priv == NULL ||\r
+           data->phase2_method->isKeyAvailable == NULL ||\r
+           data->phase2_method->getKey == NULL)\r
+               return 0;\r
+\r
+       if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) ||\r
+           (key = data->phase2_method->getKey(sm, data->phase2_priv,\r
+                                              &key_len)) == NULL) {\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not get key material "\r
+                          "from Phase 2");\r
+               return -1;\r
+       }\r
+\r
+       if (key_len > isk_len)\r
+               key_len = isk_len;\r
+       os_memcpy(isk, key, key_len);\r
+       os_free(key);\r
+\r
+       return 0;\r
+}\r
+\r
+\r
+static int\r
+eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)\r
+{\r
+       u8 *tk;\r
+       u8 isk[32], imck[60];\r
+\r
+       /*\r
+        * Tunnel key (TK) is the first 60 octets of the key generated by\r
+        * phase 1 of PEAP (based on TLS).\r
+        */\r
+       tk = data->key_data;\r
+       if (tk == NULL)\r
+               return -1;\r
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60);\r
+\r
+       if (data->reauth &&\r
+           tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) {\r
+               /* Fast-connect: IPMK|CMK = TK */\r
+               os_memcpy(data->ipmk, tk, 40);\r
+               wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK",\r
+                               data->ipmk, 40);\r
+               os_memcpy(data->cmk, tk + 40, 20);\r
+               wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK from TK",\r
+                               data->cmk, 20);\r
+               return 0;\r
+       }\r
+\r
+       if (eap_peap_get_isk(sm, data, isk, sizeof(isk)) < 0)\r
+               return -1;\r
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk));\r
+\r
+       /*\r
+        * IPMK Seed = "Inner Methods Compound Keys" | ISK\r
+        * TempKey = First 40 octets of TK\r
+        * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60)\r
+        * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space\r
+        * in the end of the label just before ISK; is that just a typo?)\r
+        */\r
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40);\r
+       if (peap_prfplus(data->peap_version, tk, 40,\r
+                        "Inner Methods Compound Keys",\r
+                        isk, sizeof(isk), imck, sizeof(imck)) < 0)\r
+               return -1;\r
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)",\r
+                       imck, sizeof(imck));\r
+\r
+       os_memcpy(data->ipmk, imck, 40);\r
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40);\r
+       os_memcpy(data->cmk, imck + 40, 20);\r
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20);\r
+\r
+       return 0;\r
+}\r
+\r
+\r
+static int\r
+eap_tlv_add_cryptobinding(struct eap_sm *sm,\r
+                         struct eap_peap_data *data,\r
+                         struct wpabuf *buf)\r
+{\r
+       u8 *mac;\r
+       u8 eap_type = EAP_TYPE_PEAP;\r
+       const u8 *addr[2];\r
+       size_t len[2];\r
+       u16 tlv_type;\r
+\r
+       /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */\r
+       addr[0] = wpabuf_put(buf, 0);\r
+       len[0] = 60;\r
+       addr[1] = &eap_type;\r
+       len[1] = 1;\r
+\r
+       tlv_type = EAP_TLV_CRYPTO_BINDING_TLV;\r
+       if (data->peap_version >= 2)\r
+               tlv_type |= EAP_TLV_TYPE_MANDATORY;\r
+       wpabuf_put_be16(buf, tlv_type);\r
+       wpabuf_put_be16(buf, 56);\r
+\r
+       wpabuf_put_u8(buf, 0); /* Reserved */\r
+       wpabuf_put_u8(buf, data->peap_version); /* Version */\r
+       wpabuf_put_u8(buf, data->peap_version); /* RecvVersion */\r
+       wpabuf_put_u8(buf, 1); /* SubType: 0 = Request, 1 = Response */\r
+       wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */\r
+       mac = wpabuf_put(buf, 20); /* Compound_MAC */\r
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", data->cmk, 20);\r
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1",\r
+                   addr[0], len[0]);\r
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2",\r
+                   addr[1], len[1]);\r
+       hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac);\r
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN);\r
+       data->crypto_binding_used = 1;\r
+\r
+       return 0;\r
+}\r
+\r
+\r
+/**\r
+ * eap_tlv_build_result - Build EAP-TLV Result message\r
+ * @id: EAP identifier for the header\r
+ * @status: Status (EAP_TLV_RESULT_SUCCESS or EAP_TLV_RESULT_FAILURE)\r
+ * Returns: Buffer to the allocated EAP-TLV Result message or %NULL on failure\r
+ *\r
+ * This function builds an EAP-TLV Result message. The caller is responsible\r
+ * for freeing the returned buffer.\r
+ */\r
+static struct wpabuf *\r
+eap_tlv_build_result(struct eap_sm *sm,\r
+                    struct eap_peap_data *data,\r
+                    int crypto_tlv_used,\r
+                    int id, u16 status)\r
+{\r
+       struct wpabuf *msg;\r
+       size_t len;\r
+\r
+       if (data->crypto_binding == NO_BINDING)\r
+               crypto_tlv_used = 0;\r
+\r
+       len = 6;\r
+       if (crypto_tlv_used)\r
+               len += 60; /* Cryptobinding TLV */\r
+       msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len,\r
+                           EAP_CODE_RESPONSE, id);\r
+       if (msg == NULL)\r
+               return NULL;\r
+\r
+       wpabuf_put_u8(msg, 0x80); /* Mandatory */\r
+       wpabuf_put_u8(msg, EAP_TLV_RESULT_TLV);\r
+       wpabuf_put_be16(msg, 2); /* Length */\r
+       wpabuf_put_be16(msg, status); /* Status */\r
+\r
+       if (crypto_tlv_used && eap_tlv_add_cryptobinding(sm, data, msg)) {\r
+               wpabuf_free(msg);\r
+               return NULL;\r
+       }\r
+\r
+       return msg;\r
+}\r
+\r
+\r
+static int\r
+eap_tlv_validate_cryptobinding(struct eap_sm *sm,\r
+                              struct eap_peap_data *data,\r
+                              const u8 *crypto_tlv,\r
+                              size_t crypto_tlv_len)\r
+{\r
+       u8 buf[61], mac[SHA1_MAC_LEN];\r
+       const u8 *pos;\r
+\r
+       if (eap_peap_derive_cmk(sm, data) < 0) {\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not derive CMK");\r
+               return -1;\r
+       }\r
+\r
+       if (crypto_tlv_len != 4 + 56) {\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV "\r
+                          "length %d", (int) crypto_tlv_len);\r
+               return -1;\r
+       }\r
+\r
+       pos = crypto_tlv;\r
+       pos += 4; /* TLV header */\r
+       if (pos[1] != data->peap_version) {\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version "\r
+                          "mismatch (was %d; expected %d)",\r
+                          pos[1], data->peap_version);\r
+               return -1;\r
+       }\r
+\r
+       if (pos[3] != 0) {\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV "\r
+                          "SubType %d", pos[3]);\r
+               return -1;\r
+       }\r
+       pos += 4;\r
+       os_memcpy(data->binding_nonce, pos, 32);\r
+       pos += 32; /* Nonce */\r
+\r
+       /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */\r
+       os_memcpy(buf, crypto_tlv, 60);\r
+       os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */\r
+       buf[60] = EAP_TYPE_PEAP;\r
+       wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Compound_MAC data",\r
+                   buf, sizeof(buf));\r
+       hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac);\r
+\r
+       if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) {\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in "\r
+                          "cryptobinding TLV");\r
+               wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received MAC",\r
+                           pos, SHA1_MAC_LEN);\r
+               wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Expected MAC",\r
+                           mac, SHA1_MAC_LEN);\r
+               return -1;\r
+       }\r
+\r
+       wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received");\r
+\r
+       return 0;\r
+}\r
+\r
+\r
+/**\r
+ * eap_tlv_process - Process a received EAP-TLV message and generate a response\r
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()\r
+ * @ret: Return values from EAP request validation and processing\r
+ * @req: EAP-TLV request to be processed. The caller must have validated that\r
+ * the buffer is large enough to contain full request (hdr->length bytes) and\r
+ * that the EAP type is EAP_TYPE_TLV.\r
+ * @resp: Buffer to return a pointer to the allocated response message. This\r
+ * field should be initialized to %NULL before the call. The value will be\r
+ * updated if a response message is generated. The caller is responsible for\r
+ * freeing the allocated message.\r
+ * @force_failure: Force negotiation to fail\r
+ * Returns: 0 on success, -1 on failure\r
+ */\r
+static int\r
+eap_tlv_process(struct eap_sm *sm, struct eap_peap_data *data,\r
+               struct eap_method_ret *ret,\r
+               const struct wpabuf *req, struct wpabuf **resp,\r
+               int force_failure)\r
+{\r
+       size_t left, tlv_len;\r
+       const u8 *pos;\r
+       const u8 *result_tlv = NULL, *crypto_tlv = NULL;\r
+       size_t result_tlv_len = 0, crypto_tlv_len = 0;\r
+       int tlv_type, mandatory;\r
+\r
+       /* Parse TLVs */\r
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, req, &left);\r
+       if (pos == NULL)\r
+               return -1;\r
+       wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left);\r
+       while (left >= 4) {\r
+               mandatory = !!(pos[0] & 0x80);\r
+               tlv_type = WPA_GET_BE16(pos) & 0x3fff;\r
+               pos += 2;\r
+               tlv_len = WPA_GET_BE16(pos);\r
+               pos += 2;\r
+               left -= 4;\r
+               if (tlv_len > left) {\r
+                       wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun "\r
+                                  "(tlv_len=%lu left=%lu)",\r
+                                  (unsigned long) tlv_len,\r
+                                  (unsigned long) left);\r
+                       return -1;\r
+               }\r
+               switch (tlv_type) {\r
+               case EAP_TLV_RESULT_TLV:\r
+                       result_tlv = pos;\r
+                       result_tlv_len = tlv_len;\r
+                       break;\r
+               case EAP_TLV_CRYPTO_BINDING_TLV:\r
+                       crypto_tlv = pos;\r
+                       crypto_tlv_len = tlv_len;\r
+                       break;\r
+               default:\r
+                       wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type "\r
+                                  "%d%s", tlv_type,\r
+                                  mandatory ? " (mandatory)" : "");\r
+                       if (mandatory) {\r
+                               /* NAK TLV and ignore all TLVs in this packet.\r
+                                */\r
+                               *resp = eap_tlv_build_nak(eap_get_id(req),\r
+                                                         tlv_type);\r
+                               return *resp == NULL ? -1 : 0;\r
+                       }\r
+                       /* Ignore this TLV, but process other TLVs */\r
+                       break;\r
+               }\r
+\r
+               pos += tlv_len;\r
+               left -= tlv_len;\r
+       }\r
+       if (left) {\r
+               wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in "\r
+                          "Request (left=%lu)", (unsigned long) left);\r
+               return -1;\r
+       }\r
+\r
+       /* Process supported TLVs */\r
+       if (crypto_tlv && data->crypto_binding != NO_BINDING) {\r
+               wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV",\r
+                           crypto_tlv, crypto_tlv_len);\r
+               if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4,\r
+                                                  crypto_tlv_len + 4) < 0) {\r
+                       if (result_tlv == NULL)\r
+                               return -1;\r
+                       force_failure = 1;\r
+                       crypto_tlv = NULL; /* do not include Cryptobinding TLV\r
+                                           * in response, if the received\r
+                                           * cryptobinding was invalid. */\r
+               }\r
+       } else if (!crypto_tlv && data->crypto_binding == REQUIRE_BINDING) {\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV");\r
+               return -1;\r
+       }\r
+\r
+       if (result_tlv) {\r
+               int status, resp_status;\r
+               wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV",\r
+                           result_tlv, result_tlv_len);\r
+               if (result_tlv_len < 2) {\r
+                       wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV "\r
+                                  "(len=%lu)",\r
+                                  (unsigned long) result_tlv_len);\r
+                       return -1;\r
+               }\r
+               status = WPA_GET_BE16(result_tlv);\r
+               if (status == EAP_TLV_RESULT_SUCCESS) {\r
+                       wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success "\r
+                                  "- EAP-TLV/Phase2 Completed");\r
+                       if (force_failure) {\r
+                               wpa_printf(MSG_INFO, "EAP-TLV: Earlier failure"\r
+                                          " - force failed Phase 2");\r
+                               resp_status = EAP_TLV_RESULT_FAILURE;\r
+                               ret->decision = DECISION_FAIL;\r
+                       } else {\r
+                               resp_status = EAP_TLV_RESULT_SUCCESS;\r
+                               ret->decision = DECISION_UNCOND_SUCC;\r
+                       }\r
+               } else if (status == EAP_TLV_RESULT_FAILURE) {\r
+                       wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure");\r
+                       resp_status = EAP_TLV_RESULT_FAILURE;\r
+                       ret->decision = DECISION_FAIL;\r
+               } else {\r
+                       wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result "\r
+                                  "Status %d", status);\r
+                       resp_status = EAP_TLV_RESULT_FAILURE;\r
+                       ret->decision = DECISION_FAIL;\r
+               }\r
+               ret->methodState = METHOD_DONE;\r
+\r
+               *resp = eap_tlv_build_result(sm, data, crypto_tlv != NULL,\r
+                                            eap_get_id(req), resp_status);\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+\r
+static struct wpabuf *\r
+eap_peapv2_tlv_eap_payload(struct wpabuf *buf)\r
+{\r
+       struct wpabuf *e;\r
+       struct eap_tlv_hdr *tlv;\r
+\r
+       if (buf == NULL)\r
+               return NULL;\r
+\r
+       /* Encapsulate EAP packet in EAP-Payload TLV */\r
+       wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV");\r
+       e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf));\r
+       if (e == NULL) {\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory "\r
+                          "for TLV encapsulation");\r
+               wpabuf_free(buf);\r
+               return NULL;\r
+       }\r
+       tlv = wpabuf_put(e, sizeof(*tlv));\r
+       tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |\r
+                                    EAP_TLV_EAP_PAYLOAD_TLV);\r
+       tlv->length = host_to_be16(wpabuf_len(buf));\r
+       wpabuf_put_buf(e, buf);\r
+       wpabuf_free(buf);\r
+       return e;\r
+}\r
+\r
+\r
+static int eap_peap_phase2_request(struct eap_sm *sm,\r
+                       struct eap_peap_data *data,\r
+                       struct eap_method_ret *ret,\r
+                       struct wpabuf *req,\r
+                       struct wpabuf **resp)\r
+{\r
+       struct eap_hdr *hdr = wpabuf_mhead(req);\r
+       size_t len = be_to_host16(hdr->length);\r
+       u8 *pos;\r
+       struct eap_method_ret iret;\r
+       //struct eap_peer_config *config = eap_get_config(sm);\r
+\r
+       if (len <= sizeof(struct eap_hdr)) {\r
+               wpa_printf(MSG_INFO, "EAP-PEAP: too short "\r
+                          "Phase 2 request (len=%lu)", (unsigned long) len);\r
+               return -1;\r
+       }\r
+       pos = (u8 *) (hdr + 1);\r
+       wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: type=%d\n", *pos);\r
+       switch (*pos) {\r
+       case EAP_TYPE_IDENTITY:\r
+               *resp = eap_sm_build_identity_resp(sm, hdr->identifier, 1);\r
+               break;\r
+       case EAP_TYPE_TLV:\r
+               os_memset(&iret, 0, sizeof(iret));\r
+               if (eap_tlv_process(sm, data, &iret, req, resp,\r
+                                   data->phase2_eap_started &&\r
+                                   !data->phase2_eap_success)) {\r
+                       ret->methodState = METHOD_DONE;\r
+                       ret->decision = DECISION_FAIL;\r
+                       return -1;\r
+               }\r
+               if (iret.methodState == METHOD_DONE ||\r
+                   iret.methodState == METHOD_MAY_CONT) {\r
+                       ret->methodState = iret.methodState;\r
+                       ret->decision = iret.decision;\r
+                       data->phase2_success = 1;\r
+               }\r
+               break;\r
+       case EAP_TYPE_EXPANDED:\r
+#ifdef EAP_TNC\r
+               if (data->soh) {\r
+                       const u8 *epos;\r
+                       size_t eleft;\r
+\r
+                       epos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21,\r
+                                               req, &eleft);\r
+                       if (epos) {\r
+                               struct wpabuf *buf;\r
+                               wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH EAP Extensions");\r
+                               buf = tncc_process_soh_request(data->soh,\r
+                                                              epos, eleft);\r
+                               if (buf) {\r
+                                       *resp = eap_msg_alloc(\r
+                                               EAP_VENDOR_MICROSOFT, 0x21,\r
+                                               wpabuf_len(buf),\r
+                                               EAP_CODE_RESPONSE,\r
+                                               hdr->identifier);\r
+                                       if (*resp == NULL) {\r
+                                               ret->methodState = METHOD_DONE;\r
+                                               ret->decision = DECISION_FAIL;\r
+                                               return -1;\r
+                                       }\r
+                                       wpabuf_put_buf(*resp, buf);\r
+                                       wpabuf_free(buf);\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+#endif /* EAP_TNC */\r
+               /* fall through */\r
+       default:\r
+               if (data->phase2_type.vendor == EAP_VENDOR_IETF &&\r
+                   data->phase2_type.method == EAP_TYPE_NONE) {\r
+                       size_t i;\r
+                       for (i = 0; i < data->num_phase2_types; i++) {\r
+                               if (data->phase2_types[i].vendor !=\r
+                                   EAP_VENDOR_IETF ||\r
+                                   data->phase2_types[i].method != *pos)\r
+                                       continue;\r
+\r
+                               data->phase2_type.vendor =\r
+                                       data->phase2_types[i].vendor;\r
+                               data->phase2_type.method =\r
+                                       data->phase2_types[i].method;\r
+                               wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected "\r
+                                         "Phase 2 EAP vendor %d method %d\n",\r
+                                         data->phase2_type.vendor,\r
+                                         data->phase2_type.method);\r
+                               break;\r
+                       }\r
+               }\r
+               if (*pos != data->phase2_type.method ||\r
+                   *pos == EAP_TYPE_NONE) {\r
+                       if (eap_peer_tls_phase2_nak(data->phase2_types,\r
+                                                   data->num_phase2_types,\r
+                                                   hdr, resp))\r
+                               return -1;\r
+                       return 0;\r
+               }\r
+\r
+               if (data->phase2_priv == NULL) {\r
+                       data->phase2_method = eap_peer_get_eap_method(\r
+                               data->phase2_type.vendor,\r
+                               data->phase2_type.method);\r
+                       if (data->phase2_method) {\r
+                               sm->init_phase2 = 1;\r
+                               data->phase2_priv =\r
+                                       data->phase2_method->init(sm);\r
+                               sm->init_phase2 = 0;\r
+                       }\r
+               }\r
+               if (data->phase2_priv == NULL || data->phase2_method == NULL) {\r
+                       wpa_printf(MSG_ERROR, "EAP-PEAP: failed to initialize "\r
+                                 "Phase 2 EAP method %d\n", *pos);\r
+                       ret->methodState = METHOD_DONE;\r
+                       ret->decision = DECISION_FAIL;\r
+                       return -1;\r
+               }\r
+               data->phase2_eap_started = 1;\r
+               os_memset(&iret, 0, sizeof(iret));\r
+               *resp = data->phase2_method->process(sm, data->phase2_priv,\r
+                                                    &iret, req);\r
+               if ((iret.methodState == METHOD_DONE ||\r
+                    iret.methodState == METHOD_MAY_CONT) &&\r
+                   (iret.decision == DECISION_UNCOND_SUCC ||\r
+                    iret.decision == DECISION_COND_SUCC)) {\r
+                       data->phase2_eap_success = 1;\r
+                       data->phase2_success = 1;\r
+               }\r
+               break;\r
+       }\r
+\r
+       if (*resp == NULL) {\r
+               wpa_printf(MSG_ERROR, "phase 2 response failure\n");\r
+               wpabuf_free(data->pending_phase2_req);\r
+               data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);\r
+       }\r
+/*     \r
+    if (*resp == NULL &&\r
+           (config->pending_req_identity || config->pending_req_password ||\r
+            config->pending_req_otp || config->pending_req_new_password)) {\r
+               wpabuf_free(data->pending_phase2_req);\r
+               data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);\r
+       }\r
+*/\r
+\r
+       return 0;\r
+}\r
+\r
+\r
+static int\r
+eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data,\r
+                struct eap_method_ret *ret,\r
+                const struct eap_hdr *req,\r
+                const struct wpabuf *in_data,\r
+                struct wpabuf **out_data)\r
+{\r
+       struct wpabuf *in_decrypted = NULL;\r
+       int res, skip_change = 0;\r
+       struct eap_hdr *hdr, *rhdr;\r
+       struct wpabuf *resp = NULL;\r
+       size_t len;\r
+\r
+       wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for"\r
+                 " Phase 2\n", (unsigned long) wpabuf_len(in_data));\r
+\r
+       if (data->pending_phase2_req) {\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - "\r
+                          "skip decryption and use old data");\r
+               /* Clear TLS reassembly state. */\r
+               eap_peer_tls_reset_input(&data->ssl);\r
+               in_decrypted = data->pending_phase2_req;\r
+               data->pending_phase2_req = NULL;\r
+               skip_change = 1;\r
+               goto continue_req;\r
+       }\r
+\r
+       if (wpabuf_len(in_data) == 0 && sm->workaround &&\r
+           data->phase2_success) {\r
+               /*\r
+                * Cisco ACS seems to be using TLS ACK to terminate\r
+                * EAP-PEAPv0/GTC. Try to reply with TLS ACK.\r
+                */\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Received TLS ACK, but "\r
+                          "expected data - acknowledge with TLS ACK since "\r
+                          "Phase 2 has been completed");\r
+               ret->decision = DECISION_COND_SUCC;\r
+               ret->methodState = METHOD_DONE;\r
+               return 1;\r
+       } else if (wpabuf_len(in_data) == 0) {\r
+               /* Received TLS ACK - requesting more fragments */\r
+               return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP,\r
+                                           data->peap_version,\r
+                                           req->identifier, NULL, out_data);\r
+       }\r
+\r
+       res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);\r
+       if (res)\r
+               return res;\r
+\r
+continue_req:\r
+       wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP",\r
+                       in_decrypted);\r
+\r
+       hdr = wpabuf_mhead(in_decrypted);\r
+       if (wpabuf_len(in_decrypted) == 5 && hdr->code == EAP_CODE_REQUEST &&\r
+           be_to_host16(hdr->length) == 5 &&\r
+           eap_get_type(in_decrypted) == EAP_TYPE_IDENTITY) {\r
+               /* At least FreeRADIUS seems to send full EAP header with\r
+                * EAP Request Identity */\r
+               skip_change = 1;\r
+       }\r
+       if (wpabuf_len(in_decrypted) >= 5 && hdr->code == EAP_CODE_REQUEST &&\r
+           eap_get_type(in_decrypted) == EAP_TYPE_TLV) {\r
+               skip_change = 1;\r
+       }\r
+\r
+       if (data->peap_version == 0 && !skip_change) {\r
+               struct eap_hdr *nhdr;\r
+               struct wpabuf *nmsg = wpabuf_alloc(sizeof(struct eap_hdr) +\r
+                                                  wpabuf_len(in_decrypted));\r
+               if (nmsg == NULL) {\r
+                       wpabuf_free(in_decrypted);\r
+                       return 0;\r
+               }\r
+               nhdr = wpabuf_put(nmsg, sizeof(*nhdr));\r
+               wpabuf_put_buf(nmsg, in_decrypted);\r
+               nhdr->code = req->code;\r
+               nhdr->identifier = req->identifier;\r
+               nhdr->length = host_to_be16(sizeof(struct eap_hdr) +\r
+                                           wpabuf_len(in_decrypted));\r
+\r
+               wpabuf_free(in_decrypted);\r
+               in_decrypted = nmsg;\r
+       }\r
+\r
+       if (data->peap_version >= 2) {\r
+               struct eap_tlv_hdr *tlv;\r
+               struct wpabuf *nmsg;\r
+\r
+               if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) {\r
+                       wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 "\r
+                                  "EAP TLV");\r
+                       wpabuf_free(in_decrypted);\r
+                       return 0;\r
+               }\r
+               tlv = wpabuf_mhead(in_decrypted);\r
+               if ((be_to_host16(tlv->tlv_type) & 0x3fff) !=\r
+                   EAP_TLV_EAP_PAYLOAD_TLV) {\r
+                       wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV");\r
+                       wpabuf_free(in_decrypted);\r
+                       return 0;\r
+               }\r
+               if (sizeof(*tlv) + be_to_host16(tlv->length) >\r
+                   wpabuf_len(in_decrypted)) {\r
+                       wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV "\r
+                                  "length");\r
+                       wpabuf_free(in_decrypted);\r
+                       return 0;\r
+               }\r
+               hdr = (struct eap_hdr *) (tlv + 1);\r
+               if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) {\r
+                       wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full "\r
+                                  "EAP packet in EAP TLV");\r
+                       wpabuf_free(in_decrypted);\r
+                       return 0;\r
+               }\r
+\r
+               nmsg = wpabuf_alloc(be_to_host16(hdr->length));\r
+               if (nmsg == NULL) {\r
+                       wpabuf_free(in_decrypted);\r
+                       return 0;\r
+               }\r
+\r
+               wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length));\r
+               wpabuf_free(in_decrypted);\r
+               in_decrypted = nmsg;\r
+       }\r
+\r
+       hdr = wpabuf_mhead(in_decrypted);\r
+       if (wpabuf_len(in_decrypted) < sizeof(*hdr)) {\r
+               wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 "\r
+                          "EAP frame (len=%lu)",\r
+                          (unsigned long) wpabuf_len(in_decrypted));\r
+               wpabuf_free(in_decrypted);\r
+               return 0;\r
+       }\r
+       len = be_to_host16(hdr->length);\r
+       if (len > wpabuf_len(in_decrypted)) {\r
+               wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in "\r
+                          "Phase 2 EAP frame (len=%lu hdr->length=%lu)",\r
+                          (unsigned long) wpabuf_len(in_decrypted),\r
+                          (unsigned long) len);\r
+               wpabuf_free(in_decrypted);\r
+               return 0;\r
+       }\r
+       if (len < wpabuf_len(in_decrypted)) {\r
+               wpa_printf(MSG_INFO, "EAP-PEAP: Odd.. Phase 2 EAP header has "\r
+                          "shorter length than full decrypted data "\r
+                          "(%lu < %lu)",\r
+                          (unsigned long) len,\r
+                          (unsigned long) wpabuf_len(in_decrypted));\r
+       }\r
+       wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d "\r
+                 "identifier=%d length=%lu\n", hdr->code, hdr->identifier,\r
+                 (unsigned long) len);\r
+       switch (hdr->code) {\r
+       case EAP_CODE_REQUEST:\r
+               if (eap_peap_phase2_request(sm, data, ret, in_decrypted,\r
+                                           &resp)) {\r
+                       wpabuf_free(in_decrypted);\r
+                       wpa_printf(MSG_ERROR, "EAP-PEAP: Phase2 Request "\r
+                                 "processing failed\n");\r
+                       return 0;\r
+               }\r
+               break;\r
+       case EAP_CODE_SUCCESS:\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success\n");\r
+               if (data->peap_version == 1) {\r
+                       /* EAP-Success within TLS tunnel is used to indicate\r
+                        * shutdown of the TLS channel. The authentication has\r
+                        * been completed. */\r
+                       if (data->phase2_eap_started &&\r
+                           !data->phase2_eap_success) {\r
+                               wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 "\r
+                                          "Success used to indicate success, "\r
+                                          "but Phase 2 EAP was not yet "\r
+                                          "completed successfully");\r
+                               ret->methodState = METHOD_DONE;\r
+                               ret->decision = DECISION_FAIL;\r
+                               wpabuf_free(in_decrypted);\r
+                               return 0;\r
+                       }\r
+                       wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - "\r
+                                  "EAP-Success within TLS tunnel - "\r
+                                  "authentication completed");\r
+                       ret->decision = DECISION_UNCOND_SUCC;\r
+                       ret->methodState = METHOD_DONE;\r
+                       data->phase2_success = 1;\r
+                       if (data->peap_outer_success == 2) {\r
+                               wpabuf_free(in_decrypted);\r
+                               wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK "\r
+                                          "to finish authentication");\r
+                               return 1;\r
+                       } else if (data->peap_outer_success == 1) {\r
+                               /* Reply with EAP-Success within the TLS\r
+                                * channel to complete the authentication. */\r
+                               resp = wpabuf_alloc(sizeof(struct eap_hdr));\r
+                               if (resp) {\r
+                                       rhdr = wpabuf_put(resp, sizeof(*rhdr));\r
+                                       rhdr->code = EAP_CODE_SUCCESS;\r
+                                       rhdr->identifier = hdr->identifier;\r
+                                       rhdr->length =\r
+                                               host_to_be16(sizeof(*rhdr));\r
+                               }\r
+                       } else {\r
+                               /* No EAP-Success expected for Phase 1 (outer,\r
+                                * unencrypted auth), so force EAP state\r
+                                * machine to SUCCESS state. */\r
+                               sm->peap_done = TRUE;\r
+                       }\r
+               } else {\r
+                       /* FIX: ? */\r
+               }\r
+               break;\r
+       case EAP_CODE_FAILURE:\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure\n");\r
+               ret->decision = DECISION_FAIL;\r
+               ret->methodState = METHOD_MAY_CONT;\r
+               ret->allowNotifications = FALSE;\r
+               /* Reply with EAP-Failure within the TLS channel to complete\r
+                * failure reporting. */\r
+               resp = wpabuf_alloc(sizeof(struct eap_hdr));\r
+               if (resp) {\r
+                       rhdr = wpabuf_put(resp, sizeof(*rhdr));\r
+                       rhdr->code = EAP_CODE_FAILURE;\r
+                       rhdr->identifier = hdr->identifier;\r
+                       rhdr->length = host_to_be16(sizeof(*rhdr));\r
+               }\r
+               break;\r
+       default:\r
+               wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in "\r
+                          "Phase 2 EAP header", hdr->code);\r
+               break;\r
+       }\r
+\r
+       wpabuf_free(in_decrypted);\r
+\r
+       if (resp) {\r
+               int skip_change2 = 0;\r
+               struct wpabuf *rmsg, buf;\r
+\r
+               wpa_hexdump_buf_key(MSG_DEBUG,\r
+                                   "EAP-PEAP: Encrypting Phase 2 data", resp);\r
+               /* PEAP version changes */\r
+               if (data->peap_version >= 2) {\r
+                       resp = eap_peapv2_tlv_eap_payload(resp);\r
+                       if (resp == NULL)\r
+                               return -1;\r
+               }\r
+               if (wpabuf_len(resp) >= 5 &&\r
+                   wpabuf_head_u8(resp)[0] == EAP_CODE_RESPONSE &&\r
+                   eap_get_type(resp) == EAP_TYPE_TLV)\r
+                       skip_change2 = 1;\r
+               rmsg = resp;\r
+               if (data->peap_version == 0 && !skip_change2) {\r
+                       wpabuf_set(&buf, wpabuf_head_u8(resp) +\r
+                                  sizeof(struct eap_hdr),\r
+                                  wpabuf_len(resp) - sizeof(struct eap_hdr));\r
+                       rmsg = &buf;\r
+               }\r
+\r
+               if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP,\r
+                                        data->peap_version, req->identifier,\r
+                                        rmsg, out_data)) {\r
+                       wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt "\r
+                                  "a Phase 2 frame");\r
+               }\r
+               wpabuf_free(resp);\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+\r
+static struct wpabuf *\r
+eap_peap_process(struct eap_sm *sm, void *priv,\r
+                struct eap_method_ret *ret,\r
+                const struct wpabuf *reqData)\r
+{\r
+       const struct eap_hdr *req;\r
+       size_t left;\r
+       int res;\r
+       u8 flags, id;\r
+       struct wpabuf *resp;\r
+       const u8 *pos;\r
+       struct eap_peap_data *data = priv;\r
+\r
+       pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret,\r
+                                       reqData, &left, &flags);\r
+       if (pos == NULL)\r
+               return NULL;\r
+       req = wpabuf_head(reqData);\r
+       id = req->identifier;\r
+\r
+       if (flags & EAP_TLS_FLAGS_START) {\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own "\r
+                          "ver=%d)", flags & EAP_TLS_VERSION_MASK,\r
+                       data->peap_version);\r
+               if ((flags & EAP_TLS_VERSION_MASK) < data->peap_version)\r
+                       data->peap_version = flags & EAP_TLS_VERSION_MASK;\r
+               if (data->force_peap_version >= 0 &&\r
+                   data->force_peap_version != data->peap_version) {\r
+                       wpa_printf(MSG_WARNING, "EAP-PEAP: Failed to select "\r
+                                  "forced PEAP version %d",\r
+                                  data->force_peap_version);\r
+                       ret->methodState = METHOD_DONE;\r
+                       ret->decision = DECISION_FAIL;\r
+                       ret->allowNotifications = FALSE;\r
+                       return NULL;\r
+               }\r
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Using PEAP version %d",\r
+                          data->peap_version);\r
+               left = 0; /* make sure that this frame is empty, even though it\r
+                          * should always be, anyway */\r
+       }\r
+\r
+       resp = NULL;\r
+       if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&\r
+           !data->resuming) {\r
+               struct wpabuf msg;\r
+               wpabuf_set(&msg, pos, left);\r
+               res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp);\r
+       } else {\r
+               res = eap_peer_tls_process_helper(sm, &data->ssl,\r
+                                                 EAP_TYPE_PEAP,\r
+                                                 data->peap_version, id, pos,\r
+                                                 left, &resp);\r
+\r
+               if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {\r
+                       char label[24];\r
+                       wpa_printf(MSG_DEBUG, "EAP-PEAP: TLS done, proceed to Phase 2");\r
+                       os_free(data->key_data);\r
+                       /* draft-josefsson-ppext-eap-tls-eap-05.txt\r
+                        * specifies that PEAPv1 would use "client PEAP\r
+                        * encryption" as the label. However, most existing\r
+                        * PEAPv1 implementations seem to be using the old\r
+                        * label, "client EAP encryption", instead. Use the old\r
+                        * label by default, but allow it to be configured with\r
+                        * phase1 parameter peaplabel=1. */\r
+                       if (data->peap_version > 1 || data->force_new_label)\r
+                               //label = "client PEAP encryption";\r
+                               strcpy(label, "client PEAP encryption");\r
+                       else\r
+                               //label = "client EAP encryption";\r
+                               strcpy(label, "client EAP encryption");\r
+                       wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in "\r
+                                  "key derivation", label);\r
+                       data->key_data =\r
+                               eap_peer_tls_derive_key(sm, &data->ssl, label,\r
+                                                       EAP_TLS_KEY_LEN);\r
+                       if (data->key_data) {\r
+                               wpa_hexdump_key(MSG_DEBUG, \r
+                                               "EAP-PEAP: Derived key",\r
+                                               data->key_data,\r
+                                               EAP_TLS_KEY_LEN);\r
+                       } else {\r
+                               wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to "\r
+                                          "derive key");\r
+                       }\r
+\r
+                       os_free(data->session_id);\r
+                       data->session_id =\r
+                               eap_peer_tls_derive_session_id(sm, &data->ssl,\r
+                                                              EAP_TYPE_PEAP,\r
+                                                              &data->id_len);\r
+                       if (data->session_id) {\r
+                               wpa_hexdump(MSG_DEBUG,\r
+                                           "EAP-PEAP: Derived Session-Id",\r
+                                           data->session_id, data->id_len);\r
+                       } else {\r
+                               wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to "\r
+                                          "derive Session-Id");\r
+                       }\r
+\r
+                       if (sm->workaround && data->resuming) {\r
+                               /*\r
+                                * At least few RADIUS servers (Aegis v1.1.6;\r
+                                * but not v1.1.4; and Cisco ACS) seem to be\r
+                                * terminating PEAPv1 (Aegis) or PEAPv0 (Cisco\r
+                                * ACS) session resumption with outer\r
+                                * EAP-Success. This does not seem to follow\r
+                                * draft-josefsson-pppext-eap-tls-eap-05.txt\r
+                                * section 4.2, so only allow this if EAP\r
+                                * workarounds are enabled.\r
+                                */\r
+                               wpa_printf(MSG_DEBUG, "EAP-PEAP: Workaround - "\r
+                                          "allow outer EAP-Success to "\r
+                                          "terminate PEAP resumption");\r
+                               ret->decision = DECISION_COND_SUCC;\r
+                               data->phase2_success = 1;\r
+                       }\r
+\r
+                       data->resuming = 0;\r
+               }\r
+\r
+               if (res == 2) {\r
+                       struct wpabuf msg;\r
+                       /*\r
+                        * Application data included in the handshake message.\r
+                        */\r
+                       wpabuf_free(data->pending_phase2_req);\r
+                       data->pending_phase2_req = resp;\r
+                       resp = NULL;\r
+                       wpabuf_set(&msg, pos, left);\r
+                       res = eap_peap_decrypt(sm, data, ret, req, &msg,\r
+                                              &resp);\r
+               }\r
+       }\r
+\r
+       if (ret->methodState == METHOD_DONE) {\r
+               ret->allowNotifications = FALSE;\r
+       }\r
+\r
+       if (res == 1) {\r
+               wpabuf_free(resp);\r
+               return eap_peer_tls_build_ack(id, EAP_TYPE_PEAP,\r
+                                             data->peap_version);\r
+       }\r
+\r
+       return resp;\r
+}\r
+\r
+\r
+static bool\r
+eap_peap_has_reauth_data(struct eap_sm *sm, void *priv)\r
+{\r
+       struct eap_peap_data *data = priv;\r
+       return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&\r
+               data->phase2_success;\r
+}\r
+\r
+\r
+static void\r
+eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv)\r
+{\r
+       struct eap_peap_data *data = priv;\r
+       wpabuf_free(data->pending_phase2_req);\r
+       data->pending_phase2_req = NULL;\r
+       data->crypto_binding_used = 0;\r
+}\r
+\r
+\r
+static void *\r
+eap_peap_init_for_reauth(struct eap_sm *sm, void *priv)\r
+{\r
+       struct eap_peap_data *data = priv;\r
+       os_free(data->key_data);\r
+       data->key_data = NULL;\r
+       os_free(data->session_id);\r
+       data->session_id = NULL;\r
+       if (eap_peer_tls_reauth_init(sm, &data->ssl)) {\r
+               os_free(data);\r
+               return NULL;\r
+       }\r
+       if (data->phase2_priv && data->phase2_method &&\r
+           data->phase2_method->init_for_reauth)\r
+               data->phase2_method->init_for_reauth(sm, data->phase2_priv);\r
+       data->phase2_success = 0;\r
+       data->phase2_eap_success = 0;\r
+       data->phase2_eap_started = 0;\r
+       data->resuming = 1;\r
+       data->reauth = 1;\r
+       sm->peap_done = FALSE;\r
+       return priv;\r
+}\r
+\r
+\r
+static int\r
+eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf,\r
+                   size_t buflen, int verbose)\r
+{\r
+       struct eap_peap_data *data = priv;\r
+       int len, ret;\r
+\r
+       len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);\r
+       if (data->phase2_method) {\r
+               ret = snprintf(buf + len, buflen - len,\r
+                                 "EAP-PEAPv%d Phase2 method=%d\n",\r
+                                 data->peap_version,\r
+                                 data->phase2_method->method);\r
+               if (ret < 0 || (size_t) ret >= buflen - len)\r
+                       return len;\r
+               len += ret;\r
+       }\r
+       return len;\r
+}\r
+\r
+\r
+static bool\r
+eap_peap_isKeyAvailable(struct eap_sm *sm, void *priv)\r
+{\r
+       struct eap_peap_data *data = priv;\r
+       return data->key_data != NULL && data->phase2_success;\r
+}\r
+\r
+\r
+static u8 *\r
+eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)\r
+{\r
+       struct eap_peap_data *data = priv;\r
+       u8 *key;\r
+\r
+       if (data->key_data == NULL || !data->phase2_success)\r
+               return NULL;\r
+\r
+       key = os_malloc(EAP_TLS_KEY_LEN);\r
+       if (key == NULL)\r
+               return NULL;\r
+\r
+       *len = EAP_TLS_KEY_LEN;\r
+\r
+       if (data->crypto_binding_used) {\r
+               u8 csk[128];\r
+               /*\r
+                * Note: It looks like Microsoft implementation requires null\r
+                * termination for this label while the one used for deriving\r
+                * IPMK|CMK did not use null termination.\r
+                */\r
+               if (peap_prfplus(data->peap_version, data->ipmk, 40,\r
+                                "Session Key Generating Function",\r
+                                (u8 *) "\00", 1, csk, sizeof(csk)) < 0) {\r
+                       os_free(key);\r
+                       return NULL;\r
+               }\r
+               wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk));\r
+               os_memcpy(key, csk, EAP_TLS_KEY_LEN);\r
+               wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",\r
+                           key, EAP_TLS_KEY_LEN);\r
+       } else\r
+               os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);\r
+\r
+       return key;\r
+}\r
+\r
+\r
+static u8 *\r
+eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len)\r
+{\r
+       struct eap_peap_data *data = priv;\r
+       u8 *id;\r
+\r
+       if (data->session_id == NULL || !data->phase2_success)\r
+               return NULL;\r
+\r
+       id = os_malloc(data->id_len);\r
+       if (id == NULL)\r
+               return NULL;\r
+\r
+       *len = data->id_len;\r
+       os_memcpy(id, data->session_id, data->id_len);\r
+\r
+       return id;\r
+}\r
+\r
+\r
+int\r
+eap_peer_peap_register(void)\r
+{\r
+       struct eap_method *eap;\r
+       int ret;\r
+\r
+       eap = eap_peer_method_alloc(EAP_VENDOR_IETF, EAP_TYPE_PEAP,\r
+                                   "PEAP");\r
+       if (eap == NULL)\r
+               return -1;\r
+\r
+       eap->init = eap_peap_init;\r
+       eap->deinit = eap_peap_deinit;\r
+       eap->process = eap_peap_process;\r
+       eap->isKeyAvailable = eap_peap_isKeyAvailable;\r
+       eap->getKey = eap_peap_getKey;\r
+       eap->get_status = eap_peap_get_status;\r
+       eap->has_reauth_data = eap_peap_has_reauth_data;\r
+       eap->deinit_for_reauth = eap_peap_deinit_for_reauth;\r
+       eap->init_for_reauth = eap_peap_init_for_reauth;\r
+       eap->getSessionId = eap_peap_get_session_id;\r
+\r
+       ret = eap_peer_method_register(eap);\r
+       if (ret)\r
+               eap_peer_method_free(eap);\r
+       return ret;\r
+}\r
+\r
+#endif /* EAP_PEAP */\r
diff --git a/components/wpa_supplicant/src/wpa2/eap_peer/eap_peap_common.c b/components/wpa_supplicant/src/wpa2/eap_peer/eap_peap_common.c
new file mode 100644 (file)
index 0000000..d3fbbfc
--- /dev/null
@@ -0,0 +1,90 @@
+/*\r
+ * EAP-PEAP common routines\r
+ * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>\r
+ *\r
+ * This software may be distributed under the terms of the BSD license.\r
+ * See README for more details.\r
+ */\r
+\r
+#ifdef EAP_PEAP\r
+\r
+#include "wpa/includes.h"\r
+\r
+#include "wpa/common.h"\r
+#include "crypto/sha1.h"\r
+#include "wpa2/eap_peer/eap_peap_common.h"\r
+\r
+int\r
+peap_prfplus(int version, const u8 *key, size_t key_len,\r
+            const char *label, const u8 *seed, size_t seed_len,\r
+            u8 *buf, size_t buf_len)\r
+{\r
+       unsigned char counter = 0;\r
+       size_t pos, plen;\r
+       u8 hash[SHA1_MAC_LEN];\r
+       size_t label_len = os_strlen(label);\r
+       u8 extra[2];\r
+       const unsigned char *addr[5];\r
+       size_t len[5];\r
+\r
+       addr[0] = hash;\r
+       len[0] = 0;\r
+       addr[1] = (unsigned char *) label;\r
+       len[1] = label_len;\r
+       addr[2] = seed;\r
+       len[2] = seed_len;\r
+\r
+       if (version == 0) {\r
+               /*\r
+                * PRF+(K, S, LEN) = T1 | T2 | ... | Tn\r
+                * T1 = HMAC-SHA1(K, S | 0x01 | 0x00 | 0x00)\r
+                * T2 = HMAC-SHA1(K, T1 | S | 0x02 | 0x00 | 0x00)\r
+                * ...\r
+                * Tn = HMAC-SHA1(K, Tn-1 | S | n | 0x00 | 0x00)\r
+                */\r
+\r
+               extra[0] = 0;\r
+               extra[1] = 0;\r
+\r
+               addr[3] = &counter;\r
+               len[3] = 1;\r
+               addr[4] = extra;\r
+               len[4] = 2;\r
+       } else {\r
+               /*\r
+                * PRF (K,S,LEN) = T1 | T2 | T3 | T4 | ... where:\r
+                * T1 = HMAC-SHA1(K, S | LEN | 0x01)\r
+                * T2 = HMAC-SHA1 (K, T1 | S | LEN | 0x02)\r
+                * T3 = HMAC-SHA1 (K, T2 | S | LEN | 0x03)\r
+                * T4 = HMAC-SHA1 (K, T3 | S | LEN | 0x04)\r
+                *   ...\r
+                */\r
+\r
+               extra[0] = buf_len & 0xff;\r
+\r
+               addr[3] = extra;\r
+               len[3] = 1;\r
+               addr[4] = &counter;\r
+               len[4] = 1;\r
+       }\r
+\r
+       pos = 0;\r
+       while (pos < buf_len) {\r
+               counter++;\r
+               plen = buf_len - pos;\r
+               if (hmac_sha1_vector(key, key_len, 5, addr, len, hash) < 0)\r
+                       return -1;\r
+               if (plen >= SHA1_MAC_LEN) {\r
+                       os_memcpy(&buf[pos], hash, SHA1_MAC_LEN);\r
+                       pos += SHA1_MAC_LEN;\r
+               } else {\r
+                       os_memcpy(&buf[pos], hash, plen);\r
+                       break;\r
+               }\r
+               len[0] = SHA1_MAC_LEN;\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+#endif /* EAP_PEAP */\r
diff --git a/components/wpa_supplicant/src/wpa2/eap_peer/eap_tls.c b/components/wpa_supplicant/src/wpa2/eap_peer/eap_tls.c
new file mode 100644 (file)
index 0000000..00dabfe
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * EAP peer method: EAP-TLS (RFC 2716)
+ * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#ifdef EAP_TLS
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/eap_peer/eap_i.h"
+#include "wpa2/eap_peer/eap_defs.h"
+#include "wpa2/eap_peer/eap_tls_common.h"
+#include "wpa2/eap_peer/eap_config.h"
+#include "wpa2/eap_peer/eap_methods.h"
+
+struct eap_tls_data {
+       struct eap_ssl_data ssl;
+       u8 *key_data;
+       u8 *session_id;
+       size_t id_len;
+       void *ssl_ctx;
+       u8 eap_type;
+};
+
+
+
+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;
+       struct eap_peer_config *config = eap_get_config(sm);
+       if (config == NULL ||
+           config->private_key == 0) {
+               wpa_printf(MSG_INFO, "EAP-TLS: Private key not configured");
+               return NULL;
+       }
+
+       data = (struct eap_tls_data *)os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+
+       data->ssl_ctx = sm->ssl_ctx;
+
+       if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TLS)) {
+               wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+               eap_tls_deinit(sm, data);
+               return NULL;
+       }
+
+       data->eap_type = EAP_TYPE_TLS;
+
+       return data;
+}
+
+static struct wpabuf * eap_tls_failure(struct eap_sm *sm,
+                                      struct eap_tls_data *data,
+                                      struct eap_method_ret *ret, int res,
+                                      struct wpabuf *resp, u8 id)
+{
+       wpa_printf(MSG_DEBUG, "EAP-TLS: TLS processing failed");
+
+       ret->methodState = METHOD_DONE;
+       ret->decision = DECISION_FAIL;
+
+       if (res == -1) {
+               struct eap_peer_config *config = eap_get_config(sm);
+               if (config) {
+                       /*
+                        * The TLS handshake failed. So better forget the old
+                        * PIN. It may be wrong, we cannot be sure but trying
+                        * the wrong one again might block it on the card--so
+                        * better ask the user again.
+                        */
+                       os_free(config->pin);
+                       config->pin = NULL;
+               }
+       }
+
+       if (resp) {
+               /*
+                * This is likely an alert message, so send it instead of just
+                * ACKing the error.
+                */
+               return resp;
+       }
+
+       return eap_peer_tls_build_ack(id, data->eap_type, 0);
+}
+
+
+static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data,
+                           struct eap_method_ret *ret)
+{
+       wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
+
+       ret->methodState = METHOD_DONE;
+       ret->decision = DECISION_UNCOND_SUCC;
+
+       os_free(data->key_data);
+       data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
+                                                "client EAP encryption",
+                                                EAP_TLS_KEY_LEN +
+                                                EAP_EMSK_LEN);
+       if (data->key_data) {
+               wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived key",
+                               data->key_data, EAP_TLS_KEY_LEN);
+               wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived EMSK",
+                               data->key_data + EAP_TLS_KEY_LEN,
+                               EAP_EMSK_LEN);
+       } else {
+               wpa_printf(MSG_INFO, "EAP-TLS: Failed to derive key");
+       }
+
+       os_free(data->session_id);
+       data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl,
+                                                         EAP_TYPE_TLS,
+                                                         &data->id_len);
+       if (data->session_id) {
+               wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived Session-Id",
+                           data->session_id, data->id_len);
+       } else {
+               wpa_printf(MSG_ERROR, "EAP-TLS: Failed to derive Session-Id");
+       }
+}
+
+
+static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv,
+                                      struct eap_method_ret *ret,
+                                      const struct wpabuf *reqData)
+{
+       size_t left;
+       int res;
+       struct wpabuf *resp;
+       u8 flags, id;
+       const u8 *pos;
+       struct eap_tls_data *data = priv;
+
+       pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret,
+                                       reqData, &left, &flags);
+       if (pos == NULL)
+               return NULL;
+       id = eap_get_id(reqData);
+
+       if (flags & EAP_TLS_FLAGS_START) {
+               wpa_printf(MSG_DEBUG, "EAP-TLS: Start");
+               left = 0; /* make sure that this frame is empty, even though it
+                          * should always be, anyway */
+       }
+
+       resp = NULL;
+       res = eap_peer_tls_process_helper(sm, &data->ssl, data->eap_type, 0,
+                                         id, pos, left, &resp);
+
+       if (res < 0) {
+               return eap_tls_failure(sm, data, ret, res, resp, id);
+       }
+
+       if (tls_connection_established(data->ssl_ctx, data->ssl.conn))
+               eap_tls_success(sm, data, ret);
+
+       if (res == 1) {
+               wpabuf_free(resp);
+               return eap_peer_tls_build_ack(id, data->eap_type, 0);
+       }
+
+       return resp;
+}
+
+static bool eap_tls_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+       struct eap_tls_data *data = priv;
+
+       return data->key_data != NULL;
+}
+static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_tls_data *data = priv;
+       u8 *key;
+
+       if (data->key_data == NULL)
+               return NULL;
+
+       key = os_malloc(EAP_TLS_KEY_LEN);
+       if (key == NULL)
+               return NULL;
+
+       *len = EAP_TLS_KEY_LEN;
+       os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
+
+       return key;
+}
+
+int eap_peer_tls_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLS,
+                                   "TLS");
+
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_tls_init;
+       eap->deinit = eap_tls_deinit;
+       eap->process = eap_tls_process;
+       eap->isKeyAvailable = eap_tls_isKeyAvailable;
+       eap->getKey = eap_tls_getKey;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
+
+
+#endif /* EAP_TLS */
diff --git a/components/wpa_supplicant/src/wpa2/eap_peer/eap_tls_common.c b/components/wpa_supplicant/src/wpa2/eap_peer/eap_tls_common.c
new file mode 100644 (file)
index 0000000..2c97e6c
--- /dev/null
@@ -0,0 +1,1063 @@
+/*
+ * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
+ * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/sha1.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/eap_peer/eap_i.h"
+#include "wpa2/eap_peer/eap_tls_common.h"
+#include "wpa2/eap_peer/eap_config.h"
+#include "wpa2/eap_peer/eap_methods.h"
+
+static struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
+                                        u8 code, u8 identifier)
+{
+       if (type == EAP_UNAUTH_TLS_TYPE)
+               return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS,
+                                    EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len,
+                                    code, identifier);
+       return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code,
+                            identifier);
+}
+
+
+static int eap_tls_check_blob(struct eap_sm *sm, const char **name,
+                             const u8 **data, size_t *data_len)
+{
+       const struct wpa_config_blob *blob;
+
+       if (*name == NULL)// || os_strncmp(*name, "blob://", 7) != 0)
+               return 0;
+
+       blob = eap_get_config_blob(sm, *name);// + 7);
+       if (blob == NULL) {
+               wpa_printf(MSG_ERROR, "%s: Named configuration blob '%s' not "
+                          "found", __func__, *name);// + 7);
+               return -1;
+       }
+
+       *name = NULL;
+       *data = blob->data;
+       *data_len = blob->len;
+
+       return 0;
+}
+
+
+static void eap_tls_params_flags(struct tls_connection_params *params,
+                                const char *txt)
+{
+       if (txt == NULL)
+               return;
+       if (os_strstr(txt, "tls_allow_md5=1"))
+               params->flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5;
+       if (os_strstr(txt, "tls_disable_time_checks=1"))
+               params->flags |= TLS_CONN_DISABLE_TIME_CHECKS;
+       if (os_strstr(txt, "tls_disable_session_ticket=1"))
+               params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
+       if (os_strstr(txt, "tls_disable_session_ticket=0"))
+               params->flags &= ~TLS_CONN_DISABLE_SESSION_TICKET;
+}
+
+static void eap_tls_params_from_conf1(struct tls_connection_params *params,
+                                     struct eap_peer_config *config)
+{
+       params->ca_cert = (char *) config->ca_cert;
+       params->ca_path = (char *) config->ca_path;
+       params->client_cert = (char *) config->client_cert;
+       params->private_key = (char *) config->private_key;
+       params->private_key_passwd = (char *) config->private_key_passwd;
+       eap_tls_params_flags(params, config->phase1);
+       if (wifi_sta_get_enterprise_disable_time_check())
+               params->flags |= TLS_CONN_DISABLE_TIME_CHECKS;
+       else
+               params->flags &= (~TLS_CONN_DISABLE_TIME_CHECKS);
+}
+
+static int eap_tls_params_from_conf(struct eap_sm *sm,
+                                   struct eap_ssl_data *data,
+                                   struct tls_connection_params *params,
+                                   struct eap_peer_config *config)
+{
+       os_memset(params, 0, sizeof(*params));
+       if (sm->workaround && data->eap_type != EAP_TYPE_FAST) {
+               /*
+                * Some deployed authentication servers seem to be unable to
+                * handle the TLS Session Ticket extension (they are supposed
+                * to ignore unrecognized TLS extensions, but end up rejecting
+                * the ClientHello instead). As a workaround, disable use of
+                * TLS Sesson Ticket extension for EAP-TLS, EAP-PEAP, and
+                * EAP-TTLS (EAP-FAST uses session ticket, so any server that
+                * supports EAP-FAST does not need this workaround).
+                */
+               params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLS: using phase1 config options");
+       eap_tls_params_from_conf1(params, config);
+
+       /*
+        * Use blob data, if available. Otherwise, leave reference to external
+        * file as-is.
+        */
+       if (eap_tls_check_blob(sm, &params->ca_cert, &params->ca_cert_blob,
+                              &params->ca_cert_blob_len) ||
+           eap_tls_check_blob(sm, &params->client_cert,
+                              &params->client_cert_blob,
+                              &params->client_cert_blob_len) ||
+           eap_tls_check_blob(sm, &params->private_key,
+                              &params->private_key_blob,
+                              &params->private_key_blob_len)) {
+               wpa_printf(MSG_INFO, "SSL: Failed to get configuration blobs");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int eap_tls_init_connection(struct eap_sm *sm,
+                                  struct eap_ssl_data *data,
+                                  struct eap_peer_config *config,
+                                  struct tls_connection_params *params)
+{
+       int res;
+
+       if (config->ocsp)
+               params->flags |= TLS_CONN_REQUEST_OCSP;
+       if (config->ocsp == 2)
+               params->flags |= TLS_CONN_REQUIRE_OCSP;
+       data->conn = tls_connection_init(data->ssl_ctx);
+       if (data->conn == NULL) {
+               wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
+                          "connection");
+               return -1;
+       }
+
+       res = tls_connection_set_params(data->ssl_ctx, data->conn, params);
+
+       if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) {
+               /*
+                * At this point with the pkcs11 engine the PIN might be wrong.
+                * We reset the PIN in the configuration to be sure to not use
+                * it again and the calling function must request a new one.
+                */
+               os_free(config->pin);
+               config->pin = NULL;
+       } else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) {
+               wpa_printf(MSG_INFO, "TLS: Failed to load private key");
+               /*
+                * We do not know exactly but maybe the PIN was wrong,
+                * so ask for a new one.
+                */
+               os_free(config->pin);
+               config->pin = NULL;
+               tls_connection_deinit(data->ssl_ctx, data->conn);
+               data->conn = NULL;
+               return -1;
+       } else if (res) {
+               wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection "
+                          "parameters");
+               tls_connection_deinit(data->ssl_ctx, data->conn);
+               data->conn = NULL;
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * eap_peer_tls_ssl_init - Initialize shared TLS functionality
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @config: Pointer to the network configuration
+ * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to initialize shared TLS functionality for EAP-TLS,
+ * EAP-PEAP, EAP-TTLS, and EAP-FAST.
+ */
+int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
+                         struct eap_peer_config *config, u8 eap_type)
+{
+       struct tls_connection_params params;
+
+       if (config == NULL)
+               return -1;
+
+       data->eap = sm;
+       data->eap_type = eap_type;
+       data->ssl_ctx = sm->ssl_ctx;
+       if (eap_tls_params_from_conf(sm, data, &params, config) < 0) /* no phase2 */
+               return -1;
+
+       if (eap_tls_init_connection(sm, data, config, &params) < 0)
+               return -1;
+
+       data->tls_out_limit = config->fragment_size;
+
+       if (config->phase1 &&
+           os_strstr(config->phase1, "include_tls_length=1")) {
+               wpa_printf(MSG_INFO, "TLS: Include TLS Message Length in "
+                          "unfragmented packets");
+               data->include_tls_length = 1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * eap_peer_tls_ssl_deinit - Deinitialize shared TLS functionality
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ *
+ * This function deinitializes shared TLS functionality that was initialized
+ * with eap_peer_tls_ssl_init().
+ */
+void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
+{
+       tls_connection_deinit(data->ssl_ctx, data->conn);
+       eap_peer_tls_reset_input(data);
+       eap_peer_tls_reset_output(data);
+}
+
+
+/**
+ * eap_peer_tls_derive_key - Derive a key based on TLS session data
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @label: Label string for deriving the keys, e.g., "client EAP encryption"
+ * @len: Length of the key material to generate (usually 64 for MSK)
+ * Returns: Pointer to allocated key on success or %NULL on failure
+ *
+ * This function uses TLS-PRF to generate pseudo-random data based on the TLS
+ * session data (client/server random and master key). Each key type may use a
+ * different label to bind the key usage into the generated material.
+ *
+ * The caller is responsible for freeing the returned buffer.
+ */
+u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+                            const char *label, size_t len)
+{
+       struct tls_keys keys;
+       u8 *rnd = NULL, *out;
+
+       out = os_malloc(len);
+       if (out == NULL)
+               return NULL;
+
+       /* First, try to use TLS library function for PRF, if available. */
+       if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, out, len)
+           == 0)
+               return out;
+
+       /*
+        * TLS library did not support key generation, so get the needed TLS
+        * session parameters and use an internal implementation of TLS PRF to
+        * derive the key.
+        */
+       if (tls_connection_get_keys(data->ssl_ctx, data->conn, &keys))
+               goto fail;
+
+       if (keys.client_random == NULL || keys.server_random == NULL ||
+           keys.master_key == NULL)
+               goto fail;
+
+       rnd = os_malloc(keys.client_random_len + keys.server_random_len);
+       if (rnd == NULL)
+               goto fail;
+       os_memcpy(rnd, keys.client_random, keys.client_random_len);
+       os_memcpy(rnd + keys.client_random_len, keys.server_random,
+                 keys.server_random_len);
+
+       if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len,
+                            label, rnd, keys.client_random_len +
+                            keys.server_random_len, out, len)) {
+               goto fail;
+       }
+
+       os_free(rnd);
+       return out;
+
+fail:
+       os_free(out);
+       os_free(rnd);
+       return NULL;
+}
+
+
+/**
+ * eap_peer_tls_derive_session_id - Derive a Session-Id based on TLS data
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
+ * @len: Pointer to length of the session ID generated
+ * Returns: Pointer to allocated Session-Id on success or %NULL on failure
+ *
+ * This function derive the Session-Id based on the TLS session data
+ * (client/server random and method type).
+ *
+ * The caller is responsible for freeing the returned buffer.
+ */
+u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm,
+                                   struct eap_ssl_data *data, u8 eap_type,
+                                   size_t *len)
+{
+       struct tls_keys keys;
+       u8 *out;
+
+       /*
+        * TLS library did not support session ID generation,
+        * so get the needed TLS session parameters
+        */
+       if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
+               return NULL;
+
+       if (keys.client_random == NULL || keys.server_random == NULL ||
+           keys.master_key == NULL)
+               return NULL;
+
+       *len = 1 + keys.client_random_len + keys.server_random_len;
+       out = os_malloc(*len);
+       if (out == NULL)
+               return NULL;
+
+       /* Session-Id = EAP type || client.random || server.random */
+       out[0] = eap_type;
+       os_memcpy(out + 1, keys.client_random, keys.client_random_len);
+       os_memcpy(out + 1 + keys.client_random_len, keys.server_random,
+                 keys.server_random_len);
+
+       return out;
+}
+
+
+/**
+ * eap_peer_tls_reassemble_fragment - Reassemble a received fragment
+ * @data: Data for TLS processing
+ * @in_data: Next incoming TLS segment
+ * Returns: 0 on success, 1 if more data is needed for the full message, or
+ * -1 on error
+ */
+static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data,
+                                           const struct wpabuf *in_data)
+{
+       size_t tls_in_len, in_len;
+
+       tls_in_len = data->tls_in ? wpabuf_len(data->tls_in) : 0;
+       in_len = in_data ? wpabuf_len(in_data) : 0;
+
+       if (tls_in_len + in_len == 0) {
+               /* No message data received?! */
+               wpa_printf(MSG_WARNING, "SSL: Invalid reassembly state: "
+                          "tls_in_left=%lu tls_in_len=%lu in_len=%lu",
+                          (unsigned long) data->tls_in_left,
+                          (unsigned long) tls_in_len,
+                          (unsigned long) in_len);
+               eap_peer_tls_reset_input(data);
+               return -1;
+       }
+
+       if (tls_in_len + in_len > 65536) {
+               /*
+                * Limit length to avoid rogue servers from causing large
+                * memory allocations.
+                */
+               wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size over "
+                          "64 kB)");
+               eap_peer_tls_reset_input(data);
+               return -1;
+       }
+
+       if (in_len > data->tls_in_left) {
+               /* Sender is doing something odd - reject message */
+               wpa_printf(MSG_INFO, "SSL: more data than TLS message length "
+                          "indicated");
+               eap_peer_tls_reset_input(data);
+               return -1;
+       }
+
+       if (wpabuf_resize(&data->tls_in, in_len) < 0) {
+               wpa_printf(MSG_INFO, "SSL: Could not allocate memory for TLS "
+                          "data");
+               eap_peer_tls_reset_input(data);
+               return -1;
+       }
+       if (in_data)
+               wpabuf_put_buf(data->tls_in, in_data);
+       data->tls_in_left -= in_len;
+
+       if (data->tls_in_left > 0) {
+               wpa_printf(MSG_INFO, "SSL: Need %lu bytes more input "
+                          "data", (unsigned long) data->tls_in_left);
+               return 1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * eap_peer_tls_data_reassemble - Reassemble TLS data
+ * @data: Data for TLS processing
+ * @in_data: Next incoming TLS segment
+ * @need_more_input: Variable for returning whether more input data is needed
+ * to reassemble this TLS packet
+ * Returns: Pointer to output data, %NULL on error or when more data is needed
+ * for the full message (in which case, *need_more_input is also set to 1).
+ *
+ * This function reassembles TLS fragments. Caller must not free the returned
+ * data buffer since an internal pointer to it is maintained.
+ */
+static const struct wpabuf * eap_peer_tls_data_reassemble(
+       struct eap_ssl_data *data, const struct wpabuf *in_data,
+       int *need_more_input)
+{
+       *need_more_input = 0;
+
+       if (data->tls_in_left > wpabuf_len(in_data) || data->tls_in) {
+               /* Message has fragments */
+               int res = eap_peer_tls_reassemble_fragment(data, in_data);
+               if (res) {
+                       if (res == 1)
+                               *need_more_input = 1;
+                       return NULL;
+               }
+
+               /* Message is now fully reassembled. */
+       } else {
+               /* No fragments in this message, so just make a copy of it. */
+               data->tls_in_left = 0;
+               data->tls_in = wpabuf_dup(in_data);
+               if (data->tls_in == NULL)
+                       return NULL;
+       }
+
+       return data->tls_in;
+}
+
+
+/**
+ * eap_tls_process_input - Process incoming TLS message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @in_data: Message received from the server
+ * @in_len: Length of in_data
+ * @out_data: Buffer for returning a pointer to application data (if available)
+ * Returns: 0 on success, 1 if more input data is needed, 2 if application data
+ * is available, -1 on failure
+ */
+static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data,
+                                const u8 *in_data, size_t in_len,
+                                struct wpabuf **out_data)
+{
+       const struct wpabuf *msg;
+       int need_more_input;
+       struct wpabuf *appl_data;
+       struct wpabuf buf;
+
+       wpabuf_set(&buf, in_data, in_len);
+       msg = eap_peer_tls_data_reassemble(data, &buf, &need_more_input);
+       if (msg == NULL)
+               return need_more_input ? 1 : -1;
+
+       /* Full TLS message reassembled - continue handshake processing */
+       if (data->tls_out) {
+               /* This should not happen.. */
+               wpa_printf(MSG_INFO, "SSL: eap_tls_process_input - pending "
+                          "tls_out data even though tls_out_len = 0");
+               wpabuf_free(data->tls_out);
+               //WPA_ASSERT(data->tls_out == NULL);
+       }
+       appl_data = NULL;
+       data->tls_out = tls_connection_handshake(data->ssl_ctx, data->conn,
+                                                msg, &appl_data);
+
+       eap_peer_tls_reset_input(data);
+       if (appl_data &&
+           tls_connection_established(data->ssl_ctx, data->conn) &&
+           !tls_connection_get_failed(data->ssl_ctx, data->conn)) {
+               wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application data",
+                                   appl_data);
+               *out_data = appl_data;
+               return 2;
+       }
+
+       wpabuf_free(appl_data);
+
+       return 0;
+}
+
+
+/**
+ * eap_tls_process_output - Process outgoing TLS message
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * @id: EAP identifier for the response
+ * @ret: Return value to use on success
+ * @out_data: Buffer for returning the allocated output buffer
+ * Returns: ret (0 or 1) on success, -1 on failure
+ */
+static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type,
+                                 int peap_version, u8 id, int ret,
+                                 struct wpabuf **out_data)
+{
+       size_t len;
+       u8 *flags;
+       int more_fragments, length_included;
+
+       if (data->tls_out == NULL)
+               return -1;
+       len = wpabuf_len(data->tls_out) - data->tls_out_pos;
+       wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total "
+                  "%lu bytes)",
+                  (unsigned long) len,
+                  (unsigned long) wpabuf_len(data->tls_out));
+
+       /*
+        * Limit outgoing message to the configured maximum size. Fragment
+        * message if needed.
+        */
+       if (len > data->tls_out_limit) {
+               more_fragments = 1;
+               len = data->tls_out_limit;
+               wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments "
+                          "will follow", (unsigned long) len);
+       } else
+               more_fragments = 0;
+
+       length_included = data->tls_out_pos == 0 &&
+               (wpabuf_len(data->tls_out) > data->tls_out_limit ||
+                data->include_tls_length);
+       if (!length_included &&
+           eap_type == EAP_TYPE_PEAP && peap_version == 0 &&
+           !tls_connection_established(data->eap->ssl_ctx, data->conn)) {
+               /*
+                * Windows Server 2008 NPS really wants to have the TLS Message
+                * length included in phase 0 even for unfragmented frames or
+                * it will get very confused with Compound MAC calculation and
+                * Outer TLVs.
+                */
+               length_included = 1;
+       }
+
+       *out_data = eap_tls_msg_alloc(eap_type, 1 + length_included * 4 + len,
+                                     EAP_CODE_RESPONSE, id);
+       if (*out_data == NULL) {
+           printf("[Debug] out_data is null, return \n");
+               return -1;
+    } 
+
+       flags = wpabuf_put(*out_data, 1);
+       *flags = peap_version;
+       if (more_fragments)
+               *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS;
+       if (length_included) {
+               *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED;
+               wpabuf_put_be32(*out_data, wpabuf_len(data->tls_out));
+       }
+       wpabuf_put_data(*out_data,
+                       wpabuf_head_u8(data->tls_out) + data->tls_out_pos,
+                       len);
+       data->tls_out_pos += len;
+
+       if (!more_fragments)
+               eap_peer_tls_reset_output(data);
+
+       return ret;
+}
+
+
+/**
+ * eap_peer_tls_process_helper - Process TLS handshake message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * @id: EAP identifier for the response
+ * @in_data: Message received from the server
+ * @in_len: Length of in_data
+ * @out_data: Buffer for returning a pointer to the response message
+ * Returns: 0 on success, 1 if more input data is needed, 2 if application data
+ * is available, or -1 on failure
+ *
+ * This function can be used to process TLS handshake messages. It reassembles
+ * the received fragments and uses a TLS library to process the messages. The
+ * response data from the TLS library is fragmented to suitable output messages
+ * that the caller can send out.
+ *
+ * out_data is used to return the response message if the return value of this
+ * function is 0, 2, or -1. In case of failure, the message is likely a TLS
+ * alarm message. The caller is responsible for freeing the allocated buffer if
+ * *out_data is not %NULL.
+ *
+ * This function is called for each received TLS message during the TLS
+ * handshake after eap_peer_tls_process_init() call and possible processing of
+ * TLS Flags field. Once the handshake has been completed, i.e., when
+ * tls_connection_established() returns 1, EAP method specific decrypting of
+ * the tunneled data is used.
+ */
+int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
+                               EapType eap_type, int peap_version,
+                               u8 id, const u8 *in_data, size_t in_len,
+                               struct wpabuf **out_data)
+{
+       int ret = 0;
+
+       *out_data = NULL;
+
+       if (data->tls_out && wpabuf_len(data->tls_out) > 0 && in_len > 0) {
+               wpa_printf(MSG_DEBUG, "SSL: Received non-ACK when output "
+                          "fragments are waiting to be sent out");
+               return -1;
+       }
+
+       if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) {
+               /*
+                * No more data to send out - expect to receive more data from
+                * the AS.
+                */
+               int res = eap_tls_process_input(sm, data, in_data, in_len,
+                                               out_data);
+               if (res) {
+                       /*
+                        * Input processing failed (res = -1) or more data is
+                        * needed (res = 1).
+                        */
+                       return res;
+               }
+
+               /*
+                * The incoming message has been reassembled and processed. The
+                * response was allocated into data->tls_out buffer.
+                */
+       }
+
+       if (data->tls_out == NULL) {
+               /*
+                * No outgoing fragments remaining from the previous message
+                * and no new message generated. This indicates an error in TLS
+                * processing.
+                */
+               eap_peer_tls_reset_output(data);
+               return -1;
+       }
+
+       if (tls_connection_get_failed(data->ssl_ctx, data->conn)) {
+               /* TLS processing has failed - return error */
+               wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to "
+                          "report error");
+               ret = -1;
+               /* TODO: clean pin if engine used? */
+       }
+
+       if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) {
+               /*
+                * TLS negotiation should now be complete since all other cases
+                * needing more data should have been caught above based on
+                * the TLS Message Length field.
+                */
+               wpa_printf(MSG_DEBUG, "SSL: No data to be sent out");
+               wpabuf_free(data->tls_out);
+               data->tls_out = NULL;
+               return 1;
+       }
+
+       /* Send the pending message (in fragments, if needed). */
+       return eap_tls_process_output(data, eap_type, peap_version, id, ret,
+                                     out_data);
+}
+
+
+/**
+ * eap_peer_tls_build_ack - Build a TLS ACK frame
+ * @id: EAP identifier for the response
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * Returns: Pointer to the allocated ACK frame or %NULL on failure
+ */
+struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type,
+                                      int peap_version)
+{      
+       struct wpabuf *resp;
+
+       resp = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_RESPONSE, id);
+       if (resp == NULL)
+               return NULL;
+       wpa_printf(MSG_DEBUG, "SSL: Building ACK (type=%d id=%d ver=%d) \n",
+                  (int) eap_type, id, peap_version);
+       wpabuf_put_u8(resp, peap_version); /* Flags */
+       return resp;
+}
+
+
+/**
+ * eap_peer_tls_reauth_init - Re-initialize shared TLS for session resumption
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data)
+{
+       eap_peer_tls_reset_input(data);
+       eap_peer_tls_reset_output(data);
+       return tls_connection_shutdown(data->ssl_ctx, data->conn);
+}
+
+
+/**
+ * eap_peer_tls_status - Get TLS status
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ */
+int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
+                       char *buf, size_t buflen, int verbose)
+{
+       char name[128];
+       int len = 0, ret;
+
+       if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) == 0)
+       {
+               //ret = os_snprintf(buf + len, buflen - len,
+               ret = sprintf(buf + len,
+                                 "EAP TLS cipher=%s\n", name);
+               if (ret < 0 || (size_t) ret >= buflen - len)
+                       return len;
+               len += ret;
+       }
+
+       return len;
+}
+
+
+/**
+ * eap_peer_tls_process_init - Initial validation/processing of EAP requests
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @ret: Return values from EAP request validation and processing
+ * @reqData: EAP request to be processed (eapReqData)
+ * @len: Buffer for returning length of the remaining payload
+ * @flags: Buffer for returning TLS flags
+ * Returns: Pointer to payload after TLS flags and length or %NULL on failure
+ *
+ * This function validates the EAP header and processes the optional TLS
+ * Message Length field. If this is the first fragment of a TLS message, the
+ * TLS reassembly code is initialized to receive the indicated number of bytes.
+ *
+ * EAP-TLS, EAP-PEAP, EAP-TTLS, and EAP-FAST methods are expected to use this
+ * function as the first step in processing received messages. They will need
+ * to process the flags (apart from Message Length Included) that are returned
+ * through the flags pointer and the message payload that will be returned (and
+ * the length is returned through the len pointer). Return values (ret) are set
+ * for continuation of EAP method processing. The caller is responsible for
+ * setting these to indicate completion (either success or failure) based on
+ * the authentication result.
+ */
+const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
+                                    struct eap_ssl_data *data,
+                                    EapType eap_type,
+                                    struct eap_method_ret *ret,
+                                    const struct wpabuf *reqData,
+                                    size_t *len, u8 *flags)
+{
+       const u8 *pos;
+       size_t left;
+       unsigned int tls_msg_len;
+
+       if (tls_get_errors(data->ssl_ctx)) {
+               wpa_printf(MSG_INFO, "SSL: TLS errors detected");
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (eap_type == EAP_UNAUTH_TLS_TYPE)
+               pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
+                                      EAP_VENDOR_TYPE_UNAUTH_TLS, reqData,
+                                      &left);
+       else
+               pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData,
+                                      &left);
+       if (pos == NULL) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+       if (left == 0) {
+               wpa_printf(MSG_DEBUG, "SSL: Invalid TLS message: no Flags "
+                          "octet included");
+               if (!sm->workaround) {
+                       ret->ignore = TRUE;
+                       return NULL;
+               }
+
+               wpa_printf(MSG_DEBUG, "SSL: Workaround - assume no Flags "
+                          "indicates ACK frame");
+               *flags = 0;
+       } else {
+               *flags = *pos++;
+               left--;
+       }
+       wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - "
+                  "Flags 0x%02x", (unsigned long) wpabuf_len(reqData),
+                  *flags);
+       if (*flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
+               if (left < 4) {
+                       wpa_printf(MSG_INFO, "SSL: Short frame with TLS "
+                                  "length");
+                       ret->ignore = TRUE;
+                       return NULL;
+               }
+               tls_msg_len = WPA_GET_BE32(pos);
+               wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d",
+                          tls_msg_len);
+               if (data->tls_in_left == 0) {
+                       data->tls_in_total = tls_msg_len;
+                       data->tls_in_left = tls_msg_len;
+                       wpabuf_free(data->tls_in);
+                       data->tls_in = NULL;
+               }
+               pos += 4;
+               left -= 4;
+
+               if (left > tls_msg_len) {
+                       wpa_printf(MSG_INFO, "SSL: TLS Message Length (%d "
+                                  "bytes) smaller than this fragment (%d "
+                                  "bytes)", (int) tls_msg_len, (int) left);
+                       ret->ignore = TRUE;
+                       return NULL;
+               }
+       }
+
+       ret->ignore = FALSE;
+       ret->methodState = METHOD_MAY_CONT;
+       ret->decision = DECISION_FAIL;
+       ret->allowNotifications = TRUE;
+
+       *len = left;
+       return pos;
+}
+
+
+/**
+ * eap_peer_tls_reset_input - Reset input buffers
+ * @data: Data for TLS processing
+ *
+ * This function frees any allocated memory for input buffers and resets input
+ * state.
+ */
+void eap_peer_tls_reset_input(struct eap_ssl_data *data)
+{
+       data->tls_in_left = data->tls_in_total = 0;
+       wpabuf_free(data->tls_in);
+       data->tls_in = NULL;
+}
+
+
+/**
+ * eap_peer_tls_reset_output - Reset output buffers
+ * @data: Data for TLS processing
+ *
+ * This function frees any allocated memory for output buffers and resets
+ * output state.
+ */
+void eap_peer_tls_reset_output(struct eap_ssl_data *data)
+{
+       data->tls_out_pos = 0;
+       wpabuf_free(data->tls_out);
+       data->tls_out = NULL;
+}
+
+
+/**
+ * eap_peer_tls_decrypt - Decrypt received phase 2 TLS message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @in_data: Message received from the server
+ * @in_decrypted: Buffer for returning a pointer to the decrypted message
+ * Returns: 0 on success, 1 if more input data is needed, or -1 on failure
+ */
+int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+                        const struct wpabuf *in_data,
+                        struct wpabuf **in_decrypted)
+{
+       const struct wpabuf *msg;
+       int need_more_input;
+
+       msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input);
+       if (msg == NULL)
+               return need_more_input ? 1 : -1;
+
+       *in_decrypted = tls_connection_decrypt(data->ssl_ctx, data->conn, msg);
+       eap_peer_tls_reset_input(data);
+       if (*in_decrypted == NULL) {
+               wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data");
+               return -1;
+       }
+       return 0;
+}
+
+
+/**
+ * eap_peer_tls_encrypt - Encrypt phase 2 TLS message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * @id: EAP identifier for the response
+ * @in_data: Plaintext phase 2 data to encrypt or %NULL to continue fragments
+ * @out_data: Buffer for returning a pointer to the encrypted response message
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+                        EapType eap_type, int peap_version, u8 id,
+                        const struct wpabuf *in_data,
+                        struct wpabuf **out_data)
+{
+       if (in_data) {
+               eap_peer_tls_reset_output(data);
+               data->tls_out = tls_connection_encrypt(data->ssl_ctx,
+                                                      data->conn, in_data);
+               if (data->tls_out == NULL) {
+                       wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 "
+                                  "data (in_len=%lu)",
+                                  (unsigned long) wpabuf_len(in_data));
+                       eap_peer_tls_reset_output(data);
+                       return -1;
+               }
+       }
+
+       return eap_tls_process_output(data, eap_type, peap_version, id, 0,
+                                     out_data);
+}
+
+/**
+ * eap_peer_select_phase2_methods - Select phase 2 EAP method
+ * @config: Pointer to the network configuration
+ * @prefix: 'phase2' configuration prefix, e.g., "auth="
+ * @types: Buffer for returning allocated list of allowed EAP methods
+ * @num_types: Buffer for returning number of allocated EAP methods
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to parse EAP method list and select allowed methods
+ * for Phase2 authentication.
+ */
+int eap_peer_select_phase2_methods(struct eap_peer_config *config,
+                                  const char *prefix,
+                                  struct eap_method_type **types,
+                                  size_t *num_types)
+{
+       char *start, *pos, *buf;
+       struct eap_method_type *methods = NULL, *_methods;
+       u8 method;
+       size_t num_methods = 0, prefix_len;
+
+       if (config == NULL || config->phase2 == NULL)
+               goto get_defaults;
+
+       start = buf = os_strdup(config->phase2);
+       if (buf == NULL)
+               return -1;
+
+       prefix_len = os_strlen(prefix);
+
+       while (start && *start != '\0') {
+               int vendor;
+               pos = os_strstr(start, prefix);
+               if (pos == NULL)
+                       break;
+               if (start != pos && *(pos - 1) != ' ') {
+                       start = pos + prefix_len;
+                       continue;
+               }
+
+               start = pos + prefix_len;
+               pos = (char *)os_strchr(start, ' ');
+               if (pos)
+                       *pos++ = '\0';
+               method = eap_get_phase2_type(start, &vendor);
+               if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_NONE) {
+                       wpa_printf(MSG_INFO, "TLS: Unsupported Phase2 EAP "
+                                 "method '%s'\n", start);
+               } else {
+                       num_methods++;
+                       _methods = (struct eap_method_type *)os_realloc(methods,
+                                               num_methods * sizeof(*methods));
+                       if (_methods == NULL) {
+                               os_free(methods);
+                               os_free(buf);
+                               return -1;
+                       }
+                       methods = _methods;
+                       methods[num_methods - 1].vendor = vendor;
+                       methods[num_methods - 1].method = method;
+               }
+
+               start = pos;
+       }
+
+       os_free(buf);
+
+get_defaults:
+       if (methods == NULL)
+               methods = eap_get_phase2_types(config, &num_methods);
+       if (methods == NULL) {
+               wpa_printf(MSG_ERROR, "TLS: No Phase EAP methods available\n");
+               return -1;
+       }
+       wpa_hexdump(MSG_DEBUG, "TLS: Phase2 EAP types",
+                   (u8 *) methods,
+                   num_methods * sizeof(struct eap_method_type));
+
+       *types = methods;
+       *num_types = num_methods;
+
+       return 0;
+}
+
+/**
+ * eap_peer_tls_phase2_nak - Generate EAP-Nak for Phase 2
+ * @types: Buffer for returning allocated list of allowed EAP methods
+ * @num_types: Buffer for returning number of allocated EAP methods
+ * @hdr: EAP-Request header (and the following EAP type octet)
+ * @resp: Buffer for returning the EAP-Nak message
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types,
+                           struct eap_hdr *hdr, struct wpabuf **resp)
+{
+       u8 *pos = (u8 *) (hdr + 1);
+       size_t i;
+
+       /* TODO: add support for expanded Nak */
+       wpa_printf(MSG_DEBUG, "TLS: Phase Request: Nak type=%d\n", *pos);
+       wpa_hexdump(MSG_DEBUG, "TLS: Allowed Phase2 EAP types",
+                   (u8 *) types, num_types * sizeof(struct eap_method_type));
+       *resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, num_types,
+                             EAP_CODE_RESPONSE, hdr->identifier);
+       if (*resp == NULL)
+               return -1;
+
+       for (i = 0; i < num_types; i++) {
+               if (types[i].vendor == EAP_VENDOR_IETF &&
+                   types[i].method < 256)
+                       wpabuf_put_u8(*resp, types[i].method);
+       }
+
+       eap_update_len(*resp);
+
+       return 0;
+}
diff --git a/components/wpa_supplicant/src/wpa2/eap_peer/eap_ttls.c b/components/wpa_supplicant/src/wpa2/eap_peer/eap_ttls.c
new file mode 100644 (file)
index 0000000..6e8984b
--- /dev/null
@@ -0,0 +1,1673 @@
+/*\r
+ * EAP peer method: EAP-TTLS (RFC 5281)\r
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>\r
+ *\r
+ * This software may be distributed under the terms of the BSD license.\r
+ * See README for more details.\r
+ */\r
+\r
+#ifdef EAP_TTLS\r
+\r
+#include "wpa/includes.h"\r
+\r
+#include "wpa/common.h"\r
+#include "crypto/ms_funcs.h"\r
+#include "crypto/sha1.h"\r
+#include "wpa2/tls/tls.h"\r
+//#include "eap_common/chap.h"\r
+#include "wpa2/eap_peer/eap.h"\r
+#include "wpa2/eap_peer/eap_ttls.h"\r
+#include "wpa2/eap_peer/mschapv2.h"\r
+//#include "wpa2/eap_peer/chap.h"\r
+#include "wpa2/eap_peer/eap_i.h"\r
+#include "wpa2/eap_peer/eap_tls_common.h"\r
+#include "wpa2/eap_peer/eap_config.h"\r
+#include "wpa2/eap_peer/eap_methods.h"\r
+\r
+\r
+#define EAP_TTLS_VERSION 0\r
+\r
+static void eap_ttls_deinit(struct eap_sm *sm, void *priv);\r
+\r
+struct eap_ttls_data {\r
+       struct eap_ssl_data ssl;\r
+\r
+       int ttls_version;\r
+\r
+       const struct eap_method *phase2_method;\r
+       void *phase2_priv;\r
+       int phase2_success;\r
+       int phase2_start;\r
+\r
+       enum phase2_types {\r
+               EAP_TTLS_PHASE2_EAP,\r
+               EAP_TTLS_PHASE2_MSCHAPV2,\r
+               EAP_TTLS_PHASE2_MSCHAP,\r
+               EAP_TTLS_PHASE2_PAP,\r
+               EAP_TTLS_PHASE2_CHAP\r
+       } phase2_type;\r
+       struct eap_method_type phase2_eap_type;\r
+       struct eap_method_type *phase2_eap_types;\r
+       size_t num_phase2_eap_types;\r
+\r
+       u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN];\r
+       int auth_response_valid;\r
+       u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; /* MSCHAPv2 master key */\r
+       u8 ident;\r
+       int resuming; /* starting a resumed session */\r
+       int reauth; /* reauthentication */\r
+       u8 *key_data;\r
+       u8 *session_id;\r
+       size_t id_len;\r
+\r
+       struct wpabuf *pending_phase2_req;\r
+\r
+#ifdef EAP_TNC\r
+       int ready_for_tnc;\r
+       int tnc_started;\r
+#endif /* EAP_TNC */\r
+};\r
+\r
+\r
+static void * eap_ttls_init(struct eap_sm *sm)\r
+{\r
+       struct eap_ttls_data *data;\r
+       struct eap_peer_config *config = eap_get_config(sm);\r
+       //char *selected;\r
+\r
+       data = (struct eap_ttls_data *)os_zalloc(sizeof(*data));\r
+       if (data == NULL)\r
+               return NULL;\r
+       data->ttls_version = EAP_TTLS_VERSION;\r
+       data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2;\r
+       \r
+/*     \r
+    selected = "MSCHAPV2";\r
+       //TODO: Now only support EAP-TTLS/MSCHAPV2\r
+       if (config && config->phase2) {\r
+               if (os_strstr(config->phase2, "autheap=")) {\r
+                       selected = "EAP";\r
+                       data->phase2_type = EAP_TTLS_PHASE2_EAP;\r
+               } else if (os_strstr(config->phase2, "auth=MSCHAPV2")) {\r
+                       selected = "MSCHAPV2";\r
+                       data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2;\r
+               } else if (os_strstr(config->phase2, "auth=MSCHAP")) {\r
+                       selected = "MSCHAP";\r
+                       data->phase2_type = EAP_TTLS_PHASE2_MSCHAP;\r
+               } else if (os_strstr(config->phase2, "auth=PAP")) {\r
+                       selected = "PAP";\r
+                       data->phase2_type = EAP_TTLS_PHASE2_PAP;\r
+               } else if (os_strstr(config->phase2, "auth=CHAP")) {\r
+                       selected = "CHAP";\r
+                       data->phase2_type = EAP_TTLS_PHASE2_CHAP;\r
+               }\r
+       }\r
+    wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected);\r
+\r
+       if (data->phase2_type == EAP_TTLS_PHASE2_EAP) {\r
+               if (eap_peer_select_phase2_methods(config, "autheap=",\r
+                                                  &data->phase2_eap_types,\r
+                                                  &data->num_phase2_eap_types)\r
+                   < 0) {\r
+                       eap_ttls_deinit(sm, data);\r
+                       return NULL;\r
+               }\r
+\r
+               data->phase2_eap_type.vendor = EAP_VENDOR_IETF;\r
+               data->phase2_eap_type.method = EAP_TYPE_NONE;\r
+       }\r
+*/\r
+\r
+       if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TTLS)) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to initialize SSL.\n");\r
+               eap_ttls_deinit(sm, data);\r
+               return NULL;\r
+       }\r
+\r
+       return data;\r
+}\r
+\r
+static void eap_ttls_phase2_eap_deinit(struct eap_sm *sm,\r
+                          struct eap_ttls_data *data)\r
+{\r
+       if (data->phase2_priv && data->phase2_method) {\r
+               data->phase2_method->deinit(sm, data->phase2_priv);\r
+               data->phase2_method = NULL;\r
+               data->phase2_priv = NULL;\r
+       }\r
+}\r
+\r
+static void eap_ttls_deinit(struct eap_sm *sm, void *priv)\r
+{\r
+       struct eap_ttls_data *data = priv;\r
+       if (data == NULL)\r
+               return;\r
+       eap_ttls_phase2_eap_deinit(sm, data);\r
+       os_free(data->phase2_eap_types);\r
+       eap_peer_tls_ssl_deinit(sm, &data->ssl);\r
+       os_free(data->key_data);\r
+       os_free(data->session_id);\r
+       wpabuf_free(data->pending_phase2_req);\r
+       os_free(data);\r
+}\r
+\r
+static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id,\r
+                int mandatory, size_t len)\r
+{\r
+       struct ttls_avp_vendor *avp;\r
+       u8 flags;\r
+       size_t hdrlen;\r
+\r
+       avp = (struct ttls_avp_vendor *) avphdr;\r
+       flags = mandatory ? AVP_FLAGS_MANDATORY : 0;\r
+       if (vendor_id) {\r
+               flags |= AVP_FLAGS_VENDOR;\r
+               hdrlen = sizeof(*avp);\r
+               avp->vendor_id = host_to_be32(vendor_id);\r
+       } else {\r
+               hdrlen = sizeof(struct ttls_avp);\r
+       }\r
+\r
+       avp->avp_code = host_to_be32(avp_code);\r
+       avp->avp_length = host_to_be32((flags << 24) | (u32) (hdrlen + len));\r
+\r
+       return avphdr + hdrlen;\r
+}\r
+\r
+\r
+static u8 * eap_ttls_avp_add(u8 *start, u8 *avphdr, u32 avp_code,\r
+                u32 vendor_id, int mandatory,\r
+                const u8 *data, size_t len)\r
+{\r
+       u8 *pos;\r
+       pos = eap_ttls_avp_hdr(avphdr, avp_code, vendor_id, mandatory, len);\r
+       os_memcpy(pos, data, len);\r
+       pos += len;\r
+       AVP_PAD(start, pos);\r
+       return pos;\r
+}\r
+\r
+#if 0\r
+static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code,\r
+                        int mandatory)\r
+{\r
+       struct wpabuf *msg;\r
+       u8 *avp, *pos;\r
+\r
+       msg = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(*resp) + 4);\r
+       if (msg == NULL) {\r
+               wpabuf_free(*resp);\r
+               *resp = NULL;\r
+               return -1;\r
+       }\r
+\r
+       avp = wpabuf_mhead(msg);\r
+       pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, wpabuf_len(*resp));\r
+       os_memcpy(pos, wpabuf_head(*resp), wpabuf_len(*resp));\r
+       pos += wpabuf_len(*resp);\r
+       AVP_PAD(avp, pos);\r
+       wpabuf_free(*resp);\r
+       wpabuf_put(msg, pos - avp);\r
+       *resp = msg;\r
+       return 0;\r
+}\r
+#endif\r
+\r
+static int eap_ttls_v0_derive_key(struct eap_sm *sm,\r
+                      struct eap_ttls_data *data)\r
+{\r
+       os_free(data->key_data);\r
+       data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,\r
+                                                "ttls keying material",\r
+                                                EAP_TLS_KEY_LEN);\r
+       if (!data->key_data) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to derive key\n");\r
+               return -1;\r
+       }\r
+\r
+       os_free(data->session_id);\r
+       data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl,\r
+                                                         EAP_TYPE_TTLS,\r
+                                                         &data->id_len);\r
+       if (data->session_id) {\r
+       } else {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to derive Session-Id\n");\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+\r
+static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,\r
+                           struct eap_ttls_data *data, size_t len)\r
+{\r
+       return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len);\r
+}\r
+\r
+#if 0\r
+static void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data,\r
+                                             u8 method)\r
+{\r
+       size_t i;\r
+       for (i = 0; i < data->num_phase2_eap_types; i++) {\r
+               if (data->phase2_eap_types[i].vendor != EAP_VENDOR_IETF ||\r
+                   data->phase2_eap_types[i].method != method)\r
+                       continue;\r
+\r
+               data->phase2_eap_type.vendor =\r
+                       data->phase2_eap_types[i].vendor;\r
+               data->phase2_eap_type.method =\r
+                       data->phase2_eap_types[i].method;\r
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected "\r
+                         "Phase 2 EAP vendor %d method %d\n",\r
+                         data->phase2_eap_type.vendor,\r
+                         data->phase2_eap_type.method);\r
+               break;\r
+       }\r
+}\r
+\r
+static int eap_ttls_phase2_eap_process(struct eap_sm *sm,\r
+                                      struct eap_ttls_data *data,\r
+                                      struct eap_method_ret *ret,\r
+                                      struct eap_hdr *hdr, size_t len,\r
+                                      struct wpabuf **resp)\r
+{\r
+       struct wpabuf msg;\r
+       struct eap_method_ret iret;\r
+\r
+       os_memset(&iret, 0, sizeof(iret));\r
+       wpabuf_set(&msg, hdr, len);\r
+       *resp = data->phase2_method->process(sm, data->phase2_priv, &iret,\r
+                                            &msg);\r
+       if ((iret.methodState == METHOD_DONE ||\r
+            iret.methodState == METHOD_MAY_CONT) &&\r
+           (iret.decision == DECISION_UNCOND_SUCC ||\r
+            iret.decision == DECISION_COND_SUCC ||\r
+            iret.decision == DECISION_FAIL)) {\r
+               ret->methodState = iret.methodState;\r
+               ret->decision = iret.decision;\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+\r
+static int eap_ttls_phase2_request_eap_method(struct eap_sm *sm,\r
+                                             struct eap_ttls_data *data,\r
+                                             struct eap_method_ret *ret,\r
+                                             struct eap_hdr *hdr, size_t len,\r
+                                             u8 method, struct wpabuf **resp)\r
+{\r
+#ifdef EAP_TNC\r
+       if (data->tnc_started && data->phase2_method &&\r
+           data->phase2_priv && method == EAP_TYPE_TNC &&\r
+           data->phase2_eap_type.method == EAP_TYPE_TNC)\r
+               return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len,\r
+                                                  resp);\r
+\r
+       if (data->ready_for_tnc && !data->tnc_started &&\r
+           method == EAP_TYPE_TNC) {\r
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed "\r
+                         "EAP method\n");\r
+               data->tnc_started = 1;\r
+       }\r
+\r
+       if (data->tnc_started) {\r
+               if (data->phase2_eap_type.vendor != EAP_VENDOR_IETF ||\r
+                   data->phase2_eap_type.method == EAP_TYPE_TNC) {\r
+                       wpa_printf(MSG_ERROR, "EAP-TTLS: Unexpected EAP "\r
+                                 "type %d for TNC\n", method);\r
+                       return -1;\r
+               }\r
+\r
+               data->phase2_eap_type.vendor = EAP_VENDOR_IETF;\r
+               data->phase2_eap_type.method = method;\r
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected "\r
+                         "Phase 2 EAP vendor %d method %d (TNC)\n",\r
+                         data->phase2_eap_type.vendor,\r
+                          data->phase2_eap_type.method);\r
+\r
+               if (data->phase2_type == EAP_TTLS_PHASE2_EAP)\r
+                       eap_ttls_phase2_eap_deinit(sm, data);\r
+       }\r
+#endif /* EAP_TNC */\r
+\r
+       if (data->phase2_eap_type.vendor == EAP_VENDOR_IETF &&\r
+           data->phase2_eap_type.method == EAP_TYPE_NONE)\r
+               eap_ttls_phase2_select_eap_method(data, method);\r
+\r
+       if (method != data->phase2_eap_type.method || method == EAP_TYPE_NONE)\r
+       {\r
+               return -1;\r
+               if (eap_peer_tls_phase2_nak(data->phase2_eap_types,\r
+                                           data->num_phase2_eap_types,\r
+                                           hdr, resp))\r
+                       return -1;\r
+               return 0;\r
+\r
+       }\r
+\r
+       if (data->phase2_priv == NULL) {\r
+               data->phase2_method = eap_peer_get_eap_method(\r
+                       EAP_VENDOR_IETF, method);\r
+               if (data->phase2_method) {\r
+                       sm->init_phase2 = 1;\r
+                       data->phase2_priv = data->phase2_method->init(sm);\r
+                       sm->init_phase2 = 0;\r
+               }\r
+       }\r
+       if (data->phase2_priv == NULL || data->phase2_method == NULL) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: failed to initialize "\r
+                         "Phase 2 EAP method %d\n", method);\r
+               return -1;\r
+       }\r
+\r
+       return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, resp);\r
+}\r
+\r
+#if 0\r
+static int eap_ttls_phase2_request_eap(struct eap_sm *sm,\r
+                                      struct eap_ttls_data *data,\r
+                                      struct eap_method_ret *ret,\r
+                                      struct eap_hdr *hdr,\r
+                                      struct wpabuf **resp)\r
+{\r
+       size_t len = be_to_host16(hdr->length);\r
+       u8 *pos;\r
+       struct eap_peer_config *config = eap_get_config(sm);\r
+\r
+       if (len <= sizeof(struct eap_hdr)) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: too short "\r
+                         "Phase 2 request (len=%lu)\n", (unsigned long) len);\r
+               return -1;\r
+       }\r
+       pos = (u8 *) (hdr + 1);\r
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP Request: type=%d\n", *pos);\r
+       switch (*pos) {\r
+       case EAP_TYPE_IDENTITY:\r
+               *resp = eap_sm_build_identity_resp(sm, hdr->identifier, 1);\r
+               break;\r
+       default:\r
+               if (eap_ttls_phase2_request_eap_method(sm, data, ret, hdr, len,\r
+                                                      *pos, resp) < 0)\r
+                       return -1;\r
+               break;\r
+       }\r
+\r
+       if (*resp == NULL &&\r
+           (config->pending_req_identity || config->pending_req_password ||\r
+            config->pending_req_otp)) {\r
+               return 0;\r
+       }\r
+\r
+       if (*resp == NULL)\r
+               return -1;\r
+\r
+       return eap_ttls_avp_encapsulate(resp, RADIUS_ATTR_EAP_MESSAGE, 1);\r
+}\r
+#endif\r
+#endif\r
+\r
+static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,\r
+                                struct eap_ttls_data *data,\r
+                                struct eap_method_ret *ret,\r
+                                struct wpabuf **resp)\r
+{\r
+#ifdef EAP_MSCHAPv2\r
+       struct wpabuf *msg;\r
+       u8 *buf, *pos, *challenge, *peer_challenge;\r
+       const u8 *identity, *password;\r
+       size_t identity_len, password_len;\r
+       int pwhash;\r
+\r
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 Request\n");\r
+\r
+       identity = eap_get_config_identity(sm, &identity_len);\r
+       password = eap_get_config_password2(sm, &password_len, &pwhash);\r
+       if (identity == NULL || password == NULL)\r
+               return -1;\r
+\r
+       msg = wpabuf_alloc(identity_len + 1000);\r
+       if (msg == NULL) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to allocate memory\n");\r
+               return -1;\r
+       }\r
+       pos = buf = wpabuf_mhead(msg);\r
+\r
+       /* User-Name */\r
+       pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,\r
+                              identity, identity_len);\r
+\r
+       /* MS-CHAP-Challenge */\r
+       challenge = eap_ttls_implicit_challenge(\r
+               sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1);\r
+       if (challenge == NULL) {\r
+               wpabuf_free(msg);\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "\r
+                         "implicit challenge\n");\r
+               return -1;\r
+       }\r
+\r
+       pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,\r
+                              RADIUS_VENDOR_ID_MICROSOFT, 1,\r
+                              challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);\r
+\r
+       /* MS-CHAP2-Response */\r
+       pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_RESPONSE,\r
+                              RADIUS_VENDOR_ID_MICROSOFT, 1,\r
+                              EAP_TTLS_MSCHAPV2_RESPONSE_LEN);\r
+       data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN];\r
+       *pos++ = data->ident;\r
+       *pos++ = 0; /* Flags */\r
+       if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) {\r
+               os_free(challenge);\r
+               wpabuf_free(msg);\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get "\r
+                         "random data for peer challenge\n");\r
+               return -1;\r
+       }\r
+       peer_challenge = pos;\r
+       pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;\r
+       os_memset(pos, 0, 8); /* Reserved, must be zero */\r
+       pos += 8;\r
+       if (mschapv2_derive_response(identity, identity_len, password,\r
+                                    password_len, pwhash, challenge,\r
+                                    peer_challenge, pos, data->auth_response,\r
+                                    data->master_key)) {\r
+               os_free(challenge);\r
+               wpabuf_free(msg);\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "\r
+                         "response\n");\r
+               return -1;\r
+       }\r
+       data->auth_response_valid = 1;\r
+\r
+       pos += 24;\r
+       os_free(challenge);\r
+       AVP_PAD(buf, pos);\r
+\r
+       wpabuf_put(msg, pos - buf);\r
+       *resp = msg;\r
+\r
+       if (sm->workaround) {\r
+               /* At least FreeRADIUS seems to be terminating\r
+                * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success\r
+                * packet. */\r
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: EAP workaround - "\r
+                         "allow success without tunneled response\n");\r
+               ret->methodState = METHOD_MAY_CONT;\r
+               ret->decision = DECISION_COND_SUCC;\r
+       }\r
+\r
+       return 0;\r
+#else /* EAP_MSCHAPv2 */\r
+    printf("[Debug] Set EEEEE \n");\r
+       wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build\n");\r
+       return -1;\r
+#endif /* EAP_MSCHAPv2 */\r
+}\r
+\r
+#if 0\r
+//only support MSCHAPv2\r
+static int eap_ttls_phase2_request_mschap(struct eap_sm *sm,\r
+                                         struct eap_ttls_data *data,\r
+                                         struct eap_method_ret *ret,\r
+                                         struct wpabuf **resp)\r
+{\r
+       struct wpabuf *msg;\r
+       u8 *buf, *pos, *challenge;\r
+       const u8 *identity, *password;\r
+       size_t identity_len, password_len;\r
+       int pwhash;\r
+\r
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAP Request\n");\r
+\r
+       identity = eap_get_config_identity(sm, &identity_len);\r
+       password = eap_get_config_password2(sm, &password_len, &pwhash);\r
+       if (identity == NULL || password == NULL)\r
+               return -1;\r
+\r
+       msg = wpabuf_alloc(identity_len + 1000);\r
+       if (msg == NULL) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to allocate memory\n");\r
+               return -1;\r
+       }\r
+       pos = buf = wpabuf_mhead(msg);\r
+\r
+       // User-Name\r
+       pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,\r
+                              identity, identity_len);\r
+\r
+       // MS-CHAP-Challenge\r
+       challenge = eap_ttls_implicit_challenge(\r
+               sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1);\r
+       if (challenge == NULL) {\r
+               wpabuf_free(msg);\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive "\r
+                         "implicit challenge\n");\r
+               return -1;\r
+       }\r
+\r
+       pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,\r
+                              RADIUS_VENDOR_ID_MICROSOFT, 1,\r
+                              challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN);\r
+\r
+       // MS-CHAP-Response\r
+       pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_RESPONSE,\r
+                              RADIUS_VENDOR_ID_MICROSOFT, 1,\r
+                              EAP_TTLS_MSCHAP_RESPONSE_LEN);\r
+       data->ident = challenge[EAP_TTLS_MSCHAP_CHALLENGE_LEN];\r
+       *pos++ = data->ident;\r
+       *pos++ = 1; // Flags: Use NT style passwords\r
+       os_memset(pos, 0, 24); // LM-Response\r
+       pos += 24;\r
+       if (pwhash) {\r
+               challenge_response(challenge, password, pos); // NT-Response\r
+       } else {\r
+               nt_challenge_response(challenge, password, password_len,\r
+                                     pos); // NT-Response\r
+       }\r
+       pos += 24;\r
+       os_free(challenge);\r
+       AVP_PAD(buf, pos);\r
+\r
+       wpabuf_put(msg, pos - buf);\r
+       *resp = msg;\r
+\r
+       // EAP-TTLS/MSCHAP does not provide tunneled success\r
+       // notification, so assume that Phase2 succeeds.\r
+       ret->methodState = METHOD_DONE;\r
+       ret->decision = DECISION_COND_SUCC;\r
+\r
+       return 0;\r
+}\r
+\r
+\r
+static int eap_ttls_phase2_request_pap(struct eap_sm *sm,\r
+                                      struct eap_ttls_data *data,\r
+                                      struct eap_method_ret *ret,\r
+                                      struct wpabuf **resp)\r
+{\r
+       struct wpabuf *msg;\r
+       u8 *buf, *pos;\r
+       size_t pad;\r
+       const u8 *identity, *password;\r
+       size_t identity_len, password_len;\r
+\r
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 PAP Request\n");\r
+\r
+       identity = eap_get_config_identity(sm, &identity_len);\r
+       password = eap_get_config_password(sm, &password_len);\r
+       if (identity == NULL || password == NULL)\r
+               return -1;\r
+\r
+       msg = wpabuf_alloc(identity_len + password_len + 100);\r
+       if (msg == NULL) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS/PAP: Failed to allocate memory\n");\r
+               return -1;\r
+       }\r
+       pos = buf = wpabuf_mhead(msg);\r
+\r
+       // User-Name\r
+       pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,\r
+                              identity, identity_len);\r
+\r
+       // User-Password; in RADIUS, this is encrypted, but EAP-TTLS encrypts\r
+       // the data, so no separate encryption is used in the AVP itself.\r
+       // However, the password is padded to obfuscate its length.\r
+       pad = password_len == 0 ? 16 : (16 - (password_len & 15)) & 15;\r
+       pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_USER_PASSWORD, 0, 1,\r
+                              password_len + pad);\r
+       os_memcpy(pos, password, password_len);\r
+       pos += password_len;\r
+       os_memset(pos, 0, pad);\r
+       pos += pad;\r
+       AVP_PAD(buf, pos);\r
+\r
+       wpabuf_put(msg, pos - buf);\r
+       *resp = msg;\r
+\r
+       // EAP-TTLS/PAP does not provide tunneled success notification,\r
+       // so assume that Phase2 succeeds.\r
+       ret->methodState = METHOD_DONE;\r
+       ret->decision = DECISION_COND_SUCC;\r
+\r
+       return 0;\r
+}\r
+\r
+\r
+static int eap_ttls_phase2_request_chap(struct eap_sm *sm,\r
+                                       struct eap_ttls_data *data,\r
+                                       struct eap_method_ret *ret,\r
+                                       struct wpabuf **resp)\r
+{\r
+       struct wpabuf *msg;\r
+       u8 *buf, *pos, *challenge;\r
+       const u8 *identity, *password;\r
+       size_t identity_len, password_len;\r
+\r
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 CHAP Request\n");\r
+\r
+       identity = eap_get_config_identity(sm, &identity_len);\r
+       password = eap_get_config_password(sm, &password_len);\r
+       if (identity == NULL || password == NULL)\r
+               return -1;\r
+\r
+       msg = wpabuf_alloc(identity_len + 1000);\r
+       if (msg == NULL) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to allocate memory\n");\r
+               return -1;\r
+       }\r
+       pos = buf = wpabuf_mhead(msg);\r
+\r
+       // User-Name\r
+       pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,\r
+                              identity, identity_len);\r
+\r
+       // CHAP-Challenge\r
+       challenge = eap_ttls_implicit_challenge(\r
+               sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1);\r
+       if (challenge == NULL) {\r
+               wpabuf_free(msg);\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive "\r
+                         "implicit challenge\n");\r
+               return -1;\r
+       }\r
+\r
+       pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_CHAP_CHALLENGE, 0, 1,\r
+                              challenge, EAP_TTLS_CHAP_CHALLENGE_LEN);\r
+\r
+       // CHAP-Password\r
+       pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_CHAP_PASSWORD, 0, 1,\r
+                              1 + EAP_TTLS_CHAP_PASSWORD_LEN);\r
+       data->ident = challenge[EAP_TTLS_CHAP_CHALLENGE_LEN];\r
+       *pos++ = data->ident;\r
+\r
+       // MD5(Ident + Password + Challenge)\r
+       chap_md5(data->ident, password, password_len, challenge,\r
+                EAP_TTLS_CHAP_CHALLENGE_LEN, pos);\r
+\r
+       pos += EAP_TTLS_CHAP_PASSWORD_LEN;\r
+       os_free(challenge);\r
+       AVP_PAD(buf, pos);\r
+\r
+       wpabuf_put(msg, pos - buf);\r
+       *resp = msg;\r
+\r
+       // EAP-TTLS/CHAP does not provide tunneled success\r
+       // notification, so assume that Phase2 succeeds.\r
+       ret->methodState = METHOD_DONE;\r
+       ret->decision = DECISION_COND_SUCC;\r
+\r
+       return 0;\r
+}\r
+#endif\r
+\r
+static int eap_ttls_phase2_request(struct eap_sm *sm,\r
+                       struct eap_ttls_data *data,\r
+                       struct eap_method_ret *ret,\r
+                       struct eap_hdr *hdr,\r
+                       struct wpabuf **resp)\r
+{\r
+       int res = 0;\r
+       size_t len;\r
+       enum phase2_types phase2_type = data->phase2_type;\r
+\r
+#ifdef EAP_TNC\r
+       if (data->tnc_started) {\r
+               printf("[debug] set phase2_type \n");\r
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Processing TNC\n");\r
+               phase2_type = EAP_TTLS_PHASE2_EAP;\r
+       }\r
+#endif /* EAP_TNC */\r
+\r
+       if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2) {\r
+               if (eap_get_config_identity(sm, &len) == NULL) {\r
+                       wpa_printf(MSG_ERROR, "EAP-TTLS: Identity not configured\n");\r
+                       printf("[Debug] Return because no identity  EAP_TTLS_PHASE2_MSCHAPV2\n");\r
+                       return 0;\r
+               }\r
+               if (eap_get_config_password(sm, &len) == NULL) {\r
+                       wpa_printf(MSG_ERROR, "EAP-TTLS: Password not configured\n");\r
+                       printf("[Debug] Return because no password  EAP_TTLS_PHASE2_MSCHAPV2\n");\r
+                       return 0;\r
+               }\r
+\r
+               res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp);\r
+       } else {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown type %d\n", phase2_type);\r
+               res = -1;\r
+       }\r
+\r
+/*     if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2 ||\r
+           phase2_type == EAP_TTLS_PHASE2_MSCHAP ||\r
+           phase2_type == EAP_TTLS_PHASE2_PAP ||\r
+           phase2_type == EAP_TTLS_PHASE2_CHAP) {\r
+               if (eap_get_config_identity(sm, &len) == NULL) {\r
+                       wpa_printf(MSG_ERROR, "EAP-TTLS: Identity not configured\n");\r
+                       //eap_sm_request_identity(sm);\r
+                       if (eap_get_config_password(sm, &len) == NULL);\r
+                       //      eap_sm_request_password(sm);\r
+            printf("[Debug] Return because no identity  EAP_TTLS_PHASE2_MSCHAPV2 EAP_TTLS_PHASE2_MSCHAP\n");\r
+                       return 0;\r
+               }\r
+\r
+               if (eap_get_config_password(sm, &len) == NULL) {\r
+                       wpa_printf(MSG_ERROR, "EAP-TTLS: Password not configured\n"); \r
+                       printf("[Debug] Return because no password  EAP_TTLS_PHASE2_MSCHAPV2 EAP_TTLS_PHASE2_MSCHAP\n");\r
+                       //eap_sm_request_password(sm);\r
+                       return 0;\r
+               }\r
+       }\r
+\r
+       switch (phase2_type) {\r
+       case EAP_TTLS_PHASE2_EAP:\r
+           printf("[Debug] EAP_TTLS_PHASE2_EAP typed \n");\r
+               res = eap_ttls_phase2_request_eap(sm, data, ret, hdr, resp);\r
+               break;\r
+       case EAP_TTLS_PHASE2_MSCHAPV2:\r
+           printf("[Debug] EAP_TTLS_PHASE2_MSCHAPV2 typed \n");\r
+               res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp);\r
+               break;\r
+       case EAP_TTLS_PHASE2_MSCHAP:\r
+           printf("[Debug] EAP_TTLS_PHASE2_MSCHAP typed \n");\r
+               res = eap_ttls_phase2_request_mschap(sm, data, ret, resp);\r
+               break;\r
+       case EAP_TTLS_PHASE2_PAP:\r
+           printf("[Debug] EAP_TTLS_PHASE2_PAP typed \n");\r
+               res = eap_ttls_phase2_request_pap(sm, data, ret, resp);\r
+               break;\r
+       case EAP_TTLS_PHASE2_CHAP:\r
+           printf("[Debug] EAP_TTLS_PHASE2_CHAP typed \n");\r
+               res = eap_ttls_phase2_request_chap(sm, data, ret, resp);\r
+               break;\r
+       default:\r
+           printf("[Debug] Default typed \n");\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown\n");\r
+               res = -1;\r
+               break;\r
+       }*/\r
+\r
+       if (res < 0) {\r
+               ret->methodState = METHOD_DONE;\r
+               ret->decision = DECISION_FAIL;\r
+       }\r
+\r
+       return res;\r
+}\r
+\r
+\r
+struct ttls_parse_avp {\r
+       u8 *mschapv2;\r
+       u8 *eapdata;\r
+       size_t eap_len;\r
+       int mschapv2_error;\r
+};\r
+\r
+\r
+static int eap_ttls_parse_attr_eap(const u8 *dpos, size_t dlen,\r
+                       struct ttls_parse_avp *parse)\r
+{\r
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message\n");\r
+       if (parse->eapdata == NULL) {\r
+               parse->eapdata = os_malloc(dlen);\r
+               if (parse->eapdata == NULL) {\r
+                       wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to allocate "\r
+                                 "memory for Phase 2 EAP data\n");\r
+                       return -1;\r
+               }\r
+               os_memcpy(parse->eapdata, dpos, dlen);\r
+               parse->eap_len = dlen;\r
+       } else {\r
+               u8 *neweap = (u8 *)os_realloc(parse->eapdata, parse->eap_len + dlen);\r
+               if (neweap == NULL) {\r
+                       wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to allocate "\r
+                                 "memory for Phase 2 EAP data\n");\r
+                       return -1;\r
+               }\r
+               os_memcpy(neweap + parse->eap_len, dpos, dlen);\r
+               parse->eapdata = neweap;\r
+               parse->eap_len += dlen;\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+\r
+static int eap_ttls_parse_avp(u8 *pos, size_t left,\r
+                  struct ttls_parse_avp *parse)\r
+{\r
+       struct ttls_avp *avp;\r
+       u32 avp_code, avp_length, vendor_id = 0;\r
+       u8 avp_flags, *dpos;\r
+       size_t dlen;\r
+\r
+       avp = (struct ttls_avp *) pos;\r
+       avp_code = be_to_host32(avp->avp_code);\r
+       avp_length = be_to_host32(avp->avp_length);\r
+       avp_flags = (avp_length >> 24) & 0xff;\r
+       avp_length &= 0xffffff;\r
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x "\r
+                 "length=%d\n", (int) avp_code, avp_flags,\r
+                 (int) avp_length);\r
+\r
+       if (avp_length > left) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: AVP overflow "\r
+                         "(len=%d, left=%lu) - dropped\n",\r
+                         (int) avp_length, (unsigned long) left);\r
+               return -1;\r
+       }\r
+\r
+       if (avp_length < sizeof(*avp)) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: Invalid AVP length %d\n",\r
+                         avp_length);\r
+               return -1;\r
+       }\r
+\r
+       dpos = (u8 *) (avp + 1);\r
+       dlen = avp_length - sizeof(*avp);\r
+       if (avp_flags & AVP_FLAGS_VENDOR) {\r
+               if (dlen < 4) {\r
+                       wpa_printf(MSG_ERROR, "EAP-TTLS: Vendor AVP underflow\n");\r
+                       return -1;\r
+               }\r
+               vendor_id = WPA_GET_BE32(dpos);\r
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d\n",\r
+                         (int) vendor_id);\r
+               dpos += 4;\r
+               dlen -= 4;\r
+       }\r
+\r
+       if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) {\r
+               if (eap_ttls_parse_attr_eap(dpos, dlen, parse) < 0)\r
+                       return -1;\r
+       } else if (vendor_id == 0 && avp_code == RADIUS_ATTR_REPLY_MESSAGE) {\r
+               /* This is an optional message that can be displayed to\r
+                * the user. */\r
+       } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&\r
+                  avp_code == RADIUS_ATTR_MS_CHAP2_SUCCESS) {\r
+               if (dlen != 43) {\r
+                       wpa_printf(MSG_ERROR, "EAP-TTLS: Unexpected "\r
+                                 "MS-CHAP2-Success length "\r
+                                 "(len=%lu, expected 43)\n",\r
+                                 (unsigned long) dlen);\r
+                       return -1;\r
+               }\r
+               parse->mschapv2 = dpos;\r
+       } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&\r
+                  avp_code == RADIUS_ATTR_MS_CHAP_ERROR) {\r
+               parse->mschapv2_error = 1;\r
+       } else if (avp_flags & AVP_FLAGS_MANDATORY) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: Unsupported mandatory AVP "\r
+                         "code %d vendor_id %d - dropped\n",\r
+                         (int) avp_code, (int) vendor_id);\r
+               return -1;\r
+       } else {\r
+               wpa_printf(MSG_INFO, "EAP-TTLS: Ignoring unsupported AVP "\r
+                         "code %d vendor_id %d\n",\r
+                         (int) avp_code, (int) vendor_id);\r
+       }\r
+\r
+       return avp_length;\r
+}\r
+\r
+\r
+static int eap_ttls_parse_avps(struct wpabuf *in_decrypted,\r
+                   struct ttls_parse_avp *parse)\r
+{\r
+       u8 *pos;\r
+       size_t left, pad;\r
+       int avp_length;\r
+\r
+       pos = wpabuf_mhead(in_decrypted);\r
+       left = wpabuf_len(in_decrypted);\r
+       if (left < sizeof(struct ttls_avp)) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: Too short Phase 2 AVP frame"\r
+                         " len=%lu expected %lu or more - dropped\n",\r
+                         (unsigned long) left,\r
+                         (unsigned long) sizeof(struct ttls_avp));\r
+               return -1;\r
+       }\r
+\r
+       /* Parse AVPs */\r
+       os_memset(parse, 0, sizeof(*parse));\r
+\r
+       while (left > 0) {\r
+               avp_length = eap_ttls_parse_avp(pos, left, parse);\r
+               if (avp_length < 0)\r
+                       return -1;\r
+\r
+               pad = (4 - (avp_length & 3)) & 3;\r
+               pos += avp_length + pad;\r
+               if (left < avp_length + pad)\r
+                       left = 0;\r
+               else\r
+                       left -= avp_length + pad;\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+\r
+static u8 * eap_ttls_fake_identity_request(void)\r
+{\r
+       struct eap_hdr *hdr;\r
+       u8 *buf;\r
+\r
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: empty data in beginning of "\r
+                 "Phase 2 - use fake EAP-Request Identity\n");\r
+       buf = os_malloc(sizeof(*hdr) + 1);\r
+       if (buf == NULL) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: failed to allocate "\r
+                         "memory for fake EAP-Identity Request\n");\r
+               return NULL;\r
+       }\r
+\r
+       hdr = (struct eap_hdr *) buf;\r
+       hdr->code = EAP_CODE_REQUEST;\r
+       hdr->identifier = 0;\r
+       hdr->length = host_to_be16(sizeof(*hdr) + 1);\r
+       buf[sizeof(*hdr)] = EAP_TYPE_IDENTITY;\r
+\r
+       return buf;\r
+}\r
+\r
+\r
+static int eap_ttls_encrypt_response(struct eap_sm *sm,\r
+                         struct eap_ttls_data *data,\r
+                         struct wpabuf *resp, u8 identifier,\r
+                         struct wpabuf **out_data)\r
+{\r
+       if (resp == NULL)\r
+               return 0;\r
+\r
+       if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS,\r
+                                data->ttls_version, identifier,\r
+                                resp, out_data)) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to encrypt a Phase 2 frame\n");\r
+               return -1;\r
+       }\r
+       wpabuf_free(resp);\r
+\r
+       return 0;\r
+}\r
+\r
+#if 0\r
+static int eap_ttls_process_phase2_eap(struct eap_sm *sm,\r
+                                      struct eap_ttls_data *data,\r
+                                      struct eap_method_ret *ret,\r
+                                      struct ttls_parse_avp *parse,\r
+                                      struct wpabuf **resp)\r
+{\r
+       struct eap_hdr *hdr;\r
+       size_t len;\r
+\r
+       if (parse->eapdata == NULL) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: No EAP Message in the "\r
+                         "packet - dropped\n");\r
+               return -1;\r
+       }\r
+\r
+       hdr = (struct eap_hdr *) parse->eapdata;\r
+\r
+       if (parse->eap_len < sizeof(*hdr)) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: Too short Phase 2 EAP "\r
+                         "frame (len=%lu, expected %lu or more) - dropped\n",\r
+                         (unsigned long) parse->eap_len,\r
+                         (unsigned long) sizeof(*hdr));\r
+               return -1;\r
+       }\r
+       len = be_to_host16(hdr->length);\r
+       if (len > parse->eap_len) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: Length mismatch in Phase 2 "\r
+                         "EAP frame (EAP hdr len=%lu, EAP data len in "\r
+                         "AVP=%lu)\n",\r
+                         (unsigned long) len,\r
+                         (unsigned long) parse->eap_len);\r
+               return -1;\r
+       }\r
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: received Phase 2: code=%d "\r
+                 "identifier=%d length=%lu\n",\r
+                 hdr->code, hdr->identifier, (unsigned long) len);\r
+       switch (hdr->code) {\r
+       case EAP_CODE_REQUEST:\r
+               if (eap_ttls_phase2_request(sm, data, ret, hdr, resp)) {\r
+                       wpa_printf(MSG_ERROR, "EAP-TTLS: Phase2 Request "\r
+                                 "processing failed\n");\r
+                       return -1;\r
+               }\r
+               break;\r
+       default:\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: Unexpected code=%d in "\r
+                         "Phase 2 EAP header\n", hdr->code);\r
+               return -1;\r
+       }\r
+\r
+       return 0;\r
+}\r
+#endif\r
+\r
+static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,\r
+                                struct eap_ttls_data *data,\r
+                                struct eap_method_ret *ret,\r
+                                struct ttls_parse_avp *parse)\r
+{\r
+#ifdef EAP_MSCHAPv2\r
+       if (parse->mschapv2_error) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Received "\r
+                         "MS-CHAP-Error - failed\n");\r
+               ret->methodState = METHOD_DONE;\r
+               ret->decision = DECISION_FAIL;\r
+               /* Reply with empty data to ACK error */\r
+               return 1;\r
+       }\r
+\r
+       if (parse->mschapv2 == NULL) {\r
+#ifdef EAP_TNC\r
+               if (data->phase2_success && parse->eapdata) {\r
+                       /*\r
+                        * Allow EAP-TNC to be started after successfully\r
+                        * completed MSCHAPV2.\r
+                        */\r
+                       return 1;\r
+               }\r
+#endif /* EAP_TNC */\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: no MS-CHAP2-Success AVP "\r
+                         "received for Phase2 MSCHAPV2\n");\r
+               return -1;\r
+       }\r
+       if (parse->mschapv2[0] != data->ident) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: Ident mismatch for Phase 2 "\r
+                         "MSCHAPV2 (received Ident 0x%02x, expected 0x%02x)\n",\r
+                         parse->mschapv2[0], data->ident);\r
+               return -1;\r
+       }\r
+       if (!data->auth_response_valid ||\r
+           mschapv2_verify_auth_response(data->auth_response,\r
+                                         parse->mschapv2 + 1, 42)) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: Invalid authenticator "\r
+                         "response in Phase 2 MSCHAPV2 success request\n");\r
+               return -1;\r
+       }\r
+\r
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 "\r
+                 "authentication succeeded\n");\r
+       ret->methodState = METHOD_DONE;\r
+       ret->decision = DECISION_UNCOND_SUCC;\r
+       data->phase2_success = 1;\r
+\r
+       /*\r
+        * Reply with empty data; authentication server will reply\r
+        * with EAP-Success after this.\r
+        */\r
+       return 1;\r
+#else /* EAP_MSCHAPv2 */\r
+       wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build\n");\r
+       return -1;\r
+#endif /* EAP_MSCHAPv2 */\r
+}\r
+\r
+\r
+#ifdef EAP_TNC\r
+static int eap_ttls_process_tnc_start(struct eap_sm *sm,\r
+                                     struct eap_ttls_data *data,\r
+                                     struct eap_method_ret *ret,\r
+                                     struct ttls_parse_avp *parse,\r
+                                     struct wpabuf **resp)\r
+{\r
+       /* TNC uses inner EAP method after non-EAP TTLS phase 2. */\r
+       if (parse->eapdata == NULL) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 received "\r
+                         "unexpected tunneled data (no EAP)\n");\r
+               return -1;\r
+       }\r
+\r
+       if (!data->ready_for_tnc) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 received "\r
+                         "EAP after non-EAP, but not ready for TNC\n");\r
+               return -1;\r
+       }\r
+\r
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed "\r
+                 "non-EAP method\n");\r
+       data->tnc_started = 1;\r
+\r
+       if (eap_ttls_process_phase2_eap(sm, data, ret, parse, resp) < 0)\r
+               return -1;\r
+\r
+       return 0;\r
+}\r
+#endif /* EAP_TNC */\r
+\r
+\r
+static int eap_ttls_process_decrypted(struct eap_sm *sm,\r
+                          struct eap_ttls_data *data,\r
+                          struct eap_method_ret *ret,\r
+                          u8 identifier,\r
+                          struct ttls_parse_avp *parse,\r
+                          struct wpabuf *in_decrypted,\r
+                          struct wpabuf **out_data)\r
+{\r
+       struct wpabuf *resp = NULL;\r
+       //struct eap_peer_config *config = eap_get_config(sm);\r
+       int res;\r
+       enum phase2_types phase2_type = data->phase2_type;\r
+\r
+#ifdef EAP_TNC\r
+       if (data->tnc_started)\r
+               phase2_type = EAP_TTLS_PHASE2_EAP;\r
+#endif /* EAP_TNC */\r
+\r
+       switch (phase2_type) {\r
+       case EAP_TTLS_PHASE2_MSCHAPV2:\r
+               res = eap_ttls_process_phase2_mschapv2(sm, data, ret, parse);\r
+#ifdef EAP_TNC\r
+               if (res == 1 && parse->eapdata && data->phase2_success) {\r
+                       /*\r
+                        * TNC may be required as the next\r
+                        * authentication method within the tunnel.\r
+                        */\r
+                       ret->methodState = METHOD_MAY_CONT;\r
+                       data->ready_for_tnc = 1;\r
+                       if (eap_ttls_process_tnc_start(sm, data, ret, parse,\r
+                                                      &resp) == 0)\r
+                               break;\r
+               }\r
+#endif /* EAP_TNC */\r
+               return res;\r
+/*     case EAP_TTLS_PHASE2_EAP:\r
+               if (eap_ttls_process_phase2_eap(sm, data, ret, parse, &resp) <\r
+                   0)\r
+                       return -1;\r
+               break;\r
+       case EAP_TTLS_PHASE2_MSCHAP:\r
+       case EAP_TTLS_PHASE2_PAP:\r
+       case EAP_TTLS_PHASE2_CHAP:\r
+#ifdef EAP_TNC\r
+               if (eap_ttls_process_tnc_start(sm, data, ret, parse, &resp) <\r
+                   0)\r
+                       return -1;\r
+               break;\r
+#else // EAP_TNC\r
+               // EAP-TTLS/{MSCHAP,PAP,CHAP} should not send any TLS tunneled\r
+               // requests to the supplicant\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 received unexpected "\r
+                         "tunneled data\n");\r
+               return -1;\r
+#endif // EAP_TNC\r
+*/\r
+       default:\r
+               return -1;\r
+       }\r
+\r
+       if (resp) {\r
+               if (eap_ttls_encrypt_response(sm, data, resp, identifier,\r
+                                             out_data) < 0)\r
+                       return -1;\r
+       } else {\r
+               wpabuf_free(data->pending_phase2_req);\r
+               data->pending_phase2_req = wpabuf_dup(in_decrypted);\r
+       }/* else if (config->pending_req_identity ||\r
+                  config->pending_req_password ||\r
+                  config->pending_req_otp ||\r
+                  config->pending_req_new_password) {\r
+               wpabuf_free(data->pending_phase2_req);\r
+               data->pending_phase2_req = wpabuf_dup(in_decrypted);\r
+       }*/\r
+\r
+       return 0;\r
+}\r
+\r
+\r
+static int eap_ttls_implicit_identity_request(struct eap_sm *sm,\r
+                                  struct eap_ttls_data *data,\r
+                                  struct eap_method_ret *ret,\r
+                                  u8 identifier,\r
+                                  struct wpabuf **out_data)\r
+{\r
+       int retval = 0;\r
+       struct eap_hdr *hdr;\r
+       struct wpabuf *resp;\r
+\r
+       hdr = (struct eap_hdr *) eap_ttls_fake_identity_request();\r
+       if (hdr == NULL) {\r
+               ret->methodState = METHOD_DONE;\r
+               ret->decision = DECISION_FAIL;\r
+               return -1;\r
+       }\r
+\r
+       resp = NULL;\r
+       if (eap_ttls_phase2_request(sm, data, ret, hdr, &resp)) {\r
+               wpa_printf(MSG_ERROR, "EAP-TTLS: Phase2 Request "\r
+                         "processing failed\n");\r
+               retval = -1;\r
+       } else {\r
+               //struct eap_peer_config *config = eap_get_config(sm);\r
+               if (resp == NULL) {/* &&\r
+                   (config->pending_req_identity ||\r
+                    config->pending_req_password ||\r
+                    config->pending_req_otp ||\r
+                    config->pending_req_new_password)) {*/\r
+                       /*\r
+                        * Use empty buffer to force implicit request\r
+                        * processing when EAP request is re-processed after\r
+                        * user input.\r
+                        */\r
+                       wpabuf_free(data->pending_phase2_req);\r
+                       data->pending_phase2_req = wpabuf_alloc(0);\r
+               }\r
+\r
+               retval = eap_ttls_encrypt_response(sm, data, resp, identifier,\r
+                                                  out_data);\r
+       }\r
+\r
+       os_free(hdr);\r
+\r
+       if (retval < 0) {\r
+               ret->methodState = METHOD_DONE;\r
+               ret->decision = DECISION_FAIL;\r
+       }\r
+\r
+       return retval;\r
+}\r
+\r
+\r
+static int eap_ttls_phase2_start(struct eap_sm *sm, struct eap_ttls_data *data,\r
+                     struct eap_method_ret *ret, u8 identifier,\r
+                     struct wpabuf **out_data)\r
+{\r
+       data->phase2_start = 0;\r
+\r
+       /*\r
+        * EAP-TTLS does not use Phase2 on fast re-auth; this must be done only\r
+        * if TLS part was indeed resuming a previous session. Most\r
+        * Authentication Servers terminate EAP-TTLS before reaching this\r
+        * point, but some do not. Make wpa_supplicant stop phase 2 here, if\r
+        * needed.\r
+        */\r
+       if (data->reauth &&\r
+           tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) {\r
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Session resumption - "\r
+                         "skip phase 2\n");\r
+               *out_data = eap_peer_tls_build_ack(identifier, EAP_TYPE_TTLS,\r
+                                                  data->ttls_version);\r
+               ret->methodState = METHOD_DONE;\r
+               ret->decision = DECISION_UNCOND_SUCC;\r
+               data->phase2_success = 1;\r
+               return 0;\r
+       }\r
+\r
+       return eap_ttls_implicit_identity_request(sm, data, ret, identifier,\r
+                                                 out_data);\r
+}\r
+\r
+\r
+static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data,\r
+                struct eap_method_ret *ret, u8 identifier,\r
+                const struct wpabuf *in_data,\r
+                struct wpabuf **out_data)\r
+{\r
+       struct wpabuf *in_decrypted = NULL;\r
+       int retval = 0;\r
+       struct ttls_parse_avp parse;\r
+\r
+       os_memset(&parse, 0, sizeof(parse));\r
+\r
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for"\r
+                 " Phase 2\n",\r
+                 in_data ? (unsigned long) wpabuf_len(in_data) : 0);\r
+\r
+       if (data->pending_phase2_req) {\r
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 request - "\r
+                         "skip decryption and use old data\n");\r
+               /* Clear TLS reassembly state. */\r
+               eap_peer_tls_reset_input(&data->ssl);\r
+\r
+               in_decrypted = data->pending_phase2_req;\r
+               data->pending_phase2_req = NULL;\r
+               if (wpabuf_len(in_decrypted) == 0) {\r
+                       wpabuf_free(in_decrypted);\r
+                       return eap_ttls_implicit_identity_request(\r
+                               sm, data, ret, identifier, out_data);\r
+               }\r
+               goto continue_req;\r
+       }\r
+\r
+       if ((in_data == NULL || wpabuf_len(in_data) == 0) &&\r
+           data->phase2_start) {\r
+               return eap_ttls_phase2_start(sm, data, ret, identifier,\r
+                                            out_data);\r
+       }\r
+\r
+       if (in_data == NULL || wpabuf_len(in_data) == 0) {\r
+               /* Received TLS ACK - requesting more fragments */\r
+               return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS,\r
+                                           data->ttls_version,\r
+                                           identifier, NULL, out_data);\r
+       }\r
+\r
+       retval = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);\r
+       if (retval)\r
+               goto done;\r
+\r
+continue_req:\r
+       data->phase2_start = 0;\r
+\r
+       if (eap_ttls_parse_avps(in_decrypted, &parse) < 0) {\r
+               retval = -1;\r
+               goto done;\r
+       }\r
+\r
+       retval = eap_ttls_process_decrypted(sm, data, ret, identifier,\r
+                                           &parse, in_decrypted, out_data);\r
+\r
+done:\r
+       wpabuf_free(in_decrypted);\r
+       os_free(parse.eapdata);\r
+\r
+       if (retval < 0) {\r
+               ret->methodState = METHOD_DONE;\r
+               ret->decision = DECISION_FAIL;\r
+       }\r
+\r
+       return retval;\r
+}\r
+\r
+\r
+static int eap_ttls_process_handshake(struct eap_sm *sm,\r
+                          struct eap_ttls_data *data,\r
+                          struct eap_method_ret *ret,\r
+                          u8 identifier,\r
+                          const u8 *in_data, size_t in_len,\r
+                          struct wpabuf **out_data)\r
+{\r
+       int res;\r
+\r
+       res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS,\r
+                                         data->ttls_version, identifier,\r
+                                         in_data, in_len, out_data);\r
+\r
+       if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {\r
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to "\r
+                         "Phase 2\n");\r
+               if (data->resuming) {\r
+                       wpa_printf(MSG_DEBUG, "EAP-TTLS: fast reauth - may "\r
+                                 "skip Phase 2\n");\r
+                       ret->decision = DECISION_COND_SUCC;\r
+                       ret->methodState = METHOD_MAY_CONT;\r
+               }\r
+               data->phase2_start = 1;\r
+               eap_ttls_v0_derive_key(sm, data);\r
+\r
+               if (*out_data == NULL || wpabuf_len(*out_data) == 0) {\r
+                       if (eap_ttls_decrypt(sm, data, ret, identifier,\r
+                                            NULL, out_data)) {\r
+                               wpa_printf(MSG_ERROR, "EAP-TTLS: "\r
+                                         "failed to process early "\r
+                                         "start for Phase 2\n");\r
+                       }\r
+                       res = 0;\r
+               }\r
+               data->resuming = 0;\r
+       }\r
+\r
+       if (res == 2) {\r
+               struct wpabuf msg;\r
+               /*\r
+                * Application data included in the handshake message.\r
+                */\r
+               wpabuf_free(data->pending_phase2_req);\r
+               data->pending_phase2_req = *out_data;\r
+               *out_data = NULL;\r
+               wpabuf_set(&msg, in_data, in_len);\r
+               res = eap_ttls_decrypt(sm, data, ret, identifier, &msg,\r
+                                      out_data);\r
+       }\r
+\r
+       return res;\r
+}\r
+\r
+\r
+static void eap_ttls_check_auth_status(struct eap_sm *sm, \r
+                          struct eap_ttls_data *data,\r
+                          struct eap_method_ret *ret)\r
+{\r
+       if (ret->methodState == METHOD_DONE) {\r
+               ret->allowNotifications = FALSE;\r
+               if (ret->decision == DECISION_UNCOND_SUCC ||\r
+                   ret->decision == DECISION_COND_SUCC) {\r
+                       wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "\r
+                                 "completed successfully");\r
+                       data->phase2_success = 1;\r
+#ifdef EAP_TNC\r
+                       if (!data->ready_for_tnc && !data->tnc_started) {\r
+                               /*\r
+                                * TNC may be required as the next\r
+                                * authentication method within the tunnel.\r
+                                */\r
+                               ret->methodState = METHOD_MAY_CONT;\r
+                               data->ready_for_tnc = 1;\r
+                       }\r
+#endif /* EAP_TNC */\r
+               }\r
+       } else if (ret->methodState == METHOD_MAY_CONT &&\r
+                  (ret->decision == DECISION_UNCOND_SUCC ||\r
+                   ret->decision == DECISION_COND_SUCC)) {\r
+                       wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "\r
+                                 "completed successfully (MAY_CONT)\n");\r
+                       data->phase2_success = 1;\r
+       }\r
+}\r
+\r
+\r
+static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv,\r
+                struct eap_method_ret *ret,\r
+                const struct wpabuf *reqData)\r
+{\r
+       size_t left;\r
+       int res;\r
+       u8 flags, id;\r
+       struct wpabuf *resp;\r
+       const u8 *pos;\r
+       struct eap_ttls_data *data = priv;\r
+\r
+       pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, ret,\r
+                                       reqData, &left, &flags);\r
+       if (pos == NULL)\r
+               return NULL;\r
+       id = eap_get_id(reqData);\r
+\r
+       if (flags & EAP_TLS_FLAGS_START) {\r
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own "\r
+                         "ver=%d)\n", flags & EAP_TLS_VERSION_MASK,\r
+                         data->ttls_version);\r
+\r
+               /* RFC 5281, Ch. 9.2:\r
+                * "This packet MAY contain additional information in the form\r
+                * of AVPs, which may provide useful hints to the client"\r
+                * For now, ignore any potential extra data.\r
+                */\r
+               left = 0;\r
+       }\r
+\r
+       resp = NULL;\r
+       if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&\r
+           !data->resuming) {\r
+               struct wpabuf msg;\r
+               wpabuf_set(&msg, pos, left);\r
+               res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp);\r
+       } else {\r
+               res = eap_ttls_process_handshake(sm, data, ret, id,\r
+                                                pos, left, &resp);\r
+       }\r
+\r
+       eap_ttls_check_auth_status(sm, data, ret);\r
+       /* FIX: what about res == -1? Could just move all error processing into\r
+        * the other functions and get rid of this res==1 case here. */\r
+       if (res == 1) {\r
+               wpabuf_free(resp);\r
+               return eap_peer_tls_build_ack(id, EAP_TYPE_TTLS,\r
+                                             data->ttls_version);\r
+       }\r
+\r
+       return resp;\r
+}\r
+\r
+\r
+static bool eap_ttls_has_reauth_data(struct eap_sm *sm, void *priv)\r
+{\r
+       struct eap_ttls_data *data = priv;\r
+       return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&\r
+               data->phase2_success;\r
+}\r
+\r
+\r
+static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv)\r
+{\r
+       struct eap_ttls_data *data = priv;\r
+       wpabuf_free(data->pending_phase2_req);\r
+       data->pending_phase2_req = NULL;\r
+#ifdef EAP_TNC\r
+       data->ready_for_tnc = 0;\r
+       data->tnc_started = 0;\r
+#endif /* EAP_TNC */\r
+}\r
+\r
+\r
+static void * eap_ttls_init_for_reauth(struct eap_sm *sm, void *priv)\r
+{\r
+       struct eap_ttls_data *data = priv;\r
+       os_free(data->key_data);\r
+       data->key_data = NULL;\r
+       os_free(data->session_id);\r
+       data->session_id = NULL;\r
+       if (eap_peer_tls_reauth_init(sm, &data->ssl)) {\r
+               os_free(data);\r
+               return NULL;\r
+       }\r
+       if (data->phase2_priv && data->phase2_method &&\r
+           data->phase2_method->init_for_reauth)\r
+               data->phase2_method->init_for_reauth(sm, data->phase2_priv);\r
+       data->phase2_start = 0;\r
+       data->phase2_success = 0;\r
+       data->resuming = 1;\r
+       data->reauth = 1;\r
+       return priv;\r
+}\r
+\r
+\r
+static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf,\r
+                   size_t buflen, int verbose)\r
+{\r
+       struct eap_ttls_data *data = priv;\r
+       int len, ret;\r
+\r
+       len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);\r
+       ret = snprintf(buf + len, buflen - len,\r
+                         "EAP-TTLSv%d Phase2 method=",\r
+                         data->ttls_version);\r
+       if (ret < 0 || (size_t) ret >= buflen - len)\r
+               return len;\r
+       len += ret;\r
+       switch (data->phase2_type) {\r
+       /*case EAP_TTLS_PHASE2_EAP:\r
+               ret = os_snprintf(buf + len, buflen - len, "EAP-%s\n",\r
+                                 data->phase2_method ?\r
+                                 data->phase2_method->name : "?");\r
+               break;*/\r
+       case EAP_TTLS_PHASE2_MSCHAPV2:\r
+               ret = snprintf(buf + len, buflen - len, "MSCHAPV2\n");\r
+               break;\r
+       /*case EAP_TTLS_PHASE2_MSCHAP:\r
+               ret = os_snprintf(buf + len, buflen - len, "MSCHAP\n");\r
+               break;\r
+       case EAP_TTLS_PHASE2_PAP:\r
+               ret = os_snprintf(buf + len, buflen - len, "PAP\n");\r
+               break;\r
+       case EAP_TTLS_PHASE2_CHAP:\r
+               ret = os_snprintf(buf + len, buflen - len, "CHAP\n");\r
+               break;*/\r
+       default:\r
+               ret = 0;\r
+               break;\r
+       }\r
+       if (ret < 0 || (size_t) ret >= buflen - len)\r
+               return len;\r
+       len += ret;\r
+\r
+       return len;\r
+}\r
+\r
+\r
+static bool eap_ttls_isKeyAvailable(struct eap_sm *sm, void *priv)\r
+{\r
+       struct eap_ttls_data *data = priv;\r
+       return data->key_data != NULL && data->phase2_success;\r
+}\r
+\r
+\r
+static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)\r
+{\r
+       struct eap_ttls_data *data = priv;\r
+       u8 *key;\r
+\r
+       if (data->key_data == NULL || !data->phase2_success)\r
+               return NULL;\r
+\r
+       key = os_malloc(EAP_TLS_KEY_LEN);\r
+       if (key == NULL)\r
+               return NULL;\r
+\r
+       *len = EAP_TLS_KEY_LEN;\r
+       os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);\r
+\r
+       return key;\r
+}\r
+\r
+\r
+static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)\r
+{\r
+       struct eap_ttls_data *data = priv;\r
+       u8 *id;\r
+\r
+       if (data->session_id == NULL || !data->phase2_success)\r
+               return NULL;\r
+\r
+       id = os_malloc(data->id_len);\r
+       if (id == NULL)\r
+               return NULL;\r
+\r
+       *len = data->id_len;\r
+       os_memcpy(id, data->session_id, data->id_len);\r
+\r
+       return id;\r
+}\r
+\r
+int eap_peer_ttls_register(void)\r
+{\r
+       struct eap_method *eap;\r
+       int ret;\r
+\r
+       eap = eap_peer_method_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS,\r
+                                   "TTLS");\r
+       if (eap == NULL)\r
+               return -1;\r
+\r
+       eap->init = eap_ttls_init;\r
+       eap->deinit = eap_ttls_deinit;\r
+       eap->process = eap_ttls_process;\r
+       eap->isKeyAvailable = eap_ttls_isKeyAvailable;\r
+       eap->getKey = eap_ttls_getKey;\r
+       eap->getSessionId = eap_ttls_get_session_id;\r
+       eap->get_status = eap_ttls_get_status;\r
+       eap->has_reauth_data = eap_ttls_has_reauth_data;\r
+       eap->deinit_for_reauth = eap_ttls_deinit_for_reauth;\r
+       eap->init_for_reauth = eap_ttls_init_for_reauth;\r
+\r
+       ret = eap_peer_method_register(eap);\r
+       if (ret)\r
+               eap_peer_method_free(eap);\r
+       return ret;\r
+}\r
+\r
+#endif /* EAP_TTLS */\r
diff --git a/components/wpa_supplicant/src/wpa2/eap_peer/mschapv2.c b/components/wpa_supplicant/src/wpa2/eap_peer/mschapv2.c
new file mode 100644 (file)
index 0000000..3335198
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * MSCHAPV2
+ */
+
+#ifdef EAP_MSCHAPv2
+
+#include "wpa/includes.h"
+#include "wpa/common.h"
+#include "crypto/ms_funcs.h"
+#include "wpa2/eap_peer/mschapv2.h"
+
+const u8 * mschapv2_remove_domain(const u8 *username, size_t *len)
+{
+       size_t i;
+
+       /*
+        * MSCHAPV2 does not include optional domain name in the
+        * challenge-response calculation, so remove domain prefix
+        * (if present)
+        */
+       for (i = 0; i < *len; i++) {
+               if (username[i] == '\\') {
+                       *len -= i + 1;
+                       return username + i + 1;
+               }
+       }
+
+       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 *username;
+       size_t username_len;
+       u8 password_hash[16], password_hash_hash[16];
+
+       username_len = identity_len;
+       username = mschapv2_remove_domain(identity, &username_len);
+
+       if (pwhash) {
+               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))
+                       return -1;
+       } else {
+               if (generate_nt_response(auth_challenge, peer_challenge,
+                                        username, username_len,
+                                        password, password_len,
+                                        nt_response) ||
+                   generate_authenticator_response(password, password_len,
+                                                   peer_challenge,
+                                                   auth_challenge,
+                                                   username, username_len,
+                                                   nt_response,
+                                                   auth_response))
+                       return -1;
+       }
+
+       if (pwhash) {
+               if (hash_nt_password_hash(password, password_hash_hash))
+                       return -1;
+       } else {
+               if (nt_password_hash(password, password_len, password_hash) ||
+                   hash_nt_password_hash(password_hash, password_hash_hash))
+                       return -1;
+       }
+       if (get_master_key(password_hash_hash, nt_response, master_key))
+               return -1;
+
+       return 0;
+}
+
+int mschapv2_verify_auth_response(const u8 *auth_response,
+                             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,
+                      MSCHAPV2_AUTH_RESPONSE_LEN) ||
+           os_memcmp(auth_response, recv_response,
+                     MSCHAPV2_AUTH_RESPONSE_LEN) != 0)
+               return -1;
+       return 0;
+}
+
+#endif /* EAP_MSCHAPv2 */
diff --git a/components/wpa_supplicant/src/wpa2/tls/asn1.c b/components/wpa_supplicant/src/wpa2/tls/asn1.c
new file mode 100644 (file)
index 0000000..ced8018
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * ASN.1 DER parsing
+ * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wpa2/tls/asn1.h"
+
+int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr)
+{
+       const u8 *pos, *end;
+       u8 tmp;
+
+       os_memset(hdr, 0, sizeof(*hdr));
+       pos = buf;
+       end = buf + len;
+
+       hdr->identifier = *pos++;
+       hdr->class = hdr->identifier >> 6;
+       hdr->constructed = !!(hdr->identifier & (1 << 5));
+
+       if ((hdr->identifier & 0x1f) == 0x1f) {
+               hdr->tag = 0;
+               do {
+                       if (pos >= end) {
+                               wpa_printf(MSG_DEBUG, "ASN.1: Identifier "
+                                          "underflow");
+                               return -1;
+                       }
+                       tmp = *pos++;
+                       wpa_printf(MSG_DEBUG, "ASN.1: Extended tag data: "
+                                  "0x%02x", tmp);
+                       hdr->tag = (hdr->tag << 7) | (tmp & 0x7f);
+               } while (tmp & 0x80);
+       } else
+               hdr->tag = hdr->identifier & 0x1f;
+
+       tmp = *pos++;
+       if (tmp & 0x80) {
+               if (tmp == 0xff) {
+                       wpa_printf(MSG_DEBUG, "ASN.1: Reserved length "
+                                  "value 0xff used");
+                       return -1;
+               }
+               tmp &= 0x7f; /* number of subsequent octets */
+               hdr->length = 0;
+               if (tmp > 4) {
+                       wpa_printf(MSG_DEBUG, "ASN.1: Too long length field");
+                       return -1;
+               }
+               while (tmp--) {
+                       if (pos >= end) {
+                               wpa_printf(MSG_DEBUG, "ASN.1: Length "
+                                          "underflow");
+                               return -1;
+                       }
+                       hdr->length = (hdr->length << 8) | *pos++;
+               }
+       } else {
+               /* Short form - length 0..127 in one octet */
+               hdr->length = tmp;
+       }
+
+       if (end < pos || hdr->length > (unsigned int) (end - pos)) {
+               wpa_printf(MSG_DEBUG, "ASN.1: Contents underflow");
+               return -1;
+       }
+
+       hdr->payload = pos;
+       return 0;
+}
+
+
+int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid)
+{
+       const u8 *pos, *end;
+       unsigned long val;
+       u8 tmp;
+
+       os_memset(oid, 0, sizeof(*oid));
+
+       pos = buf;
+       end = buf + len;
+
+       while (pos < end) {
+               val = 0;
+
+               do {
+                       if (pos >= end)
+                               return -1;
+                       tmp = *pos++;
+                       val = (val << 7) | (tmp & 0x7f);
+               } while (tmp & 0x80);
+
+               if (oid->len >= ASN1_MAX_OID_LEN) {
+                       wpa_printf(MSG_DEBUG, "ASN.1: Too long OID value");
+                       return -1;
+               }
+               if (oid->len == 0) {
+                       /*
+                        * The first octet encodes the first two object
+                        * identifier components in (X*40) + Y formula.
+                        * X = 0..2.
+                        */
+                       oid->oid[0] = val / 40;
+                       if (oid->oid[0] > 2)
+                               oid->oid[0] = 2;
+                       oid->oid[1] = val - oid->oid[0] * 40;
+                       oid->len = 2;
+               } else
+                       oid->oid[oid->len++] = val;
+       }
+
+       return 0;
+}
+
+
+int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid,
+                const u8 **next)
+{
+       struct asn1_hdr hdr;
+
+       if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0)
+               return -1;
+
+       if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OID) {
+               wpa_printf(MSG_DEBUG, "ASN.1: Expected OID - found class %d "
+                          "tag 0x%x", hdr.class, hdr.tag);
+               return -1;
+       }
+
+       *next = hdr.payload + hdr.length;
+
+       return asn1_parse_oid(hdr.payload, hdr.length, oid);
+}
+
+
+void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len)
+{
+       char *pos = buf;
+       size_t i;
+       int ret;
+
+       if (len == 0)
+               return;
+
+       buf[0] = '\0';
+
+       for (i = 0; i < oid->len; i++) {
+               //ret = os_snprintf(pos, buf + len - pos,
+               ret = sprintf(pos,
+                                 "%s%lu",
+                                 i == 0 ? "" : ".", oid->oid[i]);
+               if (ret < 0 || ret >= buf + len - pos)
+                       break;
+               pos += ret;
+       }
+       buf[len - 1] = '\0';
+}
+
+
+static u8 rotate_bits(u8 octet)
+{
+       int i;
+       u8 res;
+
+       res = 0;
+       for (i = 0; i < 8; i++) {
+               res <<= 1;
+               if (octet & 1)
+                       res |= 1;
+               octet >>= 1;
+       }
+
+       return res;
+}
+
+
+unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len)
+{
+       unsigned long val = 0;
+       const u8 *pos = buf;
+
+       /* BER requires that unused bits are zero, so we can ignore the number
+        * of unused bits */
+       pos++;
+
+       if (len >= 2)
+               val |= rotate_bits(*pos++);
+       if (len >= 3)
+               val |= ((unsigned long) rotate_bits(*pos++)) << 8;
+       if (len >= 4)
+               val |= ((unsigned long) rotate_bits(*pos++)) << 16;
+       if (len >= 5)
+               val |= ((unsigned long) rotate_bits(*pos++)) << 24;
+       if (len >= 6)
+               wpa_printf(MSG_DEBUG, "X509: %s - some bits ignored "
+                          "(BIT STRING length %lu)",
+                          __func__, (unsigned long) len);
+
+       return val;
+}
diff --git a/components/wpa_supplicant/src/wpa2/tls/bignum.c b/components/wpa_supplicant/src/wpa2/tls/bignum.c
new file mode 100644 (file)
index 0000000..aef8f95
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Big number math
+ * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "crypto/includes.h"
+#include "crypto/common.h"
+#include "wpa/wpabuf.h"
+#include "wpa/wpa_debug.h"
+#include "wpa2/tls/bignum.h"
+
+#define CONFIG_INTERNAL_LIBTOMMATH
+#ifdef CONFIG_INTERNAL_LIBTOMMATH
+#include "wpa2/tls/libtommath.h"
+#else /* CONFIG_INTERNAL_LIBTOMMATH */
+#include <tommath.h>
+#endif /* CONFIG_INTERNAL_LIBTOMMATH */
+
+
+/*
+ * The current version is just a wrapper for LibTomMath library, so
+ * struct bignum is just typecast to mp_int.
+ */
+
+/**
+ * bignum_init - Allocate memory for bignum
+ * Returns: Pointer to allocated bignum or %NULL on failure
+ */
+struct bignum *  
+bignum_init(void)
+{
+       struct bignum *n = (struct bignum *)os_zalloc(sizeof(mp_int));
+       if (n == NULL)
+               return NULL;
+       if (mp_init((mp_int *) n) != MP_OKAY) {
+               os_free(n);
+               n = NULL;
+       }
+       return n;
+}
+
+
+/**
+ * bignum_deinit - Free bignum
+ * @n: Bignum from bignum_init()
+ */
+void  
+bignum_deinit(struct bignum *n)
+{
+       if (n) {
+               mp_clear((mp_int *) n);
+               os_free(n);
+       }
+}
+
+
+/**
+ * bignum_get_unsigned_bin - Get length of bignum as an unsigned binary buffer
+ * @n: Bignum from bignum_init()
+ * Returns: Length of n if written to a binary buffer
+ */
+size_t  
+bignum_get_unsigned_bin_len(struct bignum *n)
+{
+       return mp_unsigned_bin_size((mp_int *) n);
+}
+
+
+/**
+ * bignum_get_unsigned_bin - Set binary buffer to unsigned bignum
+ * @n: Bignum from bignum_init()
+ * @buf: Buffer for the binary number
+ * @len: Length of the buffer, can be %NULL if buffer is known to be long
+ * enough. Set to used buffer length on success if not %NULL.
+ * Returns: 0 on success, -1 on failure
+ */
+int  
+bignum_get_unsigned_bin(const struct bignum *n, u8 *buf, size_t *len)
+{
+       size_t need = mp_unsigned_bin_size((mp_int *) n);
+       if (len && need > *len) {
+               *len = need;
+               return -1;
+       }
+       if (mp_to_unsigned_bin((mp_int *) n, buf) != MP_OKAY) {
+               wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+               return -1;
+       }
+       if (len)
+               *len = need;
+       return 0;
+}
+
+
+/**
+ * bignum_set_unsigned_bin - Set bignum based on unsigned binary buffer
+ * @n: Bignum from bignum_init(); to be set to the given value
+ * @buf: Buffer with unsigned binary value
+ * @len: Length of buf in octets
+ * Returns: 0 on success, -1 on failure
+ */
+int  
+bignum_set_unsigned_bin(struct bignum *n, const u8 *buf, size_t len)
+{
+       if (mp_read_unsigned_bin((mp_int *) n, (u8 *) buf, len) != MP_OKAY) {
+               wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+               return -1;
+       }
+       return 0;
+}
+
+
+/**
+ * bignum_cmp - Signed comparison
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int  
+bignum_cmp(const struct bignum *a, const struct bignum *b)
+{
+       return mp_cmp((mp_int *) a, (mp_int *) b);
+}
+
+
+/**
+ * bignum_cmd_d - Compare bignum to standard integer
+ * @a: Bignum from bignum_init()
+ * @b: Small integer
+ * Returns: 0 on success, -1 on failure
+ */
+int  
+bignum_cmp_d(const struct bignum *a, unsigned long b)
+{
+       return mp_cmp_d((mp_int *) a, b);
+}
+
+
+/**
+ * bignum_add - c = a + b
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); used to store the result of a + b
+ * Returns: 0 on success, -1 on failure
+ */
+int  
+bignum_add(const struct bignum *a, const struct bignum *b,
+              struct bignum *c)
+{
+       if (mp_add((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
+               wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+               return -1;
+       }
+       return 0;
+}
+
+
+/**
+ * bignum_sub - c = a - b
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); used to store the result of a - b
+ * Returns: 0 on success, -1 on failure
+ */
+int  
+bignum_sub(const struct bignum *a, const struct bignum *b,
+              struct bignum *c)
+{
+       if (mp_sub((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
+               wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+               return -1;
+       }
+       return 0;
+}
+
+
+/**
+ * bignum_mul - c = a * b
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); used to store the result of a * b
+ * Returns: 0 on success, -1 on failure
+ */
+int  
+bignum_mul(const struct bignum *a, const struct bignum *b,
+              struct bignum *c)
+{
+       if (mp_mul((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
+               wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+               return -1;
+       }
+       return 0;
+}
+
+
+/**
+ * bignum_mulmod - d = a * b (mod c)
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); modulus
+ * @d: Bignum from bignum_init(); used to store the result of a * b (mod c)
+ * Returns: 0 on success, -1 on failure
+ */
+int  
+bignum_mulmod(const struct bignum *a, const struct bignum *b,
+                 const struct bignum *c, struct bignum *d)
+{
+       if (mp_mulmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d)
+           != MP_OKAY) {
+               wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+               return -1;
+       }
+       return 0;
+}
+
+
+/**
+ * bignum_exptmod - Modular exponentiation: d = a^b (mod c)
+ * @a: Bignum from bignum_init(); base
+ * @b: Bignum from bignum_init(); exponent
+ * @c: Bignum from bignum_init(); modulus
+ * @d: Bignum from bignum_init(); used to store the result of a^b (mod c)
+ * Returns: 0 on success, -1 on failure
+ */
+int  
+bignum_exptmod(const struct bignum *a, const struct bignum *b,
+                  const struct bignum *c, struct bignum *d)
+{
+       if (mp_exptmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d)
+           != MP_OKAY) {
+               wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+               return -1;
+       }
+       return 0;
+}
diff --git a/components/wpa_supplicant/src/wpa2/tls/pkcs1.c b/components/wpa_supplicant/src/wpa2/tls/pkcs1.c
new file mode 100644 (file)
index 0000000..6266806
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * PKCS #1 (RSA Encryption)
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wpa2/tls/rsa.h"
+#include "wpa2/tls/pkcs1.h"
+
+
+static int pkcs1_generate_encryption_block(u8 block_type, size_t modlen,
+                                          const u8 *in, size_t inlen,
+                                          u8 *out, size_t *outlen)
+{
+       size_t ps_len;
+       u8 *pos;
+
+       /*
+        * PKCS #1 v1.5, 8.1:
+        *
+        * EB = 00 || BT || PS || 00 || D
+        * BT = 00 or 01 for private-key operation; 02 for public-key operation
+        * PS = k-3-||D||; at least eight octets
+        * (BT=0: PS=0x00, BT=1: PS=0xff, BT=2: PS=pseudorandom non-zero)
+        * k = length of modulus in octets (modlen)
+        */
+
+       if (modlen < 12 || modlen > *outlen || inlen > modlen - 11) {
+               wpa_printf(MSG_DEBUG, "PKCS #1: %s - Invalid buffer "
+                          "lengths (modlen=%lu outlen=%lu inlen=%lu)",
+                          __func__, (unsigned long) modlen,
+                          (unsigned long) *outlen,
+                          (unsigned long) inlen);
+               return -1;
+       }
+
+       pos = out;
+       *pos++ = 0x00;
+       *pos++ = block_type; /* BT */
+       ps_len = modlen - inlen - 3;
+       switch (block_type) {
+       case 0:
+               os_memset(pos, 0x00, ps_len);
+               pos += ps_len;
+               break;
+       case 1:
+               os_memset(pos, 0xff, ps_len);
+               pos += ps_len;
+               break;
+       case 2:
+               if (os_get_random(pos, ps_len) < 0) {
+                       wpa_printf(MSG_DEBUG, "PKCS #1: %s - Failed to get "
+                                  "random data for PS", __func__);
+                       return -1;
+               }
+               while (ps_len--) {
+                       if (*pos == 0x00)
+                               *pos = 0x01;
+                       pos++;
+               }
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "PKCS #1: %s - Unsupported block type "
+                          "%d", __func__, block_type);
+               return -1;
+       }
+       *pos++ = 0x00;
+       os_memcpy(pos, in, inlen); /* D */
+
+       return 0;
+}
+
+
+int pkcs1_encrypt(int block_type, struct crypto_rsa_key *key,
+                 int use_private, const u8 *in, size_t inlen,
+                 u8 *out, size_t *outlen)
+{
+       size_t modlen;
+
+       modlen = crypto_rsa_get_modulus_len(key);
+
+       if (pkcs1_generate_encryption_block(block_type, modlen, in, inlen,
+                                           out, outlen) < 0)
+               return -1;
+
+       return crypto_rsa_exptmod(out, modlen, out, outlen, key, use_private);
+}
+
+
+int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key,
+                                 const u8 *in, size_t inlen,
+                                 u8 *out, size_t *outlen)
+{
+       int res;
+       u8 *pos, *end;
+
+       res = crypto_rsa_exptmod(in, inlen, out, outlen, key, 1);
+       if (res)
+               return res;
+
+       if (*outlen < 2 || out[0] != 0 || out[1] != 2)
+               return -1;
+
+       /* Skip PS (pseudorandom non-zero octets) */
+       pos = out + 2;
+       end = out + *outlen;
+       while (*pos && pos < end)
+               pos++;
+       if (pos == end)
+               return -1;
+       pos++;
+
+       *outlen -= pos - out;
+
+       /* Strip PKCS #1 header */
+       os_memmove(out, pos, *outlen);
+
+       return 0;
+}
+
+
+int pkcs1_decrypt_public_key(struct crypto_rsa_key *key,
+                            const u8 *crypt, size_t crypt_len,
+                            u8 *plain, size_t *plain_len)
+{
+       size_t len;
+       u8 *pos;
+
+       len = *plain_len;
+       if (crypto_rsa_exptmod(crypt, crypt_len, plain, &len, key, 0) < 0)
+               return -1;
+
+       /*
+        * PKCS #1 v1.5, 8.1:
+        *
+        * EB = 00 || BT || PS || 00 || D
+        * BT = 00 or 01
+        * PS = k-3-||D|| times (00 if BT=00) or (FF if BT=01)
+        * k = length of modulus in octets
+        */
+
+       if (len < 3 + 8 + 16 /* min hash len */ ||
+           plain[0] != 0x00 || (plain[1] != 0x00 && plain[1] != 0x01)) {
+               wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
+                          "structure");
+               return -1;
+       }
+
+       pos = plain + 3;
+       if (plain[1] == 0x00) {
+               /* BT = 00 */
+               if (plain[2] != 0x00) {
+                       wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature "
+                                  "PS (BT=00)");
+                       return -1;
+               }
+               while (pos + 1 < plain + len && *pos == 0x00 && pos[1] == 0x00)
+                       pos++;
+       } else {
+               /* BT = 01 */
+               if (plain[2] != 0xff) {
+                       wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature "
+                                  "PS (BT=01)");
+                       return -1;
+               }
+               while (pos < plain + len && *pos == 0xff)
+                       pos++;
+       }
+
+       if (pos - plain - 2 < 8) {
+               /* PKCS #1 v1.5, 8.1: At least eight octets long PS */
+               wpa_printf(MSG_INFO, "LibTomCrypt: Too short signature "
+                          "padding");
+               return -1;
+       }
+
+       if (pos + 16 /* min hash len */ >= plain + len || *pos != 0x00) {
+               wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
+                          "structure (2)");
+               return -1;
+       }
+       pos++;
+       len -= pos - plain;
+
+       /* Strip PKCS #1 header */
+       os_memmove(plain, pos, len);
+       *plain_len = len;
+
+       return 0;
+}
diff --git a/components/wpa_supplicant/src/wpa2/tls/pkcs5.c b/components/wpa_supplicant/src/wpa2/tls/pkcs5.c
new file mode 100644 (file)
index 0000000..0a0ac9e
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * PKCS #5 (Password-based Encryption)
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/crypto.h"
+#include "crypto/md5.h"
+#include "wpa2/tls/asn1.h"
+#include "wpa2/tls/pkcs5.h"
+
+#include "wpa2/eap_peer/eap_i.h"
+
+struct pkcs5_params {
+       enum pkcs5_alg {
+               PKCS5_ALG_UNKNOWN,
+               PKCS5_ALG_MD5_DES_CBC
+       } alg;
+       u8 salt[8];
+       size_t salt_len;
+       unsigned int iter_count;
+};
+
+
+static enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid)
+{
+       if (oid->len == 7 &&
+           oid->oid[0] == 1 /* iso */ &&
+           oid->oid[1] == 2 /* member-body */ &&
+           oid->oid[2] == 840 /* us */ &&
+           oid->oid[3] == 113549 /* rsadsi */ &&
+           oid->oid[4] == 1 /* pkcs */ &&
+           oid->oid[5] == 5 /* pkcs-5 */ &&
+           oid->oid[6] == 3 /* pbeWithMD5AndDES-CBC */)
+               return PKCS5_ALG_MD5_DES_CBC;
+
+       return PKCS5_ALG_UNKNOWN;
+}
+
+
+static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len,
+                           struct pkcs5_params *params)
+{
+       struct asn1_hdr hdr;
+       const u8 *enc_alg_end, *pos, *end;
+       struct asn1_oid oid;
+       char obuf[80];
+
+       /* AlgorithmIdentifier */
+
+       enc_alg_end = enc_alg + enc_alg_len;
+
+       os_memset(params, 0, sizeof(*params));
+
+       if (asn1_get_oid(enc_alg, enc_alg_end - enc_alg, &oid, &pos)) {
+               wpa_printf(MSG_DEBUG, "PKCS #5: Failed to parse OID "
+                          "(algorithm)");
+               return -1;
+       }
+
+       asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+       wpa_printf(MSG_DEBUG, "PKCS #5: encryption algorithm %s", obuf);
+       params->alg = pkcs5_get_alg(&oid);
+       if (params->alg == PKCS5_ALG_UNKNOWN) {
+               wpa_printf(MSG_INFO, "PKCS #5: unsupported encryption "
+                          "algorithm %s", obuf);
+               return -1;
+       }
+
+       /*
+        * PKCS#5, Section 8
+        * PBEParameter ::= SEQUENCE {
+        *   salt OCTET STRING SIZE(8),
+        *   iterationCount INTEGER }
+        */
+
+       if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "PKCS #5: Expected SEQUENCE "
+                          "(PBEParameter) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+       pos = hdr.payload;
+       end = hdr.payload + hdr.length;
+
+       /* salt OCTET STRING SIZE(8) */
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_OCTETSTRING ||
+           hdr.length != 8) {
+               wpa_printf(MSG_DEBUG, "PKCS #5: Expected OCTETSTRING SIZE(8) "
+                          "(salt) - found class %d tag 0x%x size %d",
+                          hdr.class, hdr.tag, hdr.length);
+               return -1;
+       }
+       pos = hdr.payload + hdr.length;
+       os_memcpy(params->salt, hdr.payload, hdr.length);
+       params->salt_len = hdr.length;
+       wpa_hexdump(MSG_DEBUG, "PKCS #5: salt",
+                   params->salt, params->salt_len);
+
+       /* iterationCount INTEGER */
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
+               wpa_printf(MSG_DEBUG, "PKCS #5: Expected INTEGER - found "
+                          "class %d tag 0x%x", hdr.class, hdr.tag);
+               return -1;
+       }
+       if (hdr.length == 1)
+               params->iter_count = *hdr.payload;
+       else if (hdr.length == 2)
+               params->iter_count = WPA_GET_BE16(hdr.payload);
+       else if (hdr.length == 4)
+               params->iter_count = WPA_GET_BE32(hdr.payload);
+       else {
+               wpa_hexdump(MSG_DEBUG, "PKCS #5: Unsupported INTEGER value "
+                           " (iterationCount)",
+                           hdr.payload, hdr.length);
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG, "PKCS #5: iterationCount=0x%x",
+                  params->iter_count);
+       if (params->iter_count == 0 || params->iter_count > 0xffff) {
+               wpa_printf(MSG_INFO, "PKCS #5: Unsupported "
+                          "iterationCount=0x%x", params->iter_count);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params,
+                                               const char *passwd)
+{
+       unsigned int i;
+       u8 hash[MD5_MAC_LEN];
+       const u8 *addr[2];
+       size_t len[2];
+
+       if (params->alg != PKCS5_ALG_MD5_DES_CBC) {
+               return NULL;
+       }
+
+       addr[0] = (const u8 *) passwd;
+       len[0] = os_strlen(passwd);
+       addr[1] = params->salt;
+       len[1] = params->salt_len;
+       if (md5_vector(2, addr, len, hash) < 0)
+               return NULL;
+       addr[0] = hash;
+       len[0] = MD5_MAC_LEN;
+       for (i = 1; i < params->iter_count; i++) {
+               if (md5_vector(1, addr, len, hash) < 0)
+                       return NULL;
+       }
+       /* TODO: DES key parity bits(?) */
+       wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES key", hash, 8);
+       wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES IV", hash + 8, 8);
+
+       if (wpa2_crypto_funcs.crypto_cipher_init) {
+               return wpa2_crypto_funcs.crypto_cipher_init(CRYPTO_CIPHER_ALG_DES, hash + 8, hash, 8);
+       } else {
+               wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_cipher_init function! \r\n", __FUNCTION__);
+               return NULL;
+       }
+}
+
+u8 * pkcs5_decrypt(const u8 *enc_alg, size_t enc_alg_len,
+                  const u8 *enc_data, size_t enc_data_len,
+                  const char *passwd, size_t *data_len)
+{
+       struct crypto_cipher *ctx = NULL;
+       u8 *eb, pad;
+       struct pkcs5_params params;
+       unsigned int i;
+
+       if (pkcs5_get_params(enc_alg, enc_alg_len, &params) < 0) {
+               wpa_printf(MSG_DEBUG, "PKCS #5: Unsupported parameters");
+               return NULL;
+       }
+
+       ctx = pkcs5_crypto_init(&params, passwd);
+
+       if (ctx == NULL) {
+               wpa_printf(MSG_DEBUG, "PKCS #5: Failed to initialize crypto");
+               return NULL;
+       }
+
+       /* PKCS #5, Section 7 - Decryption process */
+       if (enc_data_len < 16 || enc_data_len % 8) {
+               wpa_printf(MSG_INFO, "PKCS #5: invalid length of ciphertext "
+                          "%d", (int) enc_data_len);
+               if (wpa2_crypto_funcs.crypto_cipher_deinit) {
+                       wpa2_crypto_funcs.crypto_cipher_deinit(ctx);
+               } else {
+                       wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n");
+                       return NULL;
+               }
+               return NULL;
+       }
+
+       eb = os_malloc(enc_data_len);
+       if (eb == NULL) {
+               if (wpa2_crypto_funcs.crypto_cipher_deinit) {
+                       wpa2_crypto_funcs.crypto_cipher_deinit(ctx);
+               } else {
+                       wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n");
+                       return NULL;
+               }
+               return NULL;
+       }
+
+       if (wpa2_crypto_funcs.crypto_cipher_decrypt) {
+               if ((int)wpa2_crypto_funcs.crypto_cipher_decrypt(ctx, enc_data, eb, enc_data_len) < 0) {
+                       wpa_printf(MSG_DEBUG, "PKCS #5: Failed to decrypt EB");
+                       wpa2_crypto_funcs.crypto_cipher_deinit(ctx);
+                       os_free(eb);
+                       return NULL;
+               }
+       } else {
+               wpa_printf(MSG_ERROR, "Fail to register crypto cipher decrypt function.\r\n");
+               wpa2_crypto_funcs.crypto_cipher_deinit(ctx);
+               os_free(eb);
+               return NULL;
+       }
+
+       if (wpa2_crypto_funcs.crypto_cipher_deinit) {
+               wpa2_crypto_funcs.crypto_cipher_deinit(ctx);
+       } else {
+               wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n");
+               return NULL;
+       }
+
+       pad = eb[enc_data_len - 1];
+       if (pad > 8) {
+               wpa_printf(MSG_INFO, "PKCS #5: Invalid PS octet 0x%x", pad);
+               os_free(eb);
+               return NULL;
+       }
+       for (i = enc_data_len - pad; i < enc_data_len; i++) {
+               if (eb[i] != pad) {
+                       wpa_hexdump(MSG_INFO, "PKCS #5: Invalid PS",
+                                   eb + enc_data_len - pad, pad);
+                       os_free(eb);
+                       return NULL;
+               }
+       }
+
+       wpa_hexdump_key(MSG_MSGDUMP, "PKCS #5: message M (encrypted key)",
+                       eb, enc_data_len - pad);
+
+       *data_len = enc_data_len - pad;
+       return eb;
+}
diff --git a/components/wpa_supplicant/src/wpa2/tls/pkcs8.c b/components/wpa_supplicant/src/wpa2/tls/pkcs8.c
new file mode 100644 (file)
index 0000000..0f39c45
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * PKCS #8 (Private-key information syntax)
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wpa2/tls/asn1.h"
+#include "wpa2/tls/bignum.h"
+#include "wpa2/tls/rsa.h"
+#include "wpa2/tls/pkcs5.h"
+#include "wpa2/tls/pkcs8.h"
+
+struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len)
+{
+       struct asn1_hdr hdr;
+       const u8 *pos, *end;
+       struct bignum *zero;
+       struct asn1_oid oid;
+       char obuf[80];
+
+       /* PKCS #8, Chapter 6 */
+
+       /* PrivateKeyInfo ::= SEQUENCE */
+       if (asn1_get_next(buf, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 "
+                          "header (SEQUENCE); assume PKCS #8 not used");
+               return NULL;
+       }
+       pos = hdr.payload;
+       end = pos + hdr.length;
+
+       /* version Version (Version ::= INTEGER) */
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
+               wpa_printf(MSG_DEBUG, "PKCS #8: Expected INTEGER - found "
+                          "class %d tag 0x%x; assume PKCS #8 not used",
+                          hdr.class, hdr.tag);
+               return NULL;
+       }
+
+       zero = bignum_init();
+       if (zero == NULL)
+               return NULL;
+
+       if (bignum_set_unsigned_bin(zero, hdr.payload, hdr.length) < 0) {
+               wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse INTEGER");
+               bignum_deinit(zero);
+               return NULL;
+       }
+       pos = hdr.payload + hdr.length;
+
+       if (bignum_cmp_d(zero, 0) != 0) {
+               wpa_printf(MSG_DEBUG, "PKCS #8: Expected zero INTEGER in the "
+                          "beginning of private key; not found; assume "
+                          "PKCS #8 not used");
+               bignum_deinit(zero);
+               return NULL;
+       }
+       bignum_deinit(zero);
+
+       /* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier
+        * (PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier) */
+       if (asn1_get_next(pos, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE "
+                          "(AlgorithmIdentifier) - found class %d tag 0x%x; "
+                          "assume PKCS #8 not used",
+                          hdr.class, hdr.tag);
+               return NULL;
+       }
+
+       if (asn1_get_oid(hdr.payload, hdr.length, &oid, &pos)) {
+               wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse OID "
+                          "(algorithm); assume PKCS #8 not used");
+               return NULL;
+       }
+
+       asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+       wpa_printf(MSG_DEBUG, "PKCS #8: algorithm=%s", obuf);
+
+       if (oid.len != 7 ||
+           oid.oid[0] != 1 /* iso */ ||
+           oid.oid[1] != 2 /* member-body */ ||
+           oid.oid[2] != 840 /* us */ ||
+           oid.oid[3] != 113549 /* rsadsi */ ||
+           oid.oid[4] != 1 /* pkcs */ ||
+           oid.oid[5] != 1 /* pkcs-1 */ ||
+           oid.oid[6] != 1 /* rsaEncryption */) {
+               wpa_printf(MSG_DEBUG, "PKCS #8: Unsupported private key "
+                          "algorithm %s", obuf);
+               return NULL;
+       }
+
+       pos = hdr.payload + hdr.length;
+
+       /* privateKey PrivateKey (PrivateKey ::= OCTET STRING) */
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_OCTETSTRING) {
+               wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING "
+                          "(privateKey) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return NULL;
+       }
+       wpa_printf(MSG_DEBUG, "PKCS #8: Try to parse RSAPrivateKey");
+
+       return (struct crypto_private_key *)
+               crypto_rsa_import_private_key(hdr.payload, hdr.length);
+}
+
+
+struct crypto_private_key *
+pkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd)
+{
+       struct asn1_hdr hdr;
+       const u8 *pos, *end, *enc_alg;
+       size_t enc_alg_len;
+       u8 *data;
+       size_t data_len;
+
+       if (passwd == NULL)
+               return NULL;
+
+       /*
+        * PKCS #8, Chapter 7
+        * EncryptedPrivateKeyInfo ::= SEQUENCE {
+        *   encryptionAlgorithm EncryptionAlgorithmIdentifier,
+        *   encryptedData EncryptedData }
+        * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+        * EncryptedData ::= OCTET STRING
+        */
+
+       if (asn1_get_next(buf, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 "
+                          "header (SEQUENCE); assume encrypted PKCS #8 not "
+                          "used");
+               return NULL;
+       }
+       pos = hdr.payload;
+       end = pos + hdr.length;
+
+       /* encryptionAlgorithm EncryptionAlgorithmIdentifier */
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE "
+                          "(AlgorithmIdentifier) - found class %d tag 0x%x; "
+                          "assume encrypted PKCS #8 not used",
+                          hdr.class, hdr.tag);
+               return NULL;
+       }
+       enc_alg = hdr.payload;
+       enc_alg_len = hdr.length;
+       pos = hdr.payload + hdr.length;
+
+       /* encryptedData EncryptedData */
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_OCTETSTRING) {
+               wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING "
+                          "(encryptedData) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return NULL;
+       }
+
+       data = pkcs5_decrypt(enc_alg, enc_alg_len, hdr.payload, hdr.length,
+                            passwd, &data_len);
+       if (data) {
+               struct crypto_private_key *key;
+               key = pkcs8_key_import(data, data_len);
+               os_free(data);
+               return key;
+       }
+
+       return NULL;
+}
diff --git a/components/wpa_supplicant/src/wpa2/tls/rsa.c b/components/wpa_supplicant/src/wpa2/tls/rsa.c
new file mode 100644 (file)
index 0000000..0b09d4d
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * RSA
+ * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wpa2/tls/asn1.h"
+#include "wpa2/tls/bignum.h"
+#include "wpa2/tls/rsa.h"
+#include "soc/dport_reg.h"
+
+struct crypto_rsa_key {
+       int private_key; /* whether private key is set */
+       struct bignum *n; /* modulus (p * q) */
+       struct bignum *e; /* public exponent */
+       /* The following parameters are available only if private_key is set */
+       struct bignum *d; /* private exponent */
+       struct bignum *p; /* prime p (factor of n) */
+       struct bignum *q; /* prime q (factor of n) */
+       struct bignum *dmp1; /* d mod (p - 1); CRT exponent */
+       struct bignum *dmq1; /* d mod (q - 1); CRT exponent */
+       struct bignum *iqmp; /* 1 / q mod p; CRT coefficient */
+};
+
+
+static const u8 * crypto_rsa_parse_integer(const u8 *pos, const u8 *end,
+                                          struct bignum *num)
+{
+       struct asn1_hdr hdr;
+
+       if (pos == NULL)
+               return NULL;
+
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
+               wpa_printf(MSG_DEBUG, "RSA: Expected INTEGER - found class %d "
+                          "tag 0x%x", hdr.class, hdr.tag);
+               return NULL;
+       }
+
+       if (bignum_set_unsigned_bin(num, hdr.payload, hdr.length) < 0) {
+               wpa_printf(MSG_DEBUG, "RSA: Failed to parse INTEGER");
+               return NULL;
+       }
+
+       return hdr.payload + hdr.length;
+}
+
+
+/**
+ * crypto_rsa_import_public_key - Import an RSA public key
+ * @buf: Key buffer (DER encoded RSA public key)
+ * @len: Key buffer length in bytes
+ * Returns: Pointer to the public key or %NULL on failure
+ */
+struct crypto_rsa_key *
+crypto_rsa_import_public_key(const u8 *buf, size_t len)
+{
+       struct crypto_rsa_key *key;
+       struct asn1_hdr hdr;
+       const u8 *pos, *end;
+
+       key = (struct crypto_rsa_key *)os_zalloc(sizeof(*key));
+       if (key == NULL)
+               return NULL;
+
+       key->n = bignum_init();
+       key->e = bignum_init();
+       if (key->n == NULL || key->e == NULL) {
+               crypto_rsa_free(key);
+               return NULL;
+       }
+
+       /*
+        * PKCS #1, 7.1:
+        * RSAPublicKey ::= SEQUENCE {
+        *     modulus INTEGER, -- n
+        *     publicExponent INTEGER -- e 
+        * }
+        */
+
+       if (asn1_get_next(buf, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE "
+                          "(public key) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               goto error;
+       }
+       pos = hdr.payload;
+       end = pos + hdr.length;
+
+       pos = crypto_rsa_parse_integer(pos, end, key->n);
+       pos = crypto_rsa_parse_integer(pos, end, key->e);
+
+       if (pos == NULL)
+               goto error;
+
+       if (pos != end) {
+               wpa_hexdump(MSG_DEBUG,
+                           "RSA: Extra data in public key SEQUENCE",
+                           pos, end - pos);
+               goto error;
+       }
+
+       return key;
+
+error:
+       crypto_rsa_free(key);
+       return NULL;
+}
+
+
+/**
+ * crypto_rsa_import_private_key - Import an RSA private key
+ * @buf: Key buffer (DER encoded RSA private key)
+ * @len: Key buffer length in bytes
+ * Returns: Pointer to the private key or %NULL on failure
+ */
+struct crypto_rsa_key *
+crypto_rsa_import_private_key(const u8 *buf, size_t len)
+{
+       struct crypto_rsa_key *key;
+       struct bignum *zero;
+       struct asn1_hdr hdr;
+       const u8 *pos, *end;
+
+       key = (struct crypto_rsa_key *)os_zalloc(sizeof(*key));
+       if (key == NULL)
+               return NULL;
+
+       key->private_key = 1;
+
+       key->n = bignum_init();
+       key->e = bignum_init();
+       key->d = bignum_init();
+       key->p = bignum_init();
+       key->q = bignum_init();
+       key->dmp1 = bignum_init();
+       key->dmq1 = bignum_init();
+       key->iqmp = bignum_init();
+
+       if (key->n == NULL || key->e == NULL || key->d == NULL ||
+           key->p == NULL || key->q == NULL || key->dmp1 == NULL ||
+           key->dmq1 == NULL || key->iqmp == NULL) {
+               crypto_rsa_free(key);
+               return NULL;
+       }
+
+       /*
+        * PKCS #1, 7.2:
+        * RSAPrivateKey ::= SEQUENCE {
+        *    version Version,
+        *    modulus INTEGER, -- n
+        *    publicExponent INTEGER, -- e
+        *    privateExponent INTEGER, -- d
+        *    prime1 INTEGER, -- p
+        *    prime2 INTEGER, -- q
+        *    exponent1 INTEGER, -- d mod (p-1)
+        *    exponent2 INTEGER, -- d mod (q-1)
+        *    coefficient INTEGER -- (inverse of q) mod p
+        * }
+        *
+        * Version ::= INTEGER -- shall be 0 for this version of the standard
+        */
+       if (asn1_get_next(buf, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE "
+                          "(public key) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               goto error;
+       }
+       pos = hdr.payload;
+       end = pos + hdr.length;
+
+       zero = bignum_init();
+       if (zero == NULL)
+               goto error;
+       pos = crypto_rsa_parse_integer(pos, end, zero);
+       if (pos == NULL || bignum_cmp_d(zero, 0) != 0) {
+               wpa_printf(MSG_DEBUG, "RSA: Expected zero INTEGER in the "
+                          "beginning of private key; not found");
+               bignum_deinit(zero);
+               goto error;
+       }
+       bignum_deinit(zero);
+
+       pos = crypto_rsa_parse_integer(pos, end, key->n);
+       pos = crypto_rsa_parse_integer(pos, end, key->e);
+       pos = crypto_rsa_parse_integer(pos, end, key->d);
+       pos = crypto_rsa_parse_integer(pos, end, key->p);
+       pos = crypto_rsa_parse_integer(pos, end, key->q);
+       pos = crypto_rsa_parse_integer(pos, end, key->dmp1);
+       pos = crypto_rsa_parse_integer(pos, end, key->dmq1);
+       pos = crypto_rsa_parse_integer(pos, end, key->iqmp);
+
+       if (pos == NULL)
+               goto error;
+
+       if (pos != end) {
+               wpa_hexdump(MSG_DEBUG,
+                           "RSA: Extra data in public key SEQUENCE",
+                           pos, end - pos);
+               goto error;
+       }
+
+       return key;
+
+error:
+       crypto_rsa_free(key);
+       return NULL;
+}
+
+
+/**
+ * crypto_rsa_get_modulus_len - Get the modulus length of the RSA key
+ * @key: RSA key
+ * Returns: Modulus length of the key
+ */
+size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key)
+{
+       return bignum_get_unsigned_bin_len(key->n);
+}
+
+
+/**
+ * crypto_rsa_exptmod - RSA modular exponentiation
+ * @in: Input data
+ * @inlen: Input data length
+ * @out: Buffer for output data
+ * @outlen: Maximum size of the output buffer and used size on success
+ * @key: RSA key
+ * @use_private: 1 = Use RSA private key, 0 = Use RSA public key
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen,
+                      struct crypto_rsa_key *key, int use_private)
+{
+       struct bignum *tmp, *a = NULL, *b = NULL;
+       int ret = -1;
+       size_t modlen;
+
+       if (use_private && !key->private_key)
+               return -1;
+
+       tmp = bignum_init();
+       if (tmp == NULL)
+               return -1;
+       
+       if (bignum_set_unsigned_bin(tmp, in, inlen) < 0)
+               goto error;
+       if (bignum_cmp(key->n, tmp) < 0) {
+               /* Too large input value for the RSA key modulus */
+               goto error;
+       }
+
+       if (use_private) {
+               /*
+                * Decrypt (or sign) using Chinese remainer theorem to speed
+                * up calculation. This is equivalent to tmp = tmp^d mod n
+                * (which would require more CPU to calculate directly).
+                *
+                * dmp1 = (1/e) mod (p-1)
+                * dmq1 = (1/e) mod (q-1)
+                * iqmp = (1/q) mod p, where p > q
+                * m1 = c^dmp1 mod p
+                * m2 = c^dmq1 mod q
+                * h = q^-1 (m1 - m2) mod p
+                * m = m2 + hq
+                */
+               a = bignum_init();
+               b = bignum_init();
+               if (a == NULL || b == NULL)
+                       goto error;
+
+               /* a = tmp^dmp1 mod p */
+               if (bignum_exptmod(tmp, key->dmp1, key->p, a) < 0)
+                       goto error;
+
+               /* b = tmp^dmq1 mod q */
+               if (bignum_exptmod(tmp, key->dmq1, key->q, b) < 0)
+                       goto error;
+
+               /* tmp = (a - b) * (1/q mod p) (mod p) */
+               if (bignum_sub(a, b, tmp) < 0 ||
+                   bignum_mulmod(tmp, key->iqmp, key->p, tmp) < 0)
+                       goto error;
+
+               /* tmp = b + q * tmp */
+               if (bignum_mul(tmp, key->q, tmp) < 0 ||
+                   bignum_add(tmp, b, tmp) < 0)
+                       goto error;
+       } else {
+               /* Encrypt (or verify signature) */
+               /* tmp = tmp^e mod N */
+               if (bignum_exptmod(tmp, key->e, key->n, tmp) < 0)
+                       goto error;
+       }
+
+       modlen = crypto_rsa_get_modulus_len(key);
+       if (modlen > *outlen) {
+               *outlen = modlen;
+               goto error;
+       }
+
+       if (bignum_get_unsigned_bin_len(tmp) > modlen)
+               goto error; /* should never happen */
+
+       *outlen = modlen;
+       os_memset(out, 0, modlen);
+       if (bignum_get_unsigned_bin(
+                   tmp, out +
+                   (modlen - bignum_get_unsigned_bin_len(tmp)), NULL) < 0)
+               goto error;
+
+       ret = 0;
+
+error:
+
+       bignum_deinit(tmp);
+       bignum_deinit(a);
+       bignum_deinit(b);
+       return ret;
+}
+
+
+/**
+ * crypto_rsa_free - Free RSA key
+ * @key: RSA key to be freed
+ *
+ * This function frees an RSA key imported with either
+ * crypto_rsa_import_public_key() or crypto_rsa_import_private_key().
+ */
+void crypto_rsa_free(struct crypto_rsa_key *key)
+{
+       if (key) {
+               bignum_deinit(key->n);
+               bignum_deinit(key->e);
+               bignum_deinit(key->d);
+               bignum_deinit(key->p);
+               bignum_deinit(key->q);
+               bignum_deinit(key->dmp1);
+               bignum_deinit(key->dmq1);
+               bignum_deinit(key->iqmp);
+               os_free(key);
+       }
+}
diff --git a/components/wpa_supplicant/src/wpa2/tls/tls_internal.c b/components/wpa_supplicant/src/wpa2/tls/tls_internal.c
new file mode 100644 (file)
index 0000000..682d457
--- /dev/null
@@ -0,0 +1,716 @@
+/*
+ * TLS interface functions and an internal TLS implementation
+ * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * This file interface functions for hostapd/wpa_supplicant to use the
+ * integrated TLSv1 implementation.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/sha1.h"
+#include "crypto/md5.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/tls/tlsv1_client.h"
+#include "wpa2/tls/tlsv1_server.h"
+
+#ifndef CONFIG_TLS_INTERNAL_CLIENT
+#define CONFIG_TLS_INTERNAL_CLIENT
+#endif
+
+static int tls_ref_count = 0;
+
+struct tls_global {
+       int server;
+       struct tlsv1_credentials *server_cred;
+       int check_crl;
+};
+
+struct tls_connection {
+       struct tlsv1_client *client;
+       struct tlsv1_server *server;
+};
+
+
+void * tls_init(void)
+{
+       struct tls_global *global;
+
+       if (tls_ref_count == 0) {
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+               if (tlsv1_client_global_init())
+                       return NULL;
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+               if (tlsv1_server_global_init())
+                       return NULL;
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       }
+       tls_ref_count++;
+
+       global = (struct tls_global *)os_zalloc(sizeof(*global));
+       if (global == NULL)
+               return NULL;
+
+       return global;
+}
+
+void tls_deinit(void *ssl_ctx)
+{
+       struct tls_global *global = ssl_ctx;
+       tls_ref_count--;
+       if (tls_ref_count == 0) {
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+               tlsv1_client_global_deinit();
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+               tlsv1_cred_free(global->server_cred);
+               tlsv1_server_global_deinit();
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       }
+       os_free(global);
+}
+
+
+int tls_get_errors(void *tls_ctx)
+{
+       return 0;
+}
+
+
+struct tls_connection * tls_connection_init(void *tls_ctx)
+{
+       struct tls_connection *conn;
+       struct tls_global *global = tls_ctx;
+
+       conn = (struct tls_connection *)os_zalloc(sizeof(*conn));
+       if (conn == NULL)
+               return NULL;
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (!global->server) {
+               conn->client = tlsv1_client_init();
+               if (conn->client == NULL) {
+                       os_free(conn);
+                       return NULL;
+               }
+       }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (global->server) {
+               conn->server = tlsv1_server_init(global->server_cred);
+               if (conn->server == NULL) {
+                       os_free(conn);
+                       return NULL;
+               }
+       }
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+
+       return conn;
+}
+
+
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
+{
+       if (conn == NULL)
+               return;
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client)
+               tlsv1_client_deinit(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server)
+               tlsv1_server_deinit(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       os_free(conn);
+}
+
+
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client)
+               return tlsv1_client_established(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server)
+               return tlsv1_server_established(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return 0;
+}
+
+
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client)
+               return tlsv1_client_shutdown(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server)
+               return tlsv1_server_shutdown(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return -1;
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+                             const struct tls_connection_params *params)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       struct tlsv1_credentials *cred;
+
+       if (conn->client == NULL)
+               return -1;
+
+       cred = tlsv1_cred_alloc();
+       if (cred == NULL)
+               return -1;
+
+       if (tlsv1_set_ca_cert(cred, params->ca_cert,
+                             params->ca_cert_blob, params->ca_cert_blob_len,
+                             params->ca_path)) {
+               wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA "
+                          "certificates");
+               tlsv1_cred_free(cred);
+               return -1;
+       }
+
+       if (tlsv1_set_cert(cred, params->client_cert,
+                          params->client_cert_blob,
+                          params->client_cert_blob_len)) {
+               wpa_printf(MSG_INFO, "TLS: Failed to configure client "
+                          "certificate");
+               tlsv1_cred_free(cred);
+               return -1;
+       }
+
+       if (tlsv1_set_private_key(cred, params->private_key,
+                                 params->private_key_passwd,
+                                 params->private_key_blob,
+                                 params->private_key_blob_len)) {
+               wpa_printf(MSG_INFO, "TLS: Failed to load private key");
+               tlsv1_cred_free(cred);
+               return -1;
+       }
+
+       if (tlsv1_client_set_cred(conn->client, cred) < 0) {
+               tlsv1_cred_free(cred);
+               return -1;
+       }
+
+       tlsv1_client_set_time_checks(
+               conn->client, !(params->flags & TLS_CONN_DISABLE_TIME_CHECKS));
+               //conn->client, !(TLS_CONN_DISABLE_TIME_CHECKS)); //snake
+
+       return 0;
+#else /* CONFIG_TLS_INTERNAL_CLIENT */
+       return -1;
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+                         const struct tls_connection_params *params)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       struct tls_global *global = tls_ctx;
+       struct tlsv1_credentials *cred;
+
+       /* Currently, global parameters are only set when running in server
+        * mode. */
+       global->server = 1;
+       tlsv1_cred_free(global->server_cred);
+       global->server_cred = cred = tlsv1_cred_alloc();
+       if (cred == NULL)
+               return -1;
+
+       if (tlsv1_set_ca_cert(cred, params->ca_cert, params->ca_cert_blob,
+                             params->ca_cert_blob_len, params->ca_path)) {
+               wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA "
+                          "certificates");
+               return -1;
+       }
+
+       if (tlsv1_set_cert(cred, params->client_cert, params->client_cert_blob,
+                          params->client_cert_blob_len)) {
+               wpa_printf(MSG_INFO, "TLS: Failed to configure server "
+                          "certificate");
+               return -1;
+       }
+
+       if (tlsv1_set_private_key(cred, params->private_key,
+                                 params->private_key_passwd,
+                                 params->private_key_blob,
+                                 params->private_key_blob_len)) {
+               wpa_printf(MSG_INFO, "TLS: Failed to load private key");
+               return -1;
+       }
+
+       if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob,
+                              params->dh_blob_len)) {
+               wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters");
+               return -1;
+       }
+
+       return 0;
+#else /* CONFIG_TLS_INTERNAL_SERVER */
+       return -1;
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+}
+
+
+int tls_global_set_verify(void *tls_ctx, int check_crl)
+{
+       struct tls_global *global = tls_ctx;
+       global->check_crl = check_crl;
+       return 0;
+}
+
+
+int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
+                             int verify_peer)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server)
+               return tlsv1_server_set_verify(conn->server, verify_peer);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return -1;
+}
+
+
+int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
+                           struct tls_keys *keys)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client)
+               return tlsv1_client_get_keys(conn->client, keys);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server)
+               return tlsv1_server_get_keys(conn->server, keys);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return -1;
+}
+
+
+int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+                      const char *label, int server_random_first,
+                      u8 *out, size_t out_len)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client) {
+               return tlsv1_client_prf(conn->client, label,
+                                       server_random_first,
+                                       out, out_len);
+       }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server) {
+               return tlsv1_server_prf(conn->server, label,
+                                       server_random_first,
+                                       out, out_len);
+       }
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return -1;
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+                                        struct tls_connection *conn,
+                                        const struct wpabuf *in_data,
+                                        struct wpabuf **appl_data)
+{
+       return tls_connection_handshake2(tls_ctx, conn, in_data, appl_data,
+                                        NULL);
+}
+
+
+struct wpabuf * tls_connection_handshake2(void *tls_ctx,
+                                         struct tls_connection *conn,
+                                         const struct wpabuf *in_data,
+                                         struct wpabuf **appl_data,
+                                         int *need_more_data)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       u8 *res, *ad;
+       size_t res_len, ad_len;
+       struct wpabuf *out;
+
+       if (conn->client == NULL)
+               return NULL;
+
+       ad = NULL;
+       res = tlsv1_client_handshake(conn->client,
+                                    in_data ? wpabuf_head(in_data) : NULL,
+                                    in_data ? wpabuf_len(in_data) : 0,
+                                    &res_len, &ad, &ad_len, need_more_data);
+       if (res == NULL) {
+               return NULL;
+       }
+       out = wpabuf_alloc_ext_data(res, res_len);
+       if (out == NULL) {
+               os_free(res);
+               os_free(ad);
+               return NULL;
+       }
+       if (appl_data) {
+               if (ad) {
+                       *appl_data = wpabuf_alloc_ext_data(ad, ad_len);
+                       if (*appl_data == NULL)
+                               os_free(ad);
+               } else
+                       *appl_data = NULL;
+       } else
+               os_free(ad);
+    
+       return out;
+#else /* CONFIG_TLS_INTERNAL_CLIENT */
+       return NULL;
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+                                               struct tls_connection *conn,
+                                               const struct wpabuf *in_data,
+                                               struct wpabuf **appl_data)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       u8 *res;
+       size_t res_len;
+       struct wpabuf *out;
+
+       if (conn->server == NULL)
+               return NULL;
+
+       if (appl_data)
+               *appl_data = NULL;
+
+       res = tlsv1_server_handshake(conn->server, wpabuf_head(in_data),
+                                    wpabuf_len(in_data), &res_len);
+       if (res == NULL && tlsv1_server_established(conn->server))
+               return wpabuf_alloc(0);
+       if (res == NULL)
+               return NULL;
+       out = wpabuf_alloc_ext_data(res, res_len);
+       if (out == NULL) {
+               os_free(res);
+               return NULL;
+       }
+
+       return out;
+#else /* CONFIG_TLS_INTERNAL_SERVER */
+       return NULL;
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+                                      struct tls_connection *conn,
+                                      const struct wpabuf *in_data)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client) {
+               struct wpabuf *buf;
+               int res;
+               buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
+               if (buf == NULL)
+                       return NULL;
+               res = tlsv1_client_encrypt(conn->client, wpabuf_head(in_data),
+                                          wpabuf_len(in_data),
+                                          wpabuf_mhead(buf),
+                                          wpabuf_size(buf));
+               if (res < 0) {
+                       wpabuf_free(buf);
+                       return NULL;
+               }
+               wpabuf_put(buf, res);
+               return buf;
+       }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server) {
+               struct wpabuf *buf;
+               int res;
+               buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
+               if (buf == NULL)
+                       return NULL;
+               res = tlsv1_server_encrypt(conn->server, wpabuf_head(in_data),
+                                          wpabuf_len(in_data),
+                                          wpabuf_mhead(buf),
+                                          wpabuf_size(buf));
+               if (res < 0) {
+                       wpabuf_free(buf);
+                       return NULL;
+               }
+               wpabuf_put(buf, res);
+               return buf;
+       }
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return NULL;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+                                      struct tls_connection *conn,
+                                      const struct wpabuf *in_data)
+{
+       return tls_connection_decrypt2(tls_ctx, conn, in_data, NULL);
+}
+
+
+struct wpabuf * tls_connection_decrypt2(void *tls_ctx,
+                                       struct tls_connection *conn,
+                                       const struct wpabuf *in_data,
+                                       int *need_more_data)
+{
+       if (need_more_data)
+               *need_more_data = 0;
+
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client) {
+               return tlsv1_client_decrypt(conn->client, wpabuf_head(in_data),
+                                           wpabuf_len(in_data),
+                                           need_more_data);
+       }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server) {
+               struct wpabuf *buf;
+               int res;
+               buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+               if (buf == NULL)
+                       return NULL;
+               res = tlsv1_server_decrypt(conn->server, wpabuf_head(in_data),
+                                          wpabuf_len(in_data),
+                                          wpabuf_mhead(buf),
+                                          wpabuf_size(buf));
+               if (res < 0) {
+                       wpabuf_free(buf);
+                       return NULL;
+               }
+               wpabuf_put(buf, res);
+               return buf;
+       }
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return NULL;
+}
+
+
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client)
+               return tlsv1_client_resumed(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server)
+               return tlsv1_server_resumed(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return -1;
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+                                  u8 *ciphers)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client)
+               return tlsv1_client_set_cipher_list(conn->client, ciphers);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server)
+               return tlsv1_server_set_cipher_list(conn->server, ciphers);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return -1;
+}
+
+
+int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+                  char *buf, size_t buflen)
+{
+       if (conn == NULL)
+               return -1;
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client)
+               return tlsv1_client_get_cipher(conn->client, buf, buflen);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server)
+               return tlsv1_server_get_cipher(conn->server, buf, buflen);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return -1;
+}
+
+
+int tls_connection_enable_workaround(void *tls_ctx,
+                                    struct tls_connection *conn)
+{
+       return -1;
+}
+
+
+int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
+                                   int ext_type, const u8 *data,
+                                   size_t data_len)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client) {
+               return tlsv1_client_hello_ext(conn->client, ext_type,
+                                             data, data_len);
+       }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+       return -1;
+}
+
+
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
+{
+       return 0;
+}
+
+
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
+{
+       return 0;
+}
+
+
+int tls_connection_get_write_alerts(void *tls_ctx,
+                                   struct tls_connection *conn)
+{
+       return 0;
+}
+
+
+int tls_connection_get_keyblock_size(void *tls_ctx,
+                                    struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client)
+               return tlsv1_client_get_keyblock_size(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server)
+               return tlsv1_server_get_keyblock_size(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return -1;
+}
+
+
+unsigned int tls_capabilities(void *tls_ctx)
+{
+       return 0;
+}
+
+
+int tls_connection_set_session_ticket_cb(void *tls_ctx,
+                                        struct tls_connection *conn,
+                                        tls_session_ticket_cb cb,
+                                        void *ctx)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client) {
+               tlsv1_client_set_session_ticket_cb(conn->client, cb, ctx);
+               return 0;
+       }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server) {
+               tlsv1_server_set_session_ticket_cb(conn->server, cb, ctx);
+               return 0;
+       }
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return -1;
+}
+
+
+
+/**
+ * tls_prf_sha1_md5 - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246)
+ * @secret: Key for PRF
+ * @secret_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
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in TLS. This PRF is defined in RFC 2246, Chapter 5.
+ */
+int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label,
+                    const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+{
+       size_t L_S1, L_S2, i;
+       const u8 *S1, *S2;
+       u8 A_MD5[MD5_MAC_LEN], A_SHA1[SHA1_MAC_LEN];
+       u8 P_MD5[MD5_MAC_LEN], P_SHA1[SHA1_MAC_LEN];
+       int MD5_pos, SHA1_pos;
+       const u8 *MD5_addr[3];
+       size_t MD5_len[3];
+       const unsigned char *SHA1_addr[3];
+       size_t SHA1_len[3];
+
+       if (secret_len & 1)
+               return -1;
+
+       MD5_addr[0] = A_MD5;
+       MD5_len[0] = MD5_MAC_LEN;
+       MD5_addr[1] = (unsigned char *) label;
+       MD5_len[1] = os_strlen(label);
+       MD5_addr[2] = seed;
+       MD5_len[2] = seed_len;
+
+       SHA1_addr[0] = A_SHA1;
+       SHA1_len[0] = SHA1_MAC_LEN;
+       SHA1_addr[1] = (unsigned char *) label;
+       SHA1_len[1] = os_strlen(label);
+       SHA1_addr[2] = seed;
+       SHA1_len[2] = seed_len;
+
+       /* RFC 2246, Chapter 5
+        * A(0) = seed, A(i) = HMAC(secret, A(i-1))
+        * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + ..
+        * PRF = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed)
+        */
+
+       L_S1 = L_S2 = (secret_len + 1) / 2;
+       S1 = secret;
+       S2 = secret + L_S1;
+       if (secret_len & 1) {
+               /* The last byte of S1 will be shared with S2 */
+               S2--;
+       }
+
+       hmac_md5_vector(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], A_MD5);
+       hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1);
+
+       MD5_pos = MD5_MAC_LEN;
+       SHA1_pos = SHA1_MAC_LEN;
+       for (i = 0; i < outlen; i++) {
+               if (MD5_pos == MD5_MAC_LEN) {
+                       hmac_md5_vector(S1, L_S1, 3, MD5_addr, MD5_len, P_MD5);
+                       MD5_pos = 0;
+                       hmac_md5(S1, L_S1, A_MD5, MD5_MAC_LEN, A_MD5);
+               }
+               if (SHA1_pos == SHA1_MAC_LEN) {
+                       hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len,
+                                        P_SHA1);
+                       SHA1_pos = 0;
+                       hmac_sha1(S2, L_S2, A_SHA1, SHA1_MAC_LEN, A_SHA1);
+               }
+
+               out[i] = P_MD5[MD5_pos] ^ P_SHA1[SHA1_pos];
+
+               MD5_pos++;
+               SHA1_pos++;
+       }
+
+       return 0;
+}
diff --git a/components/wpa_supplicant/src/wpa2/tls/tlsv1_client.c b/components/wpa_supplicant/src/wpa2/tls/tlsv1_client.c
new file mode 100644 (file)
index 0000000..5283af2
--- /dev/null
@@ -0,0 +1,837 @@
+/*
+ * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/sha1.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/tls/tlsv1_common.h"
+#include "wpa2/tls/tlsv1_record.h"
+#include "wpa2/tls/tlsv1_client.h"
+#include "wpa2/tls/tlsv1_client_i.h"
+
+/* TODO:
+ * Support for a message fragmented across several records (RFC 2246, 6.2.1)
+ */
+
+
+void tls_alert(struct tlsv1_client *conn, u8 level, u8 description)
+{
+       conn->alert_level = level;
+       conn->alert_description = description;
+}
+
+
+void tlsv1_client_free_dh(struct tlsv1_client *conn)
+{
+       os_free(conn->dh_p);
+       os_free(conn->dh_g);
+       os_free(conn->dh_ys);
+       conn->dh_p = conn->dh_g = conn->dh_ys = NULL;
+}
+
+
+int tls_derive_pre_master_secret(u8 *pre_master_secret)
+{
+       WPA_PUT_BE16(pre_master_secret, TLS_VERSION);
+       if (os_get_random(pre_master_secret + 2,
+                         TLS_PRE_MASTER_SECRET_LEN - 2))
+               return -1;
+       return 0;
+}
+
+
+int tls_derive_keys(struct tlsv1_client *conn,
+                   const u8 *pre_master_secret, size_t pre_master_secret_len)
+{
+       u8 seed[2 * TLS_RANDOM_LEN];
+       u8 key_block[TLS_MAX_KEY_BLOCK_LEN];
+       u8 *pos;
+       size_t key_block_len;
+
+       if (pre_master_secret) {
+               wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret",
+                               pre_master_secret, pre_master_secret_len);
+               os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+               os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+                         TLS_RANDOM_LEN);
+               if (tls_prf(conn->rl.tls_version,
+                           pre_master_secret, pre_master_secret_len,
+                           "master secret", seed, 2 * TLS_RANDOM_LEN,
+                           conn->master_secret, TLS_MASTER_SECRET_LEN)) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive "
+                                  "master_secret");
+                       return -1;
+               }
+               wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret",
+                               conn->master_secret, TLS_MASTER_SECRET_LEN);
+       }
+
+       os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+       os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN);
+       key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len);
+       if (conn->rl.tls_version == TLS_VERSION_1)
+               key_block_len += 2 * conn->rl.iv_size;
+       if (tls_prf(conn->rl.tls_version,
+                   conn->master_secret, TLS_MASTER_SECRET_LEN,
+                   "key expansion", seed, 2 * TLS_RANDOM_LEN,
+                   key_block, key_block_len)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block");
+               return -1;
+       }
+       wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block",
+                       key_block, key_block_len);
+
+       pos = key_block;
+
+       /* client_write_MAC_secret */
+       os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size);
+       pos += conn->rl.hash_size;
+       /* server_write_MAC_secret */
+       os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size);
+       pos += conn->rl.hash_size;
+
+       /* client_write_key */
+       os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len);
+       pos += conn->rl.key_material_len;
+       /* server_write_key */
+       os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len);
+       pos += conn->rl.key_material_len;
+
+       if (conn->rl.tls_version == TLS_VERSION_1) {
+               /* client_write_IV */
+               os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size);
+               pos += conn->rl.iv_size;
+               /* server_write_IV */
+               os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
+               pos += conn->rl.iv_size;
+       } else {
+               /*
+                * Use IV field to set the mask value for TLS v1.1. A fixed
+                * mask of zero is used per the RFC 4346, 6.2.3.2 CBC Block
+                * Cipher option 2a.
+                */
+               os_memset(conn->rl.write_iv, 0, conn->rl.iv_size);
+       }
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_client_handshake - Process TLS handshake
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @in_data: Input data from TLS peer
+ * @in_len: Input data length
+ * @out_len: Length of the output buffer.
+ * @appl_data: Pointer to application data pointer, or %NULL if dropped
+ * @appl_data_len: Pointer to variable that is set to appl_data length
+ * @need_more_data: Set to 1 if more data would be needed to complete
+ *     processing
+ * Returns: Pointer to output data, %NULL on failure
+ */
+u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
+                           const u8 *in_data, size_t in_len,
+                           size_t *out_len, u8 **appl_data,
+                           size_t *appl_data_len, int *need_more_data)
+{
+       const u8 *pos, *end;
+       u8 *msg = NULL, *in_msg = NULL, *in_pos, *in_end, alert, ct;
+       size_t in_msg_len;
+       int no_appl_data;
+       int used;
+
+       if (need_more_data)
+               *need_more_data = 0;
+
+       if (conn->state == CLIENT_HELLO) {
+               if (in_len)
+                       return NULL;
+               return tls_send_client_hello(conn, out_len);
+       }
+
+       if (conn->partial_input) {
+               if (wpabuf_resize(&conn->partial_input, in_len) < 0) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+                                  "memory for pending record");
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_INTERNAL_ERROR);
+                       goto failed;
+               }
+               wpabuf_put_data(conn->partial_input, in_data, in_len);
+               in_data = wpabuf_head(conn->partial_input);
+               in_len = wpabuf_len(conn->partial_input);
+       }
+
+       if (in_data == NULL || in_len == 0)
+               return NULL;
+
+       pos = in_data;
+       end = in_data + in_len;
+       in_msg = os_malloc(in_len);
+       if (in_msg == NULL)
+               return NULL;
+
+       /* Each received packet may include multiple records */
+       while (pos < end) {
+               in_msg_len = in_len;
+               used = tlsv1_record_receive(&conn->rl, pos, end - pos,
+                                           in_msg, &in_msg_len, &alert);
+               if (used < 0) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
+                                  "record failed");
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+                       goto failed;
+               }
+               if (used == 0) {
+                       struct wpabuf *partial;
+                       wpa_printf(MSG_DEBUG, "TLSv1: Need more data");
+                       partial = wpabuf_alloc_copy(pos, end - pos);
+                       wpabuf_free(conn->partial_input);
+                       conn->partial_input = partial;
+                       if (conn->partial_input == NULL) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
+                                          "allocate memory for pending "
+                                          "record");
+                               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                         TLS_ALERT_INTERNAL_ERROR);
+                               goto failed;
+                       }
+                       os_free(in_msg);
+                       if (need_more_data)
+                               *need_more_data = 1;
+                       return NULL;
+               }
+               ct = pos[0];
+
+               in_pos = in_msg;
+               in_end = in_msg + in_msg_len;
+
+               /* Each received record may include multiple messages of the
+                * same ContentType. */
+               while (in_pos < in_end) {
+                       in_msg_len = in_end - in_pos;
+                       if (tlsv1_client_process_handshake(conn, ct, in_pos,
+                                                          &in_msg_len,
+                                                          appl_data,
+                                                          appl_data_len) < 0)
+                               goto failed;
+                       in_pos += in_msg_len;
+               }
+
+               pos += used;
+       }
+
+       os_free(in_msg);
+       in_msg = NULL;
+
+       no_appl_data = appl_data == NULL || *appl_data == NULL;
+       msg = tlsv1_client_handshake_write(conn, out_len, no_appl_data);
+
+failed:
+       os_free(in_msg);
+       if (conn->alert_level) {
+               wpabuf_free(conn->partial_input);
+               conn->partial_input = NULL;
+               conn->state = FAILED;
+               os_free(msg);
+               msg = tlsv1_client_send_alert(conn, conn->alert_level,
+                                             conn->alert_description,
+                                             out_len);
+       } else if (msg == NULL) {
+               msg = (u8 *)os_zalloc(1);
+               *out_len = 0;
+       }
+
+       if (need_more_data == NULL || !(*need_more_data)) {
+               wpabuf_free(conn->partial_input);
+               conn->partial_input = NULL;
+       }
+
+       return msg;
+}
+
+
+/**
+ * tlsv1_client_encrypt - Encrypt data into TLS tunnel
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @in_data: Pointer to plaintext data to be encrypted
+ * @in_len: Input buffer length
+ * @out_data: Pointer to output buffer (encrypted TLS data)
+ * @out_len: Maximum out_data length 
+ * Returns: Number of bytes written to out_data, -1 on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * send data in the encrypted tunnel.
+ */
+int tlsv1_client_encrypt(struct tlsv1_client *conn,
+                        const u8 *in_data, size_t in_len,
+                        u8 *out_data, size_t out_len)
+{
+       size_t rlen;
+
+       wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
+                       in_data, in_len);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
+                             out_data, out_len, in_data, in_len, &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       return rlen;
+}
+
+
+/**
+ * tlsv1_client_decrypt - Decrypt data from TLS tunnel
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @in_data: Pointer to input buffer (encrypted TLS data)
+ * @in_len: Input buffer length
+ * @need_more_data: Set to 1 if more data would be needed to complete
+ *     processing
+ * Returns: Decrypted data or %NULL on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * receive data from the encrypted tunnel.
+ */
+struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn,
+                                    const u8 *in_data, size_t in_len,
+                                    int *need_more_data)
+{
+       const u8 *in_end, *pos;
+       int used;
+       u8 alert, *out_pos, ct;
+       size_t olen;
+       struct wpabuf *buf = NULL;
+
+       if (need_more_data)
+               *need_more_data = 0;
+
+       if (conn->partial_input) {
+               if (wpabuf_resize(&conn->partial_input, in_len) < 0) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+                                  "memory for pending record");
+                       alert = TLS_ALERT_INTERNAL_ERROR;
+                       goto fail;
+               }
+               wpabuf_put_data(conn->partial_input, in_data, in_len);
+               in_data = wpabuf_head(conn->partial_input);
+               in_len = wpabuf_len(conn->partial_input);
+       }
+
+       pos = in_data;
+       in_end = in_data + in_len;
+
+       while (pos < in_end) {
+               ct = pos[0];
+               if (wpabuf_resize(&buf, in_end - pos) < 0) {
+                       alert = TLS_ALERT_INTERNAL_ERROR;
+                       goto fail;
+               }
+               out_pos = wpabuf_put(buf, 0);
+               olen = wpabuf_tailroom(buf);
+               used = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
+                                           out_pos, &olen, &alert);
+               if (used < 0) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
+                                  "failed");
+                       goto fail;
+               }
+               if (used == 0) {
+                       struct wpabuf *partial;
+                       wpa_printf(MSG_DEBUG, "TLSv1: Need more data");
+                       partial = wpabuf_alloc_copy(pos, in_end - pos);
+                       wpabuf_free(conn->partial_input);
+                       conn->partial_input = partial;
+                       if (conn->partial_input == NULL) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
+                                          "allocate memory for pending "
+                                          "record");
+                               alert = TLS_ALERT_INTERNAL_ERROR;
+                               goto fail;
+                       }
+                       if (need_more_data)
+                               *need_more_data = 1;
+                       return buf;
+               }
+
+               if (ct == TLS_CONTENT_TYPE_ALERT) {
+                       if (olen < 2) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: Alert "
+                                          "underflow");
+                               alert = TLS_ALERT_DECODE_ERROR;
+                               goto fail;
+                       }
+                       wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
+                                  out_pos[0], out_pos[1]);
+                       if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) {
+                               /* Continue processing */
+                               pos += used;
+                               continue;
+                       }
+
+                       alert = out_pos[1];
+                       goto fail;
+               }
+
+               if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
+                                  "0x%x when decrypting application data",
+                                  pos[0]);
+                       alert = TLS_ALERT_UNEXPECTED_MESSAGE;
+                       goto fail;
+               }
+
+               wpabuf_put(buf, olen);
+
+               pos += used;
+       }
+
+       wpabuf_free(conn->partial_input);
+       conn->partial_input = NULL;
+       return buf;
+
+fail:
+       wpabuf_free(buf);
+       wpabuf_free(conn->partial_input);
+       conn->partial_input = NULL;
+       tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+       return NULL;
+}
+
+
+/**
+ * tlsv1_client_global_init - Initialize TLSv1 client
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function must be called before using any other TLSv1 client functions.
+ */
+int tlsv1_client_global_init(void)
+{
+       return crypto_global_init();
+}
+
+
+/**
+ * tlsv1_client_global_deinit - Deinitialize TLSv1 client
+ *
+ * This function can be used to deinitialize the TLSv1 client that was
+ * initialized by calling tlsv1_client_global_init(). No TLSv1 client functions
+ * can be called after this before calling tlsv1_client_global_init() again.
+ */
+void tlsv1_client_global_deinit(void)
+{
+       crypto_global_deinit();
+}
+
+
+/**
+ * tlsv1_client_init - Initialize TLSv1 client connection
+ * Returns: Pointer to TLSv1 client connection data or %NULL on failure
+ */
+struct tlsv1_client * tlsv1_client_init(void)
+{
+       struct tlsv1_client *conn;
+       size_t count;
+       u16 *suites;
+
+       conn = (struct tlsv1_client *)os_zalloc(sizeof(*conn));
+       if (conn == NULL)
+               return NULL;
+
+       conn->state = CLIENT_HELLO;
+
+       if (tls_verify_hash_init(&conn->verify) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify "
+                          "hash");
+               os_free(conn);
+               return NULL;
+       }
+
+       count = 0;
+       suites = conn->cipher_suites;
+       suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256;
+       suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+       suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
+       suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+       suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
+       suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
+       suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
+       conn->num_cipher_suites = count;
+
+       conn->rl.tls_version = TLS_VERSION;
+
+       return conn;
+}
+
+
+/**
+ * tlsv1_client_deinit - Deinitialize TLSv1 client connection
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ */
+void tlsv1_client_deinit(struct tlsv1_client *conn)
+{
+       crypto_public_key_free(conn->server_rsa_key);
+       tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
+       tlsv1_record_change_write_cipher(&conn->rl);
+       tlsv1_record_change_read_cipher(&conn->rl);
+       tls_verify_hash_free(&conn->verify);
+       os_free(conn->client_hello_ext);
+       tlsv1_client_free_dh(conn);
+       tlsv1_cred_free(conn->cred);
+       wpabuf_free(conn->partial_input);
+       os_free(conn);
+}
+
+
+/**
+ * tlsv1_client_established - Check whether connection has been established
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: 1 if connection is established, 0 if not
+ */
+int tlsv1_client_established(struct tlsv1_client *conn)
+{
+       return conn->state == ESTABLISHED;
+}
+
+
+/**
+ * tlsv1_client_prf - Use TLS-PRF to derive keying material
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @label: Label (e.g., description of the key) for PRF
+ * @server_random_first: seed is 0 = client_random|server_random,
+ * 1 = server_random|client_random
+ * @out: Buffer for output data from TLS-PRF
+ * @out_len: Length of the output buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
+                    int server_random_first, u8 *out, size_t out_len)
+{
+       u8 seed[2 * TLS_RANDOM_LEN];
+
+       if (conn->state != ESTABLISHED)
+               return -1;
+
+       if (server_random_first) {
+               os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+               os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random,
+                         TLS_RANDOM_LEN);
+       } else {
+               os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+               os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+                         TLS_RANDOM_LEN);
+       }
+
+       return tls_prf(conn->rl.tls_version,
+                      conn->master_secret, TLS_MASTER_SECRET_LEN,
+                      label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
+}
+
+
+/**
+ * tlsv1_client_get_cipher - Get current cipher name
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @buf: Buffer for the cipher name
+ * @buflen: buf size
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get the name of the currently used cipher.
+ */
+int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
+                           size_t buflen)
+{
+#ifndef ESPRESSIF_USE  
+       char *cipher;
+
+       switch (conn->rl.cipher_suite) {
+       case TLS_RSA_WITH_RC4_128_MD5:
+               cipher = "RC4-MD5";
+               break;
+       case TLS_RSA_WITH_RC4_128_SHA:
+               cipher = "RC4-SHA";
+               break;
+       case TLS_RSA_WITH_DES_CBC_SHA:
+               cipher = "DES-CBC-SHA";
+               break;
+       case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+               cipher = "DES-CBC3-SHA";
+               break;
+       case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+               cipher = "ADH-AES-128-SHA256";
+               break;
+       case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+               cipher = "ADH-AES-128-SHA";
+               break;
+       case TLS_RSA_WITH_AES_256_CBC_SHA:
+               cipher = "AES-256-SHA";
+               break;
+       case TLS_RSA_WITH_AES_256_CBC_SHA256:
+               cipher = "AES-256-SHA256";
+               break;
+       case TLS_RSA_WITH_AES_128_CBC_SHA:
+               cipher = "AES-128-SHA";
+               break;
+       case TLS_RSA_WITH_AES_128_CBC_SHA256:
+               cipher = "AES-128-SHA256";
+               break;
+       default:
+               return -1;
+       }
+
+       os_memcpy((u8 *)buf, (u8 *)cipher, buflen);
+
+       return 0;
+#else
+    char cipher[20];
+
+    switch (conn->rl.cipher_suite) {
+        case TLS_RSA_WITH_RC4_128_MD5:
+            strcpy(cipher, "RC4-MD5");
+            break;
+        case TLS_RSA_WITH_RC4_128_SHA:
+            strcpy(cipher, "RC4-SHA");
+            break;
+        case TLS_RSA_WITH_DES_CBC_SHA:
+            strcpy(cipher, "DES-CBC-SHA");
+            break;
+        case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+            strcpy(cipher, "DES-CBC3-SHA");
+            break;
+        case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+            strcpy(cipher, "ADH-AES-128-SHA256");
+            break;
+        case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+            strcpy(cipher, "ADH-AES-128-SHA");
+            break;
+        case TLS_RSA_WITH_AES_256_CBC_SHA:
+            strcpy(cipher, "AES-256-SHA");
+            break;
+        case TLS_RSA_WITH_AES_256_CBC_SHA256:
+            strcpy(cipher, "AES-256-SHA256");
+            break;
+        case TLS_RSA_WITH_AES_128_CBC_SHA:
+            strcpy(cipher, "AES-128-SHA");
+            break;
+        case TLS_RSA_WITH_AES_128_CBC_SHA256:
+            strcpy(cipher, "AES-128-SHA256");
+            break;
+        default:
+            return -1;
+    }
+
+    os_memcpy((u8 *)buf, (u8 *)cipher, buflen);
+
+    return 0;
+#endif
+}
+
+
+/**
+ * tlsv1_client_shutdown - Shutdown TLS connection
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_shutdown(struct tlsv1_client *conn)
+{
+       conn->state = CLIENT_HELLO;
+
+       if (tls_verify_hash_init(&conn->verify) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify "
+                          "hash");
+               return -1;
+       }
+
+       tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
+       tlsv1_record_change_write_cipher(&conn->rl);
+       tlsv1_record_change_read_cipher(&conn->rl);
+
+       conn->certificate_requested = 0;
+       crypto_public_key_free(conn->server_rsa_key);
+       conn->server_rsa_key = NULL;
+       conn->session_resumed = 0;
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_client_resumed - Was session resumption used
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: 1 if current session used session resumption, 0 if not
+ */
+int tlsv1_client_resumed(struct tlsv1_client *conn)
+{
+       return !!conn->session_resumed;
+}
+
+
+/**
+ * tlsv1_client_hello_ext - Set TLS extension for ClientHello
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @ext_type: Extension type
+ * @data: Extension payload (%NULL to remove extension)
+ * @data_len: Extension payload length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type,
+                          const u8 *data, size_t data_len)
+{
+       u8 *pos;
+
+       conn->session_ticket_included = 0;
+       os_free(conn->client_hello_ext);
+       conn->client_hello_ext = NULL;
+       conn->client_hello_ext_len = 0;
+
+       if (data == NULL || data_len == 0)
+               return 0;
+
+       pos = conn->client_hello_ext = os_malloc(6 + data_len);
+       if (pos == NULL)
+               return -1;
+
+       WPA_PUT_BE16(pos, 4 + data_len);
+       pos += 2;
+       WPA_PUT_BE16(pos, ext_type);
+       pos += 2;
+       WPA_PUT_BE16(pos, data_len);
+       pos += 2;
+       os_memcpy(pos, data, data_len);
+       conn->client_hello_ext_len = 6 + data_len;
+
+       if (ext_type == TLS_EXT_PAC_OPAQUE) {
+               conn->session_ticket_included = 1;
+               wpa_printf(MSG_DEBUG, "TLSv1: Using session ticket");
+       }
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_client_get_keys - Get master key and random data from TLS connection
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @keys: Structure of key/random data (filled on success)
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys)
+{
+       os_memset(keys, 0, sizeof(*keys));
+       if (conn->state == CLIENT_HELLO)
+               return -1;
+
+       keys->client_random = conn->client_random;
+       keys->client_random_len = TLS_RANDOM_LEN;
+
+       if (conn->state != SERVER_HELLO) {
+               keys->server_random = conn->server_random;
+               keys->server_random_len = TLS_RANDOM_LEN;
+               keys->master_key = conn->master_secret;
+               keys->master_key_len = TLS_MASTER_SECRET_LEN;
+       }
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_client_get_keyblock_size - Get TLS key_block size
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: Size of the key_block for the negotiated cipher suite or -1 on
+ * failure
+ */
+int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn)
+{
+       if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO)
+               return -1;
+
+       return 2 * (conn->rl.hash_size + conn->rl.key_material_len +
+                   conn->rl.iv_size);
+}
+
+
+/**
+ * tlsv1_client_set_cipher_list - Configure acceptable cipher suites
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
+ * (TLS_CIPHER_*).
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers)
+{
+       size_t count;
+       u16 *suites;
+
+       /* TODO: implement proper configuration of cipher suites */
+       if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) {
+               count = 0;
+               suites = conn->cipher_suites;
+               suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA256;
+               suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
+               suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA256;
+               suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
+               suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
+               suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5;
+               suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA;
+
+               /*
+                * Cisco AP (at least 350 and 1200 series) local authentication
+                * server does not know how to search cipher suites from the
+                * list and seem to require that the last entry in the list is
+                * the one that it wants to use. However, TLS specification
+                * requires the list to be in the client preference order. As a
+                * workaround, add anon-DH AES-128-SHA1 again at the end of the
+                * list to allow the Cisco code to find it.
+                */
+               suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
+               conn->num_cipher_suites = count;
+       }
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_client_set_cred - Set client credentials
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @cred: Credentials from tlsv1_cred_alloc()
+ * Returns: 0 on success, -1 on failure
+ *
+ * On success, the client takes ownership of the credentials block and caller
+ * must not free it. On failure, caller is responsible for freeing the
+ * credential block.
+ */
+int tlsv1_client_set_cred(struct tlsv1_client *conn,
+                         struct tlsv1_credentials *cred)
+{
+       tlsv1_cred_free(conn->cred);
+       conn->cred = cred;
+       return 0;
+}
+
+
+void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled)
+{
+       conn->disable_time_checks = !enabled;
+}
+
+
+void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn,
+                                       tlsv1_client_session_ticket_cb cb,
+                                       void *ctx)
+{
+       wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)",
+                  cb, ctx);
+       conn->session_ticket_cb = cb;
+       conn->session_ticket_cb_ctx = ctx;
+}
diff --git a/components/wpa_supplicant/src/wpa2/tls/tlsv1_client_read.c b/components/wpa_supplicant/src/wpa2/tls/tlsv1_client_read.c
new file mode 100644 (file)
index 0000000..13330bd
--- /dev/null
@@ -0,0 +1,1001 @@
+/*
+ * TLSv1 client - read handshake message
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/tls/x509v3.h"
+#include "wpa2/tls/tlsv1_common.h"
+#include "wpa2/tls/tlsv1_record.h"
+#include "wpa2/tls/tlsv1_client.h"
+#include "wpa2/tls/tlsv1_client_i.h"
+#include "wpa2/eap_peer/eap_i.h"
+
+static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
+                                          const u8 *in_data, size_t *in_len);
+static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct,
+                                          const u8 *in_data, size_t *in_len);
+static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct,
+                                        const u8 *in_data, size_t *in_len);
+
+
+static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct,
+                                   const u8 *in_data, size_t *in_len)
+{
+       const u8 *pos, *end;
+       size_t left, len, i;
+       u16 cipher_suite;
+       u16 tls_version;
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+                          "received content type 0x%x", ct);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 4)
+               goto decode_error;
+
+       /* HandshakeType msg_type */
+       if (*pos != TLS_HANDSHAKE_TYPE_SERVER_HELLO) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+                          "message %d (expected ServerHello)", *pos);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHello");
+       pos++;
+       /* uint24 length */
+       len = WPA_GET_BE24(pos);
+       pos += 3;
+       left -= 4;
+
+       if (len > left)
+               goto decode_error;
+
+       /* body - ServerHello */
+
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerHello", pos, len);
+       end = pos + len;
+
+       /* ProtocolVersion server_version */
+       if (end - pos < 2)
+               goto decode_error;
+       tls_version = WPA_GET_BE16(pos);
+       if (!tls_version_ok(tls_version)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
+                          "ServerHello %u.%u", pos[0], pos[1]);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_PROTOCOL_VERSION);
+               return -1;
+       }
+       pos += 2;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s",
+                  tls_version_str(tls_version));
+       conn->rl.tls_version = tls_version;
+
+       /* Random random */
+       if (end - pos < TLS_RANDOM_LEN)
+               goto decode_error;
+
+       os_memcpy(conn->server_random, pos, TLS_RANDOM_LEN);
+       pos += TLS_RANDOM_LEN;
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random",
+                   conn->server_random, TLS_RANDOM_LEN);
+
+       /* SessionID session_id */
+       if (end - pos < 1)
+               goto decode_error;
+       if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN)
+               goto decode_error;
+       if (conn->session_id_len && conn->session_id_len == *pos &&
+           os_memcmp(conn->session_id, pos + 1, conn->session_id_len) == 0) {
+               pos += 1 + conn->session_id_len;
+               wpa_printf(MSG_DEBUG, "TLSv1: Resuming old session");
+               conn->session_resumed = 1;
+       } else {
+               conn->session_id_len = *pos;
+               pos++;
+               os_memcpy(conn->session_id, pos, conn->session_id_len);
+               pos += conn->session_id_len;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id",
+                   conn->session_id, conn->session_id_len);
+
+       /* CipherSuite cipher_suite */
+       if (end - pos < 2)
+               goto decode_error;
+       cipher_suite = WPA_GET_BE16(pos);
+       pos += 2;
+       for (i = 0; i < conn->num_cipher_suites; i++) {
+               if (cipher_suite == conn->cipher_suites[i])
+                       break;
+       }
+       if (i == conn->num_cipher_suites) {
+               wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected "
+                          "cipher suite 0x%04x", cipher_suite);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_ILLEGAL_PARAMETER);
+               return -1;
+       }
+
+       if (conn->session_resumed && cipher_suite != conn->prev_cipher_suite) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Server selected a different "
+                          "cipher suite for a resumed connection (0x%04x != "
+                          "0x%04x)", cipher_suite, conn->prev_cipher_suite);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_ILLEGAL_PARAMETER);
+               return -1;
+       }
+
+       if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for "
+                          "record layer");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       conn->prev_cipher_suite = cipher_suite;
+
+       /* CompressionMethod compression_method */
+       if (end - pos < 1)
+               goto decode_error;
+       if (*pos != TLS_COMPRESSION_NULL) {
+               wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected "
+                          "compression 0x%02x", *pos);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_ILLEGAL_PARAMETER);
+               return -1;
+       }
+       pos++;
+
+       if (end != pos) {
+               /* TODO: ServerHello extensions */
+               wpa_hexdump(MSG_DEBUG, "TLSv1: Unexpected extra data in the "
+                           "end of ServerHello", pos, end - pos);
+               goto decode_error;
+       }
+
+       if (conn->session_ticket_included && conn->session_ticket_cb) {
+               /* TODO: include SessionTicket extension if one was included in
+                * ServerHello */
+               int res = conn->session_ticket_cb(
+                       conn->session_ticket_cb_ctx, NULL, 0,
+                       conn->client_random, conn->server_random,
+                       conn->master_secret);
+               if (res < 0) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback "
+                                  "indicated failure");
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_HANDSHAKE_FAILURE);
+                       return -1;
+               }
+               conn->use_session_ticket = !!res;
+       }
+
+       if ((conn->session_resumed || conn->use_session_ticket) &&
+           tls_derive_keys(conn, NULL, 0)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       *in_len = end - in_data;
+
+       conn->state = (conn->session_resumed || conn->use_session_ticket) ?
+               SERVER_CHANGE_CIPHER_SPEC : SERVER_CERTIFICATE;
+
+       return 0;
+
+decode_error:
+       wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ServerHello");
+       tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+       return -1;
+}
+
+
+static int tls_process_certificate(struct tlsv1_client *conn, u8 ct,
+                                  const u8 *in_data, size_t *in_len)
+{
+       const u8 *pos, *end;
+       size_t left, len, list_len, cert_len, idx;
+       u8 type;
+       struct x509_certificate *chain = NULL, *last = NULL, *cert;
+       int reason;
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+                          "received content type 0x%x", ct);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 4) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message "
+                          "(len=%lu)", (unsigned long) left);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       type = *pos++;
+       len = WPA_GET_BE24(pos);
+       pos += 3;
+       left -= 4;
+
+       if (len > left) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message "
+                          "length (len=%lu != left=%lu)",
+                          (unsigned long) len, (unsigned long) left);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       if (type == TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE)
+               return tls_process_server_key_exchange(conn, ct, in_data,
+                                                      in_len);
+       if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST)
+               return tls_process_certificate_request(conn, ct, in_data,
+                                                      in_len);
+       if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
+               return tls_process_server_hello_done(conn, ct, in_data,
+                                                    in_len);
+       if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+                          "message %d (expected Certificate/"
+                          "ServerKeyExchange/CertificateRequest/"
+                          "ServerHelloDone)", type);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Received Certificate (certificate_list len %lu)",
+                  (unsigned long) len);
+
+       /*
+        * opaque ASN.1Cert<2^24-1>;
+        *
+        * struct {
+        *     ASN.1Cert certificate_list<1..2^24-1>;
+        * } Certificate;
+        */
+
+       end = pos + len;
+
+       if (end - pos < 3) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate "
+                          "(left=%lu)", (unsigned long) left);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       list_len = WPA_GET_BE24(pos);
+       pos += 3;
+
+       if ((size_t) (end - pos) != list_len) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list "
+                          "length (len=%lu left=%lu)",
+                          (unsigned long) list_len,
+                          (unsigned long) (end - pos));
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       idx = 0;
+       while (pos < end) {
+               if (end - pos < 3) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+                                  "certificate_list");
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_DECODE_ERROR);
+                       x509_certificate_chain_free(chain);
+                       return -1;
+               }
+
+               cert_len = WPA_GET_BE24(pos);
+               pos += 3;
+
+               if ((size_t) (end - pos) < cert_len) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate "
+                                  "length (len=%lu left=%lu)",
+                                  (unsigned long) cert_len,
+                                  (unsigned long) (end - pos));
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_DECODE_ERROR);
+                       x509_certificate_chain_free(chain);
+                       return -1;
+               }
+
+               wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)",
+                          (unsigned long) idx, (unsigned long) cert_len);
+
+               if (idx == 0) {
+                       crypto_public_key_free(conn->server_rsa_key);
+                       if (tls_parse_cert(pos, cert_len,
+                                          &conn->server_rsa_key)) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+                                          "the certificate");
+                               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                         TLS_ALERT_BAD_CERTIFICATE);
+                               x509_certificate_chain_free(chain);
+                               return -1;
+                       }
+               }
+
+               cert = x509_certificate_parse(pos, cert_len);
+               if (cert == NULL) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+                                  "the certificate");
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_BAD_CERTIFICATE);
+                       x509_certificate_chain_free(chain);
+                       return -1;
+               }
+
+               if (last == NULL)
+                       chain = cert;
+               else
+                       last->next = cert;
+               last = cert;
+
+               idx++;
+               pos += cert_len;
+       }
+
+       if (conn->cred &&
+           x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
+                                           &reason, conn->disable_time_checks)
+           < 0) {
+               int tls_reason;
+               wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
+                          "validation failed (reason=%d)", reason);
+               switch (reason) {
+               case X509_VALIDATE_BAD_CERTIFICATE:
+                       tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+                       break;
+               case X509_VALIDATE_UNSUPPORTED_CERTIFICATE:
+                       tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE;
+                       break;
+               case X509_VALIDATE_CERTIFICATE_REVOKED:
+                       tls_reason = TLS_ALERT_CERTIFICATE_REVOKED;
+                       break;
+               case X509_VALIDATE_CERTIFICATE_EXPIRED:
+                       tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED;
+                       break;
+               case X509_VALIDATE_CERTIFICATE_UNKNOWN:
+                       tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN;
+                       break;
+               case X509_VALIDATE_UNKNOWN_CA:
+                       tls_reason = TLS_ALERT_UNKNOWN_CA;
+                       break;
+               default:
+                       tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+                       break;
+               }
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason);
+               x509_certificate_chain_free(chain);
+               return -1;
+       }
+
+       x509_certificate_chain_free(chain);
+
+       *in_len = end - in_data;
+       conn->state = SERVER_KEY_EXCHANGE;
+
+       return 0;
+}
+
+
+static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
+                                       const u8 *buf, size_t len)
+{
+       const u8 *pos, *end;
+
+       tlsv1_client_free_dh(conn);
+
+       pos = buf;
+       end = buf + len;
+
+       if (end - pos < 3)
+               goto fail;
+       conn->dh_p_len = WPA_GET_BE16(pos);
+       pos += 2;
+       if (conn->dh_p_len == 0 || end - pos < (int) conn->dh_p_len) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %lu",
+                          (unsigned long) conn->dh_p_len);
+               goto fail;
+       }
+       conn->dh_p = os_malloc(conn->dh_p_len);
+       if (conn->dh_p == NULL)
+               goto fail;
+       os_memcpy(conn->dh_p, pos, conn->dh_p_len);
+       pos += conn->dh_p_len;
+       wpa_hexdump(MSG_DEBUG, "TLSv1: DH p (prime)",
+                   conn->dh_p, conn->dh_p_len);
+
+       if (end - pos < 3)
+               goto fail;
+       conn->dh_g_len = WPA_GET_BE16(pos);
+       pos += 2;
+       if (conn->dh_g_len == 0 || end - pos < (int) conn->dh_g_len)
+               goto fail;
+       conn->dh_g = os_malloc(conn->dh_g_len);
+       if (conn->dh_g == NULL)
+               goto fail;
+       os_memcpy(conn->dh_g, pos, conn->dh_g_len);
+       pos += conn->dh_g_len;
+       wpa_hexdump(MSG_DEBUG, "TLSv1: DH g (generator)",
+                   conn->dh_g, conn->dh_g_len);
+       if (conn->dh_g_len == 1 && conn->dh_g[0] < 2)
+               goto fail;
+
+       if (end - pos < 3)
+               goto fail;
+       conn->dh_ys_len = WPA_GET_BE16(pos);
+       pos += 2;
+       if (conn->dh_ys_len == 0 || end - pos < (int) conn->dh_ys_len)
+               goto fail;
+       conn->dh_ys = os_malloc(conn->dh_ys_len);
+       if (conn->dh_ys == NULL)
+               goto fail;
+       os_memcpy(conn->dh_ys, pos, conn->dh_ys_len);
+       pos += conn->dh_ys_len;
+       wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)",
+                   conn->dh_ys, conn->dh_ys_len);
+
+       return 0;
+
+fail:
+       wpa_printf(MSG_DEBUG, "TLSv1: Processing DH params failed");
+       tlsv1_client_free_dh(conn);
+       return -1;
+}
+
+
+static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
+                                          const u8 *in_data, size_t *in_len)
+{
+       const u8 *pos, *end;
+       size_t left, len;
+       u8 type;
+       const struct tls_cipher_suite *suite;
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+                          "received content type 0x%x", ct);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 4) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerKeyExchange "
+                          "(Left=%lu)", (unsigned long) left);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       type = *pos++;
+       len = WPA_GET_BE24(pos);
+       pos += 3;
+       left -= 4;
+
+       if (len > left) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerKeyExchange "
+                          "length (len=%lu != left=%lu)",
+                          (unsigned long) len, (unsigned long) left);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       end = pos + len;
+
+       if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST)
+               return tls_process_certificate_request(conn, ct, in_data,
+                                                      in_len);
+       if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
+               return tls_process_server_hello_done(conn, ct, in_data,
+                                                    in_len);
+       if (type != TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+                          "message %d (expected ServerKeyExchange/"
+                          "CertificateRequest/ServerHelloDone)", type);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Received ServerKeyExchange");
+
+       if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not allowed "
+                          "with the selected cipher suite");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "TLSv1: ServerKeyExchange", pos, len);
+       suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+       if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) {
+               if (tlsv1_process_diffie_hellman(conn, pos, len) < 0) {
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_DECODE_ERROR);
+                       return -1;
+               }
+       } else {
+               wpa_printf(MSG_DEBUG, "TLSv1: UnexpectedServerKeyExchange");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       *in_len = end - in_data;
+       conn->state = SERVER_CERTIFICATE_REQUEST;
+
+       return 0;
+}
+
+
+static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct,
+                                          const u8 *in_data, size_t *in_len)
+{
+       const u8 *pos, *end;
+       size_t left, len;
+       u8 type;
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+                          "received content type 0x%x", ct);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 4) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateRequest "
+                          "(left=%lu)", (unsigned long) left);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       type = *pos++;
+       len = WPA_GET_BE24(pos);
+       pos += 3;
+       left -= 4;
+
+       if (len > left) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in CertificateRequest "
+                          "length (len=%lu != left=%lu)",
+                          (unsigned long) len, (unsigned long) left);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       end = pos + len;
+
+       if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
+               return tls_process_server_hello_done(conn, ct, in_data,
+                                                    in_len);
+       if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+                          "message %d (expected CertificateRequest/"
+                          "ServerHelloDone)", type);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateRequest");
+
+       conn->certificate_requested = 1;
+
+       *in_len = end - in_data;
+       conn->state = SERVER_HELLO_DONE;
+
+       return 0;
+}
+
+
+static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct,
+                                        const u8 *in_data, size_t *in_len)
+{
+       const u8 *pos, *end;
+       size_t left, len;
+       u8 type;
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+                          "received content type 0x%x", ct);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 4) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerHelloDone "
+                          "(left=%lu)", (unsigned long) left);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       type = *pos++;
+       len = WPA_GET_BE24(pos);
+       pos += 3;
+       left -= 4;
+
+       if (len > left) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerHelloDone "
+                          "length (len=%lu != left=%lu)",
+                          (unsigned long) len, (unsigned long) left);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+       end = pos + len;
+
+       if (type != TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+                          "message %d (expected ServerHelloDone)", type);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHelloDone");
+
+       *in_len = end - in_data;
+       conn->state = CLIENT_KEY_EXCHANGE;
+
+       return 0;
+}
+
+
+static int tls_process_server_change_cipher_spec(struct tlsv1_client *conn,
+                                                u8 ct, const u8 *in_data,
+                                                size_t *in_len)
+{
+       const u8 *pos;
+       size_t left;
+
+       if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
+                          "received content type 0x%x", ct);
+               if (conn->use_session_ticket) {
+                       int res;
+                       wpa_printf(MSG_DEBUG, "TLSv1: Server may have "
+                                  "rejected SessionTicket");
+                       conn->use_session_ticket = 0;
+
+                       /* Notify upper layers that SessionTicket failed */
+                       res = conn->session_ticket_cb(
+                               conn->session_ticket_cb_ctx, NULL, 0, NULL,
+                               NULL, NULL);
+                       if (res < 0) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket "
+                                          "callback indicated failure");
+                               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                         TLS_ALERT_HANDSHAKE_FAILURE);
+                               return -1;
+                       }
+            printf("[Debug] set the state to server certificate \n");
+                       conn->state = SERVER_CERTIFICATE;
+                       return tls_process_certificate(conn, ct, in_data,
+                                                      in_len);
+               }
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 1) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       if (*pos != TLS_CHANGE_CIPHER_SPEC) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
+                          "received data 0x%x", *pos);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec");
+       if (tlsv1_record_change_read_cipher(&conn->rl) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher "
+                          "for record layer");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       *in_len = pos + 1 - in_data;
+       conn->state = SERVER_FINISHED;
+
+       return 0;
+}
+
+
+static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct,
+                                      const u8 *in_data, size_t *in_len)
+{
+       const u8 *pos, *end;
+       size_t left, len, hlen;
+       u8 verify_data[TLS_VERIFY_DATA_LEN];
+       u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; "
+                          "received content type 0x%x", ct);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 4) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for "
+                          "Finished",
+                          (unsigned long) left);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received "
+                          "type 0x%x", pos[0]);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       len = WPA_GET_BE24(pos + 1);
+
+       pos += 4;
+       left -= 4;
+
+       if (len > left) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished "
+                          "(len=%lu > left=%lu)",
+                          (unsigned long) len, (unsigned long) left);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+       end = pos + len;
+       if (len != TLS_VERIFY_DATA_LEN) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length "
+                          "in Finished: %lu (expected %d)",
+                          (unsigned long) len, TLS_VERIFY_DATA_LEN);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished",
+                   pos, TLS_VERIFY_DATA_LEN);
+
+#ifdef CONFIG_TLSV12
+       if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+               hlen = SHA256_MAC_LEN;
+               if (wpa2_crypto_funcs.crypto_hash_finish) {
+                       if (conn->verify.sha256_server == NULL ||
+                           wpa_crypto_funcs.crypto_hash_finish(conn->verify.sha256_server, hash, &hlen) < 0) {
+                               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR);
+                               conn->verify.sha256_server = NULL;
+                               return -1;
+                       }
+               } else {
+                       wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__);
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR);
+                       conn->verify.sha256_server = NULL;
+                       return -1;
+               }
+               conn->verify.sha256_server = NULL;
+       } else {
+#endif /* CONFIG_TLSV12 */
+
+       hlen = MD5_MAC_LEN;
+       if (conn->verify.md5_server == NULL ||
+           crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) {
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               conn->verify.md5_server = NULL;
+               crypto_hash_finish(conn->verify.sha1_server, NULL, NULL);
+               conn->verify.sha1_server = NULL;
+               return -1;
+       }
+       conn->verify.md5_server = NULL;
+       hlen = SHA1_MAC_LEN;
+       if (conn->verify.sha1_server == NULL ||
+           crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN,
+                              &hlen) < 0) {
+               conn->verify.sha1_server = NULL;
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       conn->verify.sha1_server = NULL;
+       hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+       }
+#endif /* CONFIG_TLSV12 */
+
+       if (tls_prf(conn->rl.tls_version,
+                   conn->master_secret, TLS_MASTER_SECRET_LEN,
+                   "server finished", hash, hlen,
+                   verify_data, TLS_VERIFY_DATA_LEN)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_DECRYPT_ERROR);
+               return -1;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
+                       verify_data, TLS_VERIFY_DATA_LEN);
+
+       if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
+               wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Received Finished");
+
+       *in_len = end - in_data;
+
+       conn->state = (conn->session_resumed || conn->use_session_ticket) ?
+               CHANGE_CIPHER_SPEC : ACK_FINISHED;
+       return 0;
+}
+
+
+static int tls_process_application_data(struct tlsv1_client *conn, u8 ct,
+                                       const u8 *in_data, size_t *in_len,
+                                       u8 **out_data, size_t *out_len)
+{
+       const u8 *pos;
+       size_t left;
+
+       if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Application Data; "
+                          "received content type 0x%x", ct);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       wpa_hexdump(MSG_DEBUG, "TLSv1: Application Data included in Handshake",
+                   pos, left);
+
+       *out_data = os_malloc(left);
+       if (*out_data) {
+               os_memcpy(*out_data, pos, left);
+               *out_len = left;
+       }
+
+       return 0;
+}
+
+
+int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct,
+                                  const u8 *buf, size_t *len,
+                                  u8 **out_data, size_t *out_len)
+{
+       if (ct == TLS_CONTENT_TYPE_ALERT) {
+               if (*len < 2) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow");
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_DECODE_ERROR);
+                       return -1;
+               }
+               wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
+                          buf[0], buf[1]);
+               *len = 2;
+               conn->state = FAILED;
+               return -1;
+       }
+
+       if (ct == TLS_CONTENT_TYPE_HANDSHAKE && *len >= 4 &&
+           buf[0] == TLS_HANDSHAKE_TYPE_HELLO_REQUEST) {
+               size_t hr_len = WPA_GET_BE24(buf + 1);
+               if (hr_len > *len - 4) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: HelloRequest underflow");
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_DECODE_ERROR);
+                       return -1;
+               }
+               wpa_printf(MSG_DEBUG, "TLSv1: Ignored HelloRequest");
+               *len = 4 + hr_len;
+               return 0;
+       }
+
+       switch (conn->state) {
+       case SERVER_HELLO:
+               if (tls_process_server_hello(conn, ct, buf, len))
+                       return -1;
+               break;
+       case SERVER_CERTIFICATE:
+               if (tls_process_certificate(conn, ct, buf, len))
+                       return -1;
+               break;
+       case SERVER_KEY_EXCHANGE:
+               if (tls_process_server_key_exchange(conn, ct, buf, len))
+                       return -1;
+               break;
+       case SERVER_CERTIFICATE_REQUEST:
+               if (tls_process_certificate_request(conn, ct, buf, len))
+                       return -1;
+               break;
+       case SERVER_HELLO_DONE:
+               if (tls_process_server_hello_done(conn, ct, buf, len))
+                       return -1;
+               break;
+       case SERVER_CHANGE_CIPHER_SPEC:
+               if (tls_process_server_change_cipher_spec(conn, ct, buf, len))
+                       return -1;
+               break;
+       case SERVER_FINISHED:
+               if (tls_process_server_finished(conn, ct, buf, len)) {
+                       printf("[debug] server finish process fall \n");
+                       return -1;
+               }
+               break;
+       case ACK_FINISHED:
+               if (out_data &&
+                   tls_process_application_data(conn, ct, buf, len, out_data,
+                                                out_len))
+                       return -1;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d "
+                          "while processing received message",
+                          conn->state);
+               return -1;
+       }
+
+       if (ct == TLS_CONTENT_TYPE_HANDSHAKE) {
+               tls_verify_hash_add(&conn->verify, buf, *len);
+       }
+
+       return 0;
+}
diff --git a/components/wpa_supplicant/src/wpa2/tls/tlsv1_client_write.c b/components/wpa_supplicant/src/wpa2/tls/tlsv1_client_write.c
new file mode 100644 (file)
index 0000000..55644a0
--- /dev/null
@@ -0,0 +1,901 @@
+/*
+ * TLSv1 client - write handshake message
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/tls/x509v3.h"
+#include "wpa2/tls/tlsv1_common.h"
+#include "wpa2/tls/tlsv1_record.h"
+#include "wpa2/tls/tlsv1_client.h"
+#include "wpa2/tls/tlsv1_client_i.h"
+
+#include "wpa2/eap_peer/eap_i.h"
+
+static size_t tls_client_cert_chain_der_len(struct tlsv1_client *conn)
+{
+       size_t len = 0;
+       struct x509_certificate *cert;
+
+       if (conn->cred == NULL)
+               return 0;
+
+       cert = conn->cred->cert;
+       while (cert) {
+               len += 3 + cert->cert_len;
+               if (x509_certificate_self_signed(cert))
+                       break;
+               cert = x509_certificate_get_subject(conn->cred->trusted_certs,
+                                                   &cert->issuer);
+       }
+
+       return len;
+}
+
+
+u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len)
+{
+       u8 *hello, *end, *pos, *hs_length, *hs_start, *rhdr;
+       struct os_time now;
+       size_t len, i;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello");
+       *out_len = 0;
+
+       os_get_time(&now);
+       WPA_PUT_BE32(conn->client_random, now.sec);
+       if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) {
+               wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
+                          "client_random");
+               return NULL;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random",
+                   conn->client_random, TLS_RANDOM_LEN);
+
+       len = 100 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len;
+       hello = os_malloc(len);
+       if (hello == NULL)
+               return NULL;
+       end = hello + len;
+
+       rhdr = hello;
+       pos = rhdr + TLS_RECORD_HEADER_LEN;
+
+       /* opaque fragment[TLSPlaintext.length] */
+
+       /* Handshake */
+       hs_start = pos;
+       /* HandshakeType msg_type */
+       *pos++ = TLS_HANDSHAKE_TYPE_CLIENT_HELLO;
+       /* uint24 length (to be filled) */
+       hs_length = pos;
+       pos += 3;
+       /* body - ClientHello */
+       /* ProtocolVersion client_version */
+       WPA_PUT_BE16(pos, TLS_VERSION);
+       pos += 2;
+       /* Random random: uint32 gmt_unix_time, opaque random_bytes */
+       os_memcpy(pos, conn->client_random, TLS_RANDOM_LEN);
+       pos += TLS_RANDOM_LEN;
+       /* SessionID session_id */
+       *pos++ = conn->session_id_len;
+       os_memcpy(pos, conn->session_id, conn->session_id_len);
+       pos += conn->session_id_len;
+       /* CipherSuite cipher_suites<2..2^16-1> */
+       WPA_PUT_BE16(pos, 2 * conn->num_cipher_suites);
+       pos += 2;
+       for (i = 0; i < conn->num_cipher_suites; i++) {
+               WPA_PUT_BE16(pos, conn->cipher_suites[i]);
+               pos += 2;
+       }
+       /* CompressionMethod compression_methods<1..2^8-1> */
+       *pos++ = 1;
+       *pos++ = TLS_COMPRESSION_NULL;
+
+       if (conn->client_hello_ext) {
+               os_memcpy(pos, conn->client_hello_ext,
+                         conn->client_hello_ext_len);
+               pos += conn->client_hello_ext_len;
+       }
+
+       WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+       tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+                             rhdr, end - rhdr, hs_start, pos - hs_start,
+                             out_len) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               os_free(hello);
+               return NULL;
+       }
+
+       conn->state = SERVER_HELLO;
+
+       return hello;
+}
+
+
+static int tls_write_client_certificate(struct tlsv1_client *conn,
+                                       u8 **msgpos, u8 *end)
+{
+       u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start;
+       size_t rlen;
+       struct x509_certificate *cert;
+
+       pos = *msgpos;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate");
+       rhdr = pos;
+       pos += TLS_RECORD_HEADER_LEN;
+
+       /* opaque fragment[TLSPlaintext.length] */
+
+       /* Handshake */
+       hs_start = pos;
+       /* HandshakeType msg_type */
+       *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE;
+       /* uint24 length (to be filled) */
+       hs_length = pos;
+       pos += 3;
+       /* body - Certificate */
+       /* uint24 length (to be filled) */
+       cert_start = pos;
+       pos += 3;
+       cert = conn->cred ? conn->cred->cert : NULL;
+       while (cert) {
+               if (pos + 3 + cert->cert_len > end) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space "
+                                  "for Certificate (cert_len=%lu left=%lu)",
+                                  (unsigned long) cert->cert_len,
+                                  (unsigned long) (end - pos));
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_INTERNAL_ERROR);
+                       return -1;
+               }
+               WPA_PUT_BE24(pos, cert->cert_len);
+               pos += 3;
+               os_memcpy(pos, cert->cert_start, cert->cert_len);
+               pos += cert->cert_len;
+
+               if (x509_certificate_self_signed(cert))
+                       break;
+               cert = x509_certificate_get_subject(conn->cred->trusted_certs,
+                                                   &cert->issuer);
+       }
+       if (conn->cred == NULL || cert == conn->cred->cert || cert == NULL) {
+               /*
+                * Client was not configured with all the needed certificates
+                * to form a full certificate chain. The server may fail to
+                * validate the chain unless it is configured with all the
+                * missing CA certificates.
+                */
+               wpa_printf(MSG_DEBUG, "TLSv1: Full client certificate chain "
+                          "not configured - validation may fail");
+       }
+       WPA_PUT_BE24(cert_start, pos - cert_start - 3);
+
+       WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+                             rhdr, end - rhdr, hs_start, pos - hs_start,
+                             &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       pos = rhdr + rlen;
+
+       tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+       *msgpos = pos;
+
+       return 0;
+}
+
+
+static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end)
+{
+       /* ClientDiffieHellmanPublic */
+       u8 *csecret, *csecret_start, *dh_yc, *shared;
+       size_t csecret_len, dh_yc_len, shared_len;
+
+       csecret_len = conn->dh_p_len;
+       csecret = os_malloc(csecret_len);
+       if (csecret == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+                          "memory for Yc (Diffie-Hellman)");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       if (random_get_bytes(csecret, csecret_len)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
+                          "data for Diffie-Hellman");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               os_free(csecret);
+               return -1;
+       }
+
+       if (os_memcmp(csecret, conn->dh_p, csecret_len) > 0)
+               csecret[0] = 0; /* make sure Yc < p */
+
+       csecret_start = csecret;
+       while (csecret_len > 1 && *csecret_start == 0) {
+               csecret_start++;
+               csecret_len--;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH client's secret value",
+                       csecret_start, csecret_len);
+
+       /* Yc = g^csecret mod p */
+       dh_yc_len = conn->dh_p_len;
+       dh_yc = os_malloc(dh_yc_len);
+       if (dh_yc == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+                          "memory for Diffie-Hellman");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               os_free(csecret);
+               return -1;
+       }
+       if (wpa2_crypto_funcs.crypto_mod_exp) {
+               if(wpa2_crypto_funcs.crypto_mod_exp(conn->dh_g, conn->dh_g_len,
+                                                        csecret_start, csecret_len,
+                                                        conn->dh_p, conn->dh_p_len,
+                                                        dh_yc, &dh_yc_len)) {
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_INTERNAL_ERROR);
+                       os_free(csecret);
+                       os_free(dh_yc);
+                       return -1;
+               }
+       } else {
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               os_free(csecret);
+               os_free(dh_yc);
+               wpa_printf(MSG_ERROR, "Fail to register crypto_mod_exp function!\r\n");
+               return -1;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)",
+                   dh_yc, dh_yc_len);
+
+       WPA_PUT_BE16(*pos, dh_yc_len);
+       *pos += 2;
+       if (*pos + dh_yc_len > end) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Not enough room in the "
+                          "message buffer for Yc");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               os_free(csecret);
+               os_free(dh_yc);
+               return -1;
+       }
+       os_memcpy(*pos, dh_yc, dh_yc_len);
+       *pos += dh_yc_len;
+       os_free(dh_yc);
+
+       shared_len = conn->dh_p_len;
+       shared = os_malloc(shared_len);
+       if (shared == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for "
+                          "DH");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               os_free(csecret);
+               return -1;
+       }
+
+       /* shared = Ys^csecret mod p */
+       if (wpa2_crypto_funcs.crypto_mod_exp) {
+               if(wpa2_crypto_funcs.crypto_mod_exp(conn->dh_ys, conn->dh_ys_len,
+                                                             csecret_start, csecret_len,
+                                                             conn->dh_p, conn->dh_p_len,
+                                                             shared, &shared_len)) {
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_INTERNAL_ERROR);
+                       os_free(csecret);
+                       os_free(shared);
+                       return -1;
+               }
+       } else {
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                             TLS_ALERT_INTERNAL_ERROR);
+               os_free(csecret);
+               os_free(shared);
+               wpa_printf(MSG_ERROR, "Fail to register crypto_mod_exp function!\r\n");
+               return -1;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange",
+                       shared, shared_len);
+
+       os_memset(csecret_start, 0, csecret_len);
+       os_free(csecret);
+       if (tls_derive_keys(conn, shared, shared_len)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               os_free(shared);
+               return -1;
+       }
+       os_memset(shared, 0, shared_len);
+       os_free(shared);
+       tlsv1_client_free_dh(conn);
+       return 0;
+}
+
+
+static int tlsv1_key_x_rsa(struct tlsv1_client *conn, u8 **pos, u8 *end)
+{
+       u8 pre_master_secret[TLS_PRE_MASTER_SECRET_LEN];
+       size_t clen;
+       int res;
+
+       if (tls_derive_pre_master_secret(pre_master_secret) < 0 ||
+           tls_derive_keys(conn, pre_master_secret,
+                           TLS_PRE_MASTER_SECRET_LEN)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       /* EncryptedPreMasterSecret */
+       if (conn->server_rsa_key == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: No server RSA key to "
+                          "use for encrypting pre-master secret");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       /* RSA encrypted value is encoded with PKCS #1 v1.5 block type 2. */
+       *pos += 2;
+       clen = end - *pos;
+       res = crypto_public_key_encrypt_pkcs1_v15(
+               conn->server_rsa_key,
+               pre_master_secret, TLS_PRE_MASTER_SECRET_LEN,
+               *pos, &clen);
+       os_memset(pre_master_secret, 0, TLS_PRE_MASTER_SECRET_LEN);
+       if (res < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: RSA encryption failed");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       WPA_PUT_BE16(*pos - 2, clen);
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: Encrypted pre_master_secret",
+                   *pos, clen);
+       *pos += clen;
+
+       return 0;
+}
+
+
+static int tls_write_client_key_exchange(struct tlsv1_client *conn,
+                                        u8 **msgpos, u8 *end)
+{
+       u8 *pos, *rhdr, *hs_start, *hs_length;
+       size_t rlen;
+       tls_key_exchange keyx;
+       const struct tls_cipher_suite *suite;
+
+       suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+       if (suite == NULL)
+               keyx = TLS_KEY_X_NULL;
+       else
+               keyx = suite->key_exchange;
+
+       pos = *msgpos;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send ClientKeyExchange");
+
+       rhdr = pos;
+       pos += TLS_RECORD_HEADER_LEN;
+
+       /* opaque fragment[TLSPlaintext.length] */
+
+       /* Handshake */
+       hs_start = pos;
+       /* HandshakeType msg_type */
+       *pos++ = TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE;
+       /* uint24 length (to be filled) */
+       hs_length = pos;
+       pos += 3;
+       /* body - ClientKeyExchange */
+       if (keyx == TLS_KEY_X_DH_anon) {
+               if (tlsv1_key_x_anon_dh(conn, &pos, end) < 0)
+                       return -1;
+       } else {
+               if (tlsv1_key_x_rsa(conn, &pos, end) < 0)
+                       return -1;
+       }
+
+       WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+                             rhdr, end - rhdr, hs_start, pos - hs_start,
+                             &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       pos = rhdr + rlen;
+       tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+       *msgpos = pos;
+
+       return 0;
+}
+
+
+static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
+                                              u8 **msgpos, u8 *end)
+{
+       u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start;
+       size_t rlen, hlen, clen;
+       u8 hash[100], *hpos;
+       enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
+
+       pos = *msgpos;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateVerify");
+       rhdr = pos;
+       pos += TLS_RECORD_HEADER_LEN;
+
+       /* Handshake */
+       hs_start = pos;
+       /* HandshakeType msg_type */
+       *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY;
+       /* uint24 length (to be filled) */
+       hs_length = pos;
+       pos += 3;
+
+       /*
+        * RFC 2246: 7.4.3 and 7.4.8:
+        * Signature signature
+        *
+        * RSA:
+        * digitally-signed struct {
+        *     opaque md5_hash[16];
+        *     opaque sha_hash[20];
+        * };
+        *
+        * DSA:
+        * digitally-signed struct {
+        *     opaque sha_hash[20];
+        * };
+        *
+        * The hash values are calculated over all handshake messages sent or
+        * received starting at ClientHello up to, but not including, this
+        * CertificateVerify message, including the type and length fields of
+        * the handshake messages.
+        */
+
+       hpos = hash;
+
+#ifdef CONFIG_TLSV12
+       if (conn->rl.tls_version == TLS_VERSION_1_2) {
+               hlen = SHA256_MAC_LEN;
+               if (wpa2_crypto_funcs.crypto_hash_finish) {
+                   if (conn->verify.sha256_cert == NULL ||
+                       wpa2_crypto_funcs.crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) <
+                       0) {
+                           conn->verify.sha256_cert = NULL;
+                           tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                         TLS_ALERT_INTERNAL_ERROR);
+                           return -1;
+                   }
+               } else {
+                       conn->verify.sha256_cert = NULL;
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                         TLS_ALERT_INTERNAL_ERROR);
+                       wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__);
+                       return -1;
+               }
+               conn->verify.sha256_cert = NULL;
+
+               /*
+                * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5
+                *
+                * DigestInfo ::= SEQUENCE {
+                *   digestAlgorithm DigestAlgorithm,
+                *   digest OCTET STRING
+                * }
+                *
+                * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11}
+                *
+                * DER encoded DigestInfo for SHA256 per RFC 3447:
+                * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 ||
+                * H
+                */
+               os_memmove(hash + 19, hash, hlen);
+               hlen += 19;
+               os_memcpy(hash, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65"
+                         "\x03\x04\x02\x01\x05\x00\x04\x20", 19);
+       } else {
+#endif /* CONFIG_TLSV12 */
+
+       if (alg == SIGN_ALG_RSA) {
+               hlen = MD5_MAC_LEN;
+               if (conn->verify.md5_cert == NULL ||
+                   crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0)
+               {
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_INTERNAL_ERROR);
+                       conn->verify.md5_cert = NULL;
+                       crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
+                       conn->verify.sha1_cert = NULL;
+                       return -1;
+               }
+               hpos += MD5_MAC_LEN;
+       } else
+               crypto_hash_finish(conn->verify.md5_cert, NULL, NULL);
+
+       conn->verify.md5_cert = NULL;
+       hlen = SHA1_MAC_LEN;
+       if (conn->verify.sha1_cert == NULL ||
+           crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) {
+               conn->verify.sha1_cert = NULL;
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       conn->verify.sha1_cert = NULL;
+
+       if (alg == SIGN_ALG_RSA)
+               hlen += MD5_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+       }
+#endif /* CONFIG_TLSV12 */
+
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen);
+
+#ifdef CONFIG_TLSV12
+       if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+               /*
+                * RFC 5246, 4.7:
+                * TLS v1.2 adds explicit indication of the used signature and
+                * hash algorithms.
+                *
+                * struct {
+                *   HashAlgorithm hash;
+                *   SignatureAlgorithm signature;
+                * } SignatureAndHashAlgorithm;
+                */
+               *pos++ = TLS_HASH_ALG_SHA256;
+               *pos++ = TLS_SIGN_ALG_RSA;
+       }
+#endif /* CONFIG_TLSV12 */
+
+       /*
+        * RFC 2246, 4.7:
+        * In digital signing, one-way hash functions are used as input for a
+        * signing algorithm. A digitally-signed element is encoded as an
+        * opaque vector <0..2^16-1>, where the length is specified by the
+        * signing algorithm and key.
+        *
+        * In RSA signing, a 36-byte structure of two hashes (one SHA and one
+        * MD5) is signed (encrypted with the private key). It is encoded with
+        * PKCS #1 block type 0 or type 1 as described in [PKCS1].
+        */
+       signed_start = pos; /* length to be filled */
+       pos += 2;
+       clen = end - pos;
+       if (conn->cred == NULL ||
+           crypto_private_key_sign_pkcs1(conn->cred->key, hash, hlen,
+                                         pos, &clen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to sign hash (PKCS #1)");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       WPA_PUT_BE16(signed_start, clen);
+
+       pos += clen;
+
+       WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+                             rhdr, end - rhdr, hs_start, pos - hs_start,
+                             &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       pos = rhdr + rlen;
+
+       tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+       *msgpos = pos;
+
+       return 0;
+}
+
+
+static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn,
+                                              u8 **msgpos, u8 *end)
+{
+       size_t rlen;
+       u8 payload[1];
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
+
+       payload[0] = TLS_CHANGE_CIPHER_SPEC;
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
+                             *msgpos, end - *msgpos, payload, sizeof(payload),
+                             &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       if (tlsv1_record_change_write_cipher(&conn->rl) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for "
+                          "record layer");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       *msgpos += rlen;
+
+       return 0;
+}
+
+
+static int tls_write_client_finished(struct tlsv1_client *conn,
+                                    u8 **msgpos, u8 *end)
+{
+       u8 *pos, *hs_start;
+       size_t rlen, hlen;
+       u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN];
+       u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send Finished");
+
+       /* Encrypted Handshake Message: Finished */
+
+#ifdef CONFIG_TLSV12
+       if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+               hlen = SHA256_MAC_LEN;
+               if (wpa2_crypto_funcs.crypto_hash_finish) {
+                   if (conn->verify.sha256_client == NULL ||
+                       wpa2_crypto_funcs.crypto_hash_finish(conn->verify.sha256_client, hash, &hlen)
+                       < 0) {
+                           tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                         TLS_ALERT_INTERNAL_ERROR);
+                           conn->verify.sha256_client = NULL;
+                           return -1;
+                   }
+               } else {
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                         TLS_ALERT_INTERNAL_ERROR);
+                       conn->verify.sha256_client = NULL;
+                       wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__);
+                       return -1;
+               }
+               conn->verify.sha256_client = NULL;
+       } else {
+#endif /* CONFIG_TLSV12 */
+
+       hlen = MD5_MAC_LEN;
+       if (conn->verify.md5_client == NULL ||
+           crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) {
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               conn->verify.md5_client = NULL;
+               crypto_hash_finish(conn->verify.sha1_client, NULL, NULL);
+               conn->verify.sha1_client = NULL;
+               return -1;
+       }
+       conn->verify.md5_client = NULL;
+       hlen = SHA1_MAC_LEN;
+       if (conn->verify.sha1_client == NULL ||
+           crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN,
+                              &hlen) < 0) {
+               conn->verify.sha1_client = NULL;
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       conn->verify.sha1_client = NULL;
+       hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+       }
+#endif /* CONFIG_TLSV12 */
+
+       if (tls_prf(conn->rl.tls_version,
+                   conn->master_secret, TLS_MASTER_SECRET_LEN,
+                   "client finished", hash, hlen,
+                   verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)",
+                       verify_data + 1 + 3, TLS_VERIFY_DATA_LEN);
+
+       /* Handshake */
+       pos = hs_start = verify_data;
+       /* HandshakeType msg_type */
+       *pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
+       /* uint24 length */
+       WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN);
+       pos += 3;
+       pos += TLS_VERIFY_DATA_LEN; /* verify_data already in place */
+       tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+                             *msgpos, end - *msgpos, hs_start, pos - hs_start,
+                             &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       *msgpos += rlen;
+
+       return 0;
+}
+
+
+static u8 * tls_send_client_key_exchange(struct tlsv1_client *conn,
+                                        size_t *out_len)
+{
+       u8 *msg, *end, *pos;
+       size_t msglen;
+
+       *out_len = 0;
+
+       msglen = 2000;
+       if (conn->certificate_requested)
+               msglen += tls_client_cert_chain_der_len(conn);
+
+       msg = os_malloc(msglen);
+       if (msg == NULL)
+               return NULL;
+
+       pos = msg;
+       end = msg + msglen;
+
+       if (conn->certificate_requested) {
+               if (tls_write_client_certificate(conn, &pos, end) < 0) {
+                       os_free(msg);
+                       return NULL;
+               }
+       }
+
+       if (tls_write_client_key_exchange(conn, &pos, end) < 0 ||
+           (conn->certificate_requested && conn->cred && conn->cred->key &&
+            tls_write_client_certificate_verify(conn, &pos, end) < 0) ||
+           tls_write_client_change_cipher_spec(conn, &pos, end) < 0 ||
+           tls_write_client_finished(conn, &pos, end) < 0) {
+               os_free(msg);
+               return NULL;
+       }
+
+       *out_len = pos - msg;
+       conn->state = SERVER_CHANGE_CIPHER_SPEC;
+
+       return msg;
+}
+
+
+static u8 * tls_send_change_cipher_spec(struct tlsv1_client *conn,
+                                       size_t *out_len)
+{
+       u8 *msg, *end, *pos;
+
+       *out_len = 0;
+
+       msg = os_malloc(1000);
+       if (msg == NULL)
+               return NULL;
+
+       pos = msg;
+       end = msg + 1000;
+
+       if (tls_write_client_change_cipher_spec(conn, &pos, end) < 0 ||
+           tls_write_client_finished(conn, &pos, end) < 0) {
+               os_free(msg);
+               return NULL;
+       }
+
+       *out_len = pos - msg;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Session resumption completed "
+                  "successfully");
+
+       conn->state = ESTABLISHED;
+
+       return msg;
+}
+
+
+u8 * tlsv1_client_handshake_write(struct tlsv1_client *conn, size_t *out_len,
+                                 int no_appl_data)
+{
+       switch (conn->state) {
+       case CLIENT_KEY_EXCHANGE:
+               return tls_send_client_key_exchange(conn, out_len);
+       case CHANGE_CIPHER_SPEC:
+               return tls_send_change_cipher_spec(conn, out_len);
+       case ACK_FINISHED:
+               wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed "
+                          "successfully");
+               conn->state = ESTABLISHED;
+               *out_len = 0;
+               if (no_appl_data) {
+                       /* Need to return something to get final TLS ACK. */
+                       return os_malloc(1);
+               }
+               return NULL;
+       default:
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while "
+                          "generating reply", conn->state);
+               return NULL;
+       }
+}
+
+
+u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level,
+                            u8 description, size_t *out_len)
+{
+       u8 *alert, *pos, *length;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description);
+       *out_len = 0;
+
+       alert = os_malloc(10);
+       if (alert == NULL)
+               return NULL;
+
+       pos = alert;
+
+       /* TLSPlaintext */
+       /* ContentType type */
+       *pos++ = TLS_CONTENT_TYPE_ALERT;
+       /* ProtocolVersion version */
+       WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version :
+                    TLS_VERSION);
+       pos += 2;
+       /* uint16 length (to be filled) */
+       length = pos;
+       pos += 2;
+       /* opaque fragment[TLSPlaintext.length] */
+
+       /* Alert */
+       /* AlertLevel level */
+       *pos++ = level;
+       /* AlertDescription description */
+       *pos++ = description;
+
+       WPA_PUT_BE16(length, pos - length - 2);
+       *out_len = pos - alert;
+
+       return alert;
+}
diff --git a/components/wpa_supplicant/src/wpa2/tls/tlsv1_common.c b/components/wpa_supplicant/src/wpa2/tls/tlsv1_common.c
new file mode 100644 (file)
index 0000000..9d17928
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * TLSv1 common routines
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/tls/x509v3.h"
+#include "wpa2/tls/tlsv1_common.h"
+#include "wpa2/eap_peer/eap_i.h"
+
+
+/*
+ * TODO:
+ * RFC 2246 Section 9: Mandatory to implement TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
+ * Add support for commonly used cipher suites; don't bother with exportable
+ * suites.
+ */ 
+
+static const struct tls_cipher_suite tls_cipher_suites[] = {
+       { TLS_NULL_WITH_NULL_NULL, TLS_KEY_X_NULL, TLS_CIPHER_NULL,
+         TLS_HASH_NULL },
+       { TLS_RSA_WITH_RC4_128_MD5, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128,
+         TLS_HASH_MD5 },
+       { TLS_RSA_WITH_RC4_128_SHA, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128,
+         TLS_HASH_SHA },
+       { TLS_RSA_WITH_DES_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_DES_CBC,
+         TLS_HASH_SHA },
+       { TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_RSA,
+         TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
+       { TLS_DH_anon_WITH_RC4_128_MD5, TLS_KEY_X_DH_anon,
+         TLS_CIPHER_RC4_128, TLS_HASH_MD5 },
+       { TLS_DH_anon_WITH_DES_CBC_SHA, TLS_KEY_X_DH_anon,
+         TLS_CIPHER_DES_CBC, TLS_HASH_SHA },
+       { TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_DH_anon,
+         TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
+       { TLS_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_128_CBC,
+         TLS_HASH_SHA },
+       { TLS_DH_anon_WITH_AES_128_CBC_SHA, TLS_KEY_X_DH_anon,
+         TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA },
+       { TLS_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC,
+         TLS_HASH_SHA },
+       { TLS_DH_anon_WITH_AES_256_CBC_SHA, TLS_KEY_X_DH_anon,
+         TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA },
+       { TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_RSA,
+         TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
+       { TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_RSA,
+         TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 },
+       { TLS_DH_anon_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DH_anon,
+         TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
+       { TLS_DH_anon_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DH_anon,
+         TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 }
+};
+
+#define NUM_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
+#define NUM_TLS_CIPHER_SUITES NUM_ELEMS(tls_cipher_suites)
+
+
+static const struct tls_cipher_data tls_ciphers[] = {
+       { TLS_CIPHER_NULL,         TLS_CIPHER_STREAM,  0,  0,  0,
+         CRYPTO_CIPHER_NULL },
+       { TLS_CIPHER_IDEA_CBC,     TLS_CIPHER_BLOCK,  16, 16,  8,
+         CRYPTO_CIPHER_NULL },
+       { TLS_CIPHER_RC2_CBC_40,   TLS_CIPHER_BLOCK,   5, 16,  0,
+         CRYPTO_CIPHER_ALG_RC2 },
+       { TLS_CIPHER_RC4_40,       TLS_CIPHER_STREAM,  5, 16,  0,
+         CRYPTO_CIPHER_ALG_RC4 },
+       { TLS_CIPHER_RC4_128,      TLS_CIPHER_STREAM, 16, 16,  0,
+         CRYPTO_CIPHER_ALG_RC4 },
+       { TLS_CIPHER_DES40_CBC,    TLS_CIPHER_BLOCK,   5,  8,  8,
+         CRYPTO_CIPHER_ALG_DES },
+       { TLS_CIPHER_DES_CBC,      TLS_CIPHER_BLOCK,   8,  8,  8,
+         CRYPTO_CIPHER_ALG_DES },
+       { TLS_CIPHER_3DES_EDE_CBC, TLS_CIPHER_BLOCK,  24, 24,  8,
+         CRYPTO_CIPHER_ALG_3DES },
+       { TLS_CIPHER_AES_128_CBC,  TLS_CIPHER_BLOCK,  16, 16, 16,
+         CRYPTO_CIPHER_ALG_AES },
+       { TLS_CIPHER_AES_256_CBC,  TLS_CIPHER_BLOCK,  32, 32, 16,
+         CRYPTO_CIPHER_ALG_AES }
+};
+
+#define NUM_TLS_CIPHER_DATA NUM_ELEMS(tls_ciphers)
+
+
+/**
+ * tls_get_cipher_suite - Get TLS cipher suite
+ * @suite: Cipher suite identifier
+ * Returns: Pointer to the cipher data or %NULL if not found
+ */
+const struct tls_cipher_suite * tls_get_cipher_suite(u16 suite)
+{
+       size_t i;
+       for (i = 0; i < NUM_TLS_CIPHER_SUITES; i++)
+               if (tls_cipher_suites[i].suite == suite)
+                       return &tls_cipher_suites[i];
+       return NULL;
+}
+
+
+const struct tls_cipher_data * tls_get_cipher_data(tls_cipher cipher)
+{
+       size_t i;
+       for (i = 0; i < NUM_TLS_CIPHER_DATA; i++)
+               if (tls_ciphers[i].cipher == cipher)
+                       return &tls_ciphers[i];
+       return NULL;
+}
+
+
+int tls_server_key_exchange_allowed(tls_cipher cipher)
+{
+       const struct tls_cipher_suite *suite;
+
+       /* RFC 2246, Section 7.4.3 */
+       suite = tls_get_cipher_suite(cipher);
+       if (suite == NULL)
+               return 0;
+
+       switch (suite->key_exchange) {
+       case TLS_KEY_X_DHE_DSS:
+       case TLS_KEY_X_DHE_DSS_EXPORT:
+       case TLS_KEY_X_DHE_RSA:
+       case TLS_KEY_X_DHE_RSA_EXPORT:
+       case TLS_KEY_X_DH_anon_EXPORT:
+       case TLS_KEY_X_DH_anon:
+               return 1;
+       case TLS_KEY_X_RSA_EXPORT:
+               return 1 /* FIX: public key len > 512 bits */;
+       default:
+               return 0;
+       }
+}
+
+
+/**
+ * tls_parse_cert - Parse DER encoded X.509 certificate and get public key
+ * @buf: ASN.1 DER encoded certificate
+ * @len: Length of the buffer
+ * @pk: Buffer for returning the allocated public key
+ * Returns: 0 on success, -1 on failure
+ *
+ * This functions parses an ASN.1 DER encoded X.509 certificate and retrieves
+ * the public key from it. The caller is responsible for freeing the public key
+ * by calling crypto_public_key_free().
+ */
+int tls_parse_cert(const u8 *buf, size_t len, struct crypto_public_key **pk)
+{
+       struct x509_certificate *cert;
+
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: Parse ASN.1 DER certificate",
+                   buf, len);
+
+       *pk = crypto_public_key_from_cert(buf, len);
+       if (*pk)
+               return 0;
+
+       cert = x509_certificate_parse(buf, len);
+       if (cert == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse X.509 "
+                          "certificate");
+               return -1;
+       }
+
+       /* TODO
+        * verify key usage (must allow encryption)
+        *
+        * All certificate profiles, key and cryptographic formats are
+        * defined by the IETF PKIX working group [PKIX]. When a key
+        * usage extension is present, the digitalSignature bit must be
+        * set for the key to be eligible for signing, as described
+        * above, and the keyEncipherment bit must be present to allow
+        * encryption, as described above. The keyAgreement bit must be
+        * set on Diffie-Hellman certificates. (PKIX: RFC 3280)
+        */
+
+       *pk = crypto_public_key_import(cert->public_key, cert->public_key_len);
+       x509_certificate_free(cert);
+
+       if (*pk == NULL) {
+               wpa_printf(MSG_ERROR, "TLSv1: Failed to import "
+                          "server public key");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int tls_verify_hash_init(struct tls_verify_hash *verify)
+{
+       tls_verify_hash_free(verify);
+       verify->md5_client = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+       verify->md5_server = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+       verify->md5_cert = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+       verify->sha1_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+       verify->sha1_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+       verify->sha1_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+       if (verify->md5_client == NULL || verify->md5_server == NULL ||
+           verify->md5_cert == NULL || verify->sha1_client == NULL ||
+           verify->sha1_server == NULL || verify->sha1_cert == NULL) {
+               tls_verify_hash_free(verify);
+               return -1;
+       }
+#ifdef CONFIG_TLSV12
+       if (wpa2_crypto_funcs.crypto_hash_init) {
+               verify->sha256_client = wpa2_crypto_funcs.crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0);
+               verify->sha256_server = wpa2_crypto_funcs.crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0);
+               verify->sha256_cert = wpa2_crypto_funcs.crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0);
+       } else {
+               wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash init function!\r\n", __FUNCTION__);
+               return -1;
+       }
+       if (verify->sha256_client == NULL ||
+               verify->sha256_server == NULL ||
+           verify->sha256_cert == NULL) {
+               tls_verify_hash_free(verify);
+               return -1;
+       }
+#endif /* CONFIG_TLSV12 */
+       return 0;
+}
+
+
+void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf,
+                        size_t len)
+{
+       if (verify->md5_client && verify->sha1_client) {
+               crypto_hash_update(verify->md5_client, buf, len);
+               crypto_hash_update(verify->sha1_client, buf, len);
+       }
+       if (verify->md5_server && verify->sha1_server) {
+               crypto_hash_update(verify->md5_server, buf, len);
+               crypto_hash_update(verify->sha1_server, buf, len);
+       }
+       if (verify->md5_cert && verify->sha1_cert) {
+               crypto_hash_update(verify->md5_cert, buf, len);
+               crypto_hash_update(verify->sha1_cert, buf, len);
+       }
+#ifdef CONFIG_TLSV12
+       if (wpa2_crypto_funcs.crypto_hash_update) {
+               if (verify->sha256_client)
+                       wpa2_crypto_funcs.crypto_hash_update(verify->sha256_client, buf, len);
+               if (verify->sha256_server)
+                       wpa2_crypto_funcs.crypto_hash_update(verify->sha256_server, buf, len);
+               if (verify->sha256_cert)
+                       wpa2_crypto_funcs.crypto_hash_update(verify->sha256_cert, buf, len);
+       } else {
+               wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash update function!\r\n", __FUNCTION__);
+               return;
+       }
+#endif /* CONFIG_TLSV12 */
+}
+
+
+void tls_verify_hash_free(struct tls_verify_hash *verify)
+{
+       crypto_hash_finish(verify->md5_client, NULL, NULL);
+       crypto_hash_finish(verify->md5_server, NULL, NULL);
+       crypto_hash_finish(verify->md5_cert, NULL, NULL);
+       crypto_hash_finish(verify->sha1_client, NULL, NULL);
+       crypto_hash_finish(verify->sha1_server, NULL, NULL);
+       crypto_hash_finish(verify->sha1_cert, NULL, NULL);
+       verify->md5_client = NULL;
+       verify->md5_server = NULL;
+       verify->md5_cert = NULL;
+       verify->sha1_client = NULL;
+       verify->sha1_server = NULL;
+       verify->sha1_cert = NULL;
+#ifdef CONFIG_TLSV12
+       if (wpa2_crypto_funcs.crypto_hash_finish) {
+               wpa2_crypto_funcs.crypto_hash_finish(verify->sha256_client, NULL, NULL);
+               wpa2_crypto_funcs.crypto_hash_finish(verify->sha256_server, NULL, NULL);
+               wpa2_crypto_funcs.crypto_hash_finish(verify->sha256_cert, NULL, NULL);
+               verify->sha256_client = NULL;
+               verify->sha256_server = NULL;
+               verify->sha256_cert = NULL;
+       } else {
+               wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash finish function!\r\n", __FUNCTION__);
+               return;
+       }
+#endif /* CONFIG_TLSV12 */
+}
+
+
+int tls_version_ok(u16 ver)
+{
+       if (ver == TLS_VERSION_1)
+               return 1;
+#ifdef CONFIG_TLSV11
+       if (ver == TLS_VERSION_1_1)
+               return 1;
+#endif /* CONFIG_TLSV11 */
+#ifdef CONFIG_TLSV12
+       if (ver == TLS_VERSION_1_2)
+               return 1;
+#endif /* CONFIG_TLSV12 */
+
+       return 0;
+}
+
+
+const char * tls_version_str(u16 ver)
+{
+       switch (ver) {
+       case TLS_VERSION_1:
+               return "1.0";
+       case TLS_VERSION_1_1:
+               return "1.1";
+       case TLS_VERSION_1_2:
+               return "1.2";
+       }
+
+       return "?";
+}
+
+
+int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label,
+           const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+{
+#ifdef CONFIG_TLSV12
+       if (ver >= TLS_VERSION_1_2) {
+               tls_prf_sha256(secret, secret_len, label, seed, seed_len,
+                              out, outlen);
+               return 0;
+       }
+#endif /* CONFIG_TLSV12 */
+
+       return tls_prf_sha1_md5(secret, secret_len, label, seed, seed_len, out,
+                               outlen);
+}
diff --git a/components/wpa_supplicant/src/wpa2/tls/tlsv1_cred.c b/components/wpa_supplicant/src/wpa2/tls/tlsv1_cred.c
new file mode 100644 (file)
index 0000000..fd359a2
--- /dev/null
@@ -0,0 +1,505 @@
+/*
+ * TLSv1 credentials
+ * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wpa2/utils/base64.h"
+#include "crypto/crypto.h"
+#include "wpa2/tls/x509v3.h"
+#include "wpa2/tls/tlsv1_cred.h"
+
+struct tlsv1_credentials * tlsv1_cred_alloc(void)
+{
+       struct tlsv1_credentials *cred;
+       cred = (struct tlsv1_credentials *)os_zalloc(sizeof(*cred));
+       return cred;
+}
+
+
+void tlsv1_cred_free(struct tlsv1_credentials *cred)
+{
+       if (cred == NULL)
+               return;
+
+       x509_certificate_chain_free(cred->trusted_certs);
+       x509_certificate_chain_free(cred->cert);
+       crypto_private_key_free(cred->key);
+       os_free(cred->dh_p);
+       os_free(cred->dh_g);
+       os_free(cred);
+}
+
+
+static int tlsv1_add_cert_der(struct x509_certificate **chain,
+                             const u8 *buf, size_t len)
+{
+       struct x509_certificate *cert, *p;
+       char name[128];
+
+       cert = x509_certificate_parse(buf, len);
+       if (cert == NULL) {
+               wpa_printf(MSG_INFO, "TLSv1: %s - failed to parse certificate",
+                          __func__);
+               return -1;
+       }
+
+       p = *chain;
+       while (p && p->next)
+               p = p->next;
+       if (p && x509_name_compare(&cert->subject, &p->issuer) == 0) {
+               /*
+                * The new certificate is the issuer of the last certificate in
+                * the chain - add the new certificate to the end.
+                */
+               p->next = cert;
+       } else {
+               /* Add to the beginning of the chain */
+               cert->next = *chain;
+               *chain = cert;
+       }
+
+       x509_name_string(&cert->subject, name, sizeof(name));
+       wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name);
+
+       return 0;
+}
+
+
+static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----";
+static const char *pem_cert_end = "-----END CERTIFICATE-----";
+static const char *pem_key_begin = "-----BEGIN RSA PRIVATE KEY-----";
+static const char *pem_key_end = "-----END RSA PRIVATE KEY-----";
+static const char *pem_key2_begin = "-----BEGIN PRIVATE KEY-----";
+static const char *pem_key2_end = "-----END PRIVATE KEY-----";
+static const char *pem_key_enc_begin = "-----BEGIN ENCRYPTED PRIVATE KEY-----";
+static const char *pem_key_enc_end = "-----END ENCRYPTED PRIVATE KEY-----";
+
+
+static const u8 * search_tag(const char *tag, const u8 *buf, size_t len)
+{
+       size_t i, plen;
+
+       plen = os_strlen(tag);
+       if (len < plen)
+               return NULL;
+
+       for (i = 0; i < len - plen; i++) {
+               if (os_memcmp(buf + i, tag, plen) == 0)
+                       return buf + i;
+       }
+
+       return NULL;
+}
+
+
+static int tlsv1_add_cert(struct x509_certificate **chain,
+                         const u8 *buf, size_t len)
+{
+       const u8 *pos, *end;
+       unsigned char *der;
+       size_t der_len;
+
+       pos = search_tag(pem_cert_begin, buf, len);
+       if (!pos) {
+               wpa_printf(MSG_DEBUG, "TLSv1: No PEM certificate tag found - "
+                          "assume DER format");
+               return tlsv1_add_cert_der(chain, buf, len);
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format certificate into "
+                  "DER format");
+
+       while (pos) {
+               pos += os_strlen(pem_cert_begin);
+               end = search_tag(pem_cert_end, pos, buf + len - pos);
+               if (end == NULL) {
+                       wpa_printf(MSG_INFO, "TLSv1: Could not find PEM "
+                                  "certificate end tag (%s)", pem_cert_end);
+                       return -1;
+               }
+
+               der = base64_decode(pos, end - pos, &der_len);
+               if (der == NULL) {
+                       wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM "
+                                  "certificate");
+                       return -1;
+               }
+
+               if (tlsv1_add_cert_der(chain, der, der_len) < 0) {
+                       wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM "
+                                  "certificate after DER conversion");
+                       os_free(der);
+                       return -1;
+               }
+
+               os_free(der);
+
+               end += os_strlen(pem_cert_end);
+               pos = search_tag(pem_cert_begin, end, buf + len - end);
+       }
+
+       return 0;
+}
+
+
+static int tlsv1_set_cert_chain(struct x509_certificate **chain,
+                               const char *cert, const u8 *cert_blob,
+                               size_t cert_blob_len)
+{
+       if (cert_blob)
+               return tlsv1_add_cert(chain, cert_blob, cert_blob_len);
+
+       if (cert) {
+               u8 *buf = NULL;
+               size_t len;
+               int ret;
+
+               //buf = (u8 *) os_readfile(cert, &len);
+               if (buf == NULL) {
+                       wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
+                                  cert);
+                       return -1;
+               }
+
+               ret = tlsv1_add_cert(chain, buf, len);
+               os_free(buf);
+               return ret;
+       }
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_set_ca_cert - Set trusted CA certificate(s)
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @cert: File or reference name for X.509 certificate in PEM or DER format
+ * @cert_blob: cert as inlined data or %NULL if not used
+ * @cert_blob_len: ca_cert_blob length
+ * @path: Path to CA certificates (not yet supported)
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert,
+                     const u8 *cert_blob, size_t cert_blob_len,
+                     const char *path)
+{
+       if (tlsv1_set_cert_chain(&cred->trusted_certs, cert,
+                                cert_blob, cert_blob_len) < 0)
+               return -1;
+
+       if (path) {
+               /* TODO: add support for reading number of certificate files */
+               wpa_printf(MSG_INFO, "TLSv1: Use of CA certificate directory "
+                          "not yet supported");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_set_cert - Set certificate
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @cert: File or reference name for X.509 certificate in PEM or DER format
+ * @cert_blob: cert as inlined data or %NULL if not used
+ * @cert_blob_len: cert_blob length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert,
+                  const u8 *cert_blob, size_t cert_blob_len)
+{
+       return tlsv1_set_cert_chain(&cred->cert, cert,
+                                   cert_blob, cert_blob_len);
+}
+
+
+static struct crypto_private_key * tlsv1_set_key_pem(const u8 *key, size_t len)
+{
+       const u8 *pos, *end;
+       unsigned char *der;
+       size_t der_len;
+       struct crypto_private_key *pkey;
+
+       pos = search_tag(pem_key_begin, key, len);
+       if (!pos) {
+               pos = search_tag(pem_key2_begin, key, len);
+               if (!pos)
+                       return NULL;
+               pos += os_strlen(pem_key2_begin);
+               end = search_tag(pem_key2_end, pos, key + len - pos);
+               if (!end)
+                       return NULL;
+       } else {
+               const u8 *pos2;
+               pos += os_strlen(pem_key_begin);
+               end = search_tag(pem_key_end, pos, key + len - pos);
+               if (!end)
+                       return NULL;
+               pos2 = search_tag("Proc-Type: 4,ENCRYPTED", pos, end - pos);
+               if (pos2) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Unsupported private key "
+                                  "format (Proc-Type/DEK-Info)");
+                       return NULL;
+               }
+       }
+
+       der = base64_decode(pos, end - pos, &der_len);
+       if (!der)
+               return NULL;
+       pkey = crypto_private_key_import(der, der_len, NULL);
+       os_free(der);
+       return pkey;
+}
+
+
+static struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key,
+                                                        size_t len,
+                                                        const char *passwd)
+{
+       const u8 *pos, *end;
+       unsigned char *der;
+       size_t der_len;
+       struct crypto_private_key *pkey;
+
+       if (passwd == NULL)
+               return NULL;
+       pos = search_tag(pem_key_enc_begin, key, len);
+       if (!pos)
+               return NULL;
+       pos += os_strlen(pem_key_enc_begin);
+       end = search_tag(pem_key_enc_end, pos, key + len - pos);
+       if (!end)
+               return NULL;
+
+       der = base64_decode(pos, end - pos, &der_len);
+       if (!der)
+               return NULL;
+       pkey = crypto_private_key_import(der, der_len, passwd);
+       os_free(der);
+       return pkey;
+}
+
+
+static int tlsv1_set_key(struct tlsv1_credentials *cred,
+                        const u8 *key, size_t len, const char *passwd)
+{
+       cred->key = crypto_private_key_import(key, len, passwd);
+       if (cred->key == NULL)
+               cred->key = tlsv1_set_key_pem(key, len);
+       if (cred->key == NULL)
+               cred->key = tlsv1_set_key_enc_pem(key, len, passwd);
+       if (cred->key == NULL) {
+               wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key");
+               return -1;
+       }
+       return 0;
+}
+
+
+/**
+ * tlsv1_set_private_key - Set private key
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @private_key: File or reference name for the key in PEM or DER format
+ * @private_key_passwd: Passphrase for decrypted private key, %NULL if no
+ * passphrase is used.
+ * @private_key_blob: private_key as inlined data or %NULL if not used
+ * @private_key_blob_len: private_key_blob length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_private_key(struct tlsv1_credentials *cred,
+                         const char *private_key,
+                         const char *private_key_passwd,
+                         const u8 *private_key_blob,
+                         size_t private_key_blob_len)
+{
+       crypto_private_key_free(cred->key);
+       cred->key = NULL;
+
+       if (private_key_blob)
+               return tlsv1_set_key(cred, private_key_blob,
+                                    private_key_blob_len,
+                                    private_key_passwd);
+
+       if (private_key) {
+               u8 *buf = NULL;
+               size_t len;
+               int ret;
+
+               //buf = (u8 *) os_readfile(private_key, &len);
+               if (buf == NULL) {
+                       wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
+                                  private_key);
+                       return -1;
+               }
+
+               ret = tlsv1_set_key(cred, buf, len, private_key_passwd);
+               os_free(buf);
+               return ret;
+       }
+
+       return 0;
+}
+
+
+static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred,
+                                 const u8 *dh, size_t len)
+{
+       struct asn1_hdr hdr;
+       const u8 *pos, *end;
+
+       pos = dh;
+       end = dh + len;
+
+       /*
+        * DHParameter ::= SEQUENCE {
+        *   prime INTEGER, -- p
+        *   base INTEGER, -- g
+        *   privateValueLength INTEGER OPTIONAL }
+        */
+
+       /* DHParamer ::= SEQUENCE */
+       if (asn1_get_next(pos, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "DH: DH parameters did not start with a "
+                          "valid SEQUENCE - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+       pos = hdr.payload;
+
+       /* prime INTEGER */
+       if (asn1_get_next(pos, end - pos, &hdr) < 0)
+               return -1;
+
+       if (hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_INTEGER) {
+               wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for p; "
+                          "class=%d tag=0x%x", hdr.class, hdr.tag);
+               return -1;
+       }
+
+       wpa_hexdump(MSG_MSGDUMP, "DH: prime (p)", hdr.payload, hdr.length);
+       if (hdr.length == 0)
+               return -1;
+       os_free(cred->dh_p);
+       cred->dh_p = os_malloc(hdr.length);
+       if (cred->dh_p == NULL)
+               return -1;
+       os_memcpy(cred->dh_p, hdr.payload, hdr.length);
+       cred->dh_p_len = hdr.length;
+       pos = hdr.payload + hdr.length;
+
+       /* base INTEGER */
+       if (asn1_get_next(pos, end - pos, &hdr) < 0)
+               return -1;
+
+       if (hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_INTEGER) {
+               wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for g; "
+                          "class=%d tag=0x%x", hdr.class, hdr.tag);
+               return -1;
+       }
+
+       wpa_hexdump(MSG_MSGDUMP, "DH: base (g)", hdr.payload, hdr.length);
+       if (hdr.length == 0)
+               return -1;
+       os_free(cred->dh_g);
+       cred->dh_g = os_malloc(hdr.length);
+       if (cred->dh_g == NULL)
+               return -1;
+       os_memcpy(cred->dh_g, hdr.payload, hdr.length);
+       cred->dh_g_len = hdr.length;
+
+       return 0;
+}
+
+
+static const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----";
+static const char *pem_dhparams_end = "-----END DH PARAMETERS-----";
+
+
+static int tlsv1_set_dhparams_blob(struct tlsv1_credentials *cred,
+                                  const u8 *buf, size_t len)
+{
+       const u8 *pos, *end;
+       unsigned char *der;
+       size_t der_len;
+
+       pos = search_tag(pem_dhparams_begin, buf, len);
+       if (!pos) {
+               wpa_printf(MSG_DEBUG, "TLSv1: No PEM dhparams tag found - "
+                          "assume DER format");
+               return tlsv1_set_dhparams_der(cred, buf, len);
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format dhparams into DER "
+                  "format");
+
+       pos += os_strlen(pem_dhparams_begin);
+       end = search_tag(pem_dhparams_end, pos, buf + len - pos);
+       if (end == NULL) {
+               wpa_printf(MSG_INFO, "TLSv1: Could not find PEM dhparams end "
+                          "tag (%s)", pem_dhparams_end);
+               return -1;
+       }
+
+       der = base64_decode(pos, end - pos, &der_len);
+       if (der == NULL) {
+               wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams");
+               return -1;
+       }
+
+       if (tlsv1_set_dhparams_der(cred, der, der_len) < 0) {
+               wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM dhparams "
+                          "DER conversion");
+               os_free(der);
+               return -1;
+       }
+
+       os_free(der);
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_set_dhparams - Set Diffie-Hellman parameters
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @dh_file: File or reference name for the DH params in PEM or DER format
+ * @dh_blob: DH params as inlined data or %NULL if not used
+ * @dh_blob_len: dh_blob length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file,
+                      const u8 *dh_blob, size_t dh_blob_len)
+{
+       if (dh_blob)
+               return tlsv1_set_dhparams_blob(cred, dh_blob, dh_blob_len);
+
+       if (dh_file) {
+               u8 *buf = NULL;
+               size_t len;
+               int ret;
+
+               //buf = (u8 *) os_readfile(dh_file, &len);
+               if (buf == NULL) {
+                       wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
+                                  dh_file);
+                       return -1;
+               }
+
+               ret = tlsv1_set_dhparams_blob(cred, buf, len);
+               os_free(buf);
+               return ret;
+       }
+
+       return 0;
+}
diff --git a/components/wpa_supplicant/src/wpa2/tls/tlsv1_record.c b/components/wpa_supplicant/src/wpa2/tls/tlsv1_record.c
new file mode 100644 (file)
index 0000000..879d9f5
--- /dev/null
@@ -0,0 +1,553 @@
+/*
+ * TLSv1 Record Protocol
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "wpa2/tls/tlsv1_common.h"
+#include "wpa2/tls/tlsv1_record.h"
+
+#include "wpa2/eap_peer/eap_i.h"
+
+/**
+ * tlsv1_record_set_cipher_suite - TLS record layer: Set cipher suite
+ * @rl: Pointer to TLS record layer data
+ * @cipher_suite: New cipher suite
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to prepare TLS record layer for cipher suite change.
+ * tlsv1_record_change_write_cipher() and
+ * tlsv1_record_change_read_cipher() functions can then be used to change the
+ * currently used ciphers.
+ */
+int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl,
+                                 u16 cipher_suite)
+{
+       const struct tls_cipher_suite *suite;
+       const struct tls_cipher_data *data;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Selected cipher suite: 0x%04x",
+                  cipher_suite);
+       rl->cipher_suite = cipher_suite;
+
+       suite = tls_get_cipher_suite(cipher_suite);
+       if (suite == NULL)
+               return -1;
+
+       if (suite->hash == TLS_HASH_MD5) {
+               rl->hash_alg = CRYPTO_HASH_ALG_HMAC_MD5;
+               rl->hash_size = MD5_MAC_LEN;
+       } else if (suite->hash == TLS_HASH_SHA) {
+               rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA1;
+               rl->hash_size = SHA1_MAC_LEN;
+       } else if (suite->hash == TLS_HASH_SHA256) {
+               rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA256;
+               rl->hash_size = SHA256_MAC_LEN;
+       }
+
+       data = tls_get_cipher_data(suite->cipher);
+       if (data == NULL)
+               return -1;
+
+       rl->key_material_len = data->key_material;
+       rl->iv_size = data->block_size;
+       rl->cipher_alg = data->alg;
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_record_change_write_cipher - TLS record layer: Change write cipher
+ * @rl: Pointer to TLS record layer data
+ * Returns: 0 on success (cipher changed), -1 on failure
+ *
+ * This function changes TLS record layer to use the new cipher suite
+ * configured with tlsv1_record_set_cipher_suite() for writing.
+ */
+int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl)
+{
+       wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New write cipher suite "
+                  "0x%04x", rl->cipher_suite);
+       rl->write_cipher_suite = rl->cipher_suite;
+       os_memset(rl->write_seq_num, 0, TLS_SEQ_NUM_LEN);
+
+       if (rl->write_cbc) {
+               if (wpa2_crypto_funcs.crypto_cipher_deinit) {
+                       wpa2_crypto_funcs.crypto_cipher_deinit(rl->write_cbc);
+                       rl->write_cbc = NULL;
+               } else {
+                       wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n");
+                       return -1;
+               }
+       }
+       if (rl->cipher_alg != CRYPTO_CIPHER_NULL) {
+               if (wpa2_crypto_funcs.crypto_cipher_init) {
+                       rl->write_cbc = wpa2_crypto_funcs.crypto_cipher_init(rl->cipher_alg,
+                                                                            rl->write_iv, rl->write_key,
+                                                                            rl->key_material_len);
+               } else {
+                       wpa_printf(MSG_ERROR, "Fail to register crypto_cipher_init function!\r\n");
+                       return -1;
+               }
+
+               if (rl->write_cbc == NULL) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize "
+                                  "cipher");
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_record_change_read_cipher - TLS record layer: Change read cipher
+ * @rl: Pointer to TLS record layer data
+ * Returns: 0 on success (cipher changed), -1 on failure
+ *
+ * This function changes TLS record layer to use the new cipher suite
+ * configured with tlsv1_record_set_cipher_suite() for reading.
+ */
+int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl)
+{
+       wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New read cipher suite "
+                  "0x%04x \n", rl->cipher_suite);
+       rl->read_cipher_suite = rl->cipher_suite;
+       os_memset(rl->read_seq_num, 0, TLS_SEQ_NUM_LEN);
+
+       if (rl->read_cbc) {
+               if (wpa2_crypto_funcs.crypto_cipher_deinit) {
+                       wpa2_crypto_funcs.crypto_cipher_deinit(rl->read_cbc);
+                       rl->read_cbc =  NULL;
+               } else {
+                       wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n");
+                       return -1;
+               }
+       }
+
+       if (rl->cipher_alg != CRYPTO_CIPHER_NULL) {
+               if(wpa2_crypto_funcs.crypto_cipher_init) {
+                       rl->read_cbc = wpa2_crypto_funcs.crypto_cipher_init(rl->cipher_alg,
+                                                                           rl->read_iv, rl->read_key,
+                                                                           rl->key_material_len);
+               } else {
+                       wpa_printf(MSG_ERROR, "Fail to register crypto_cipher_init function!\r\n");
+                       return -1;
+               }
+               if (rl->read_cbc == NULL) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize "
+                                  "cipher");
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_record_send - TLS record layer: Send a message
+ * @rl: Pointer to TLS record layer data
+ * @content_type: Content type (TLS_CONTENT_TYPE_*)
+ * @buf: Buffer for the generated TLS message (needs to have extra space for
+ * header, IV (TLS v1.1), and HMAC)
+ * @buf_size: Maximum buf size
+ * @payload: Payload to be sent
+ * @payload_len: Length of the payload
+ * @out_len: Buffer for returning the used buf length
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function fills in the TLS record layer header, adds HMAC, and encrypts
+ * the data using the current write cipher.
+ */
+int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
+                     size_t buf_size, const u8 *payload, size_t payload_len,
+                     size_t *out_len)
+{
+       u8 *pos, *ct_start, *length, *cpayload;
+       struct crypto_hash *hmac = NULL;
+       size_t clen;
+       int explicit_iv;
+
+       pos = buf;
+       if (pos + TLS_RECORD_HEADER_LEN > buf + buf_size)
+               return -1;
+
+       /* ContentType type */
+       ct_start = pos;
+       *pos++ = content_type;
+       /* ProtocolVersion version */
+       WPA_PUT_BE16(pos, rl->tls_version);
+       pos += 2;
+       /* uint16 length */
+       length = pos;
+       WPA_PUT_BE16(length, payload_len);
+       pos += 2;
+
+       cpayload = pos;
+       explicit_iv = rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL &&
+               rl->iv_size && rl->tls_version >= TLS_VERSION_1_1;
+       if (explicit_iv) {
+               /* opaque IV[Cipherspec.block_length] */
+               if (pos + rl->iv_size > buf + buf_size)
+                       return -1;
+
+               /*
+                * Use random number R per the RFC 4346, 6.2.3.2 CBC Block
+                * Cipher option 2a.
+                */
+
+               if (os_get_random(pos, rl->iv_size))
+                       return -1;
+               pos += rl->iv_size;
+       }
+
+       /*
+        * opaque fragment[TLSPlaintext.length]
+        * (opaque content[TLSCompressed.length] in GenericBlockCipher)
+        */
+       if (pos + payload_len > buf + buf_size)
+               return -1;
+       os_memmove(pos, payload, payload_len);
+       pos += payload_len;
+
+       if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
+               /*
+                * MAC calculated over seq_num + TLSCompressed.type +
+                * TLSCompressed.version + TLSCompressed.length +
+                * TLSCompressed.fragment
+                */
+               if (wpa2_crypto_funcs.crypto_hash_init) {
+                       hmac = wpa2_crypto_funcs.crypto_hash_init(rl->hash_alg, rl->write_mac_secret, rl->hash_size);
+               } else {
+                       wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash init!\r\n", __FUNCTION__);
+                       return -1;
+               }
+               if (hmac == NULL) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
+                                  "to initialize HMAC");
+                       return -1;
+               }
+               if (wpa2_crypto_funcs.crypto_hash_update) {
+                       wpa2_crypto_funcs.crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN);
+                       /* type + version + length + fragment */
+                       wpa2_crypto_funcs.crypto_hash_update(hmac, ct_start, TLS_RECORD_HEADER_LEN);
+                       wpa2_crypto_funcs.crypto_hash_update(hmac, payload, payload_len);
+               } else {
+                       wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash update!\r\n", __FUNCTION__);
+                       return -1;
+               }
+               clen = buf + buf_size - pos;
+               if (clen < rl->hash_size) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not "
+                                  "enough room for MAC");
+                       if (wpa2_crypto_funcs.crypto_hash_finish) {
+                               wpa2_crypto_funcs.crypto_hash_finish(hmac, NULL, NULL);
+                       } else {
+                               wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash finish function!\r\n", __FUNCTION__);
+                               return -1;
+                       }
+
+                       return -1;
+               }
+
+               if (wpa2_crypto_funcs.crypto_hash_finish) {
+                       if ((int)wpa2_crypto_funcs.crypto_hash_finish(hmac, pos, (int *)&clen) < 0) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed to calculate HMAC");
+                               return -1;
+                       }
+               } else {
+                       wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n",__FUNCTION__);
+                       return -1;
+               }
+               wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Write HMAC",
+                           pos, clen);
+               pos += clen;
+               if (rl->iv_size) {
+                       size_t len = pos - cpayload;
+                       size_t pad;
+                       pad = (len + 1) % rl->iv_size;
+                       if (pad)
+                               pad = rl->iv_size - pad;
+                       if (pos + pad + 1 > buf + buf_size) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: No room for "
+                                          "block cipher padding");
+                               return -1;
+                       }
+                       os_memset(pos, pad, pad + 1);
+                       pos += pad + 1;
+               }
+
+               if (wpa2_crypto_funcs.crypto_cipher_encrypt) {
+                       if ((int)wpa2_crypto_funcs.crypto_cipher_encrypt(rl->write_cbc, cpayload,
+                                                                 cpayload, pos - cpayload) < 0)
+                               return -1;
+               } else {
+                       wpa_printf(MSG_ERROR, "Fail to register crypto_cipher_encrypt function!\r\n");
+                       return -1;
+               }
+       }
+
+       WPA_PUT_BE16(length, pos - length - 2);
+       inc_byte_array(rl->write_seq_num, TLS_SEQ_NUM_LEN);
+
+       *out_len = pos - buf;
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_record_receive - TLS record layer: Process a received message
+ * @rl: Pointer to TLS record layer data
+ * @in_data: Received data
+ * @in_len: Length of the received data
+ * @out_data: Buffer for output data (must be at least as long as in_data)
+ * @out_len: Set to maximum out_data length by caller; used to return the
+ * length of the used data
+ * @alert: Buffer for returning an alert value on failure
+ * Returns: Number of bytes used from in_data on success, 0 if record was not
+ *     complete (more data needed), or -1 on failure
+ *
+ * This function decrypts the received message, verifies HMAC and TLS record
+ * layer header.
+ */
+int tlsv1_record_receive(struct tlsv1_record_layer *rl,
+                        const u8 *in_data, size_t in_len,
+                        u8 *out_data, size_t *out_len, u8 *alert)
+{
+       size_t i, rlen, hlen;
+       u8 padlen;
+       struct crypto_hash *hmac = NULL;
+       u8 len[2], hash[100];
+       int force_mac_error = 0;
+       u8 ct;
+
+       if (in_len < TLS_RECORD_HEADER_LEN) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu) - "
+                          "need more data",
+                          (unsigned long) in_len);
+               wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
+                           in_data, in_len);
+               return 0;
+       }
+
+       ct = in_data[0];
+       rlen = WPA_GET_BE16(in_data + 3);
+       wpa_printf(MSG_DEBUG, "TLSv1: Received content type %d version %d.%d "
+                  "length %d", ct, in_data[1], in_data[2], (int) rlen);
+
+       /*
+        * TLS v1.0 and v1.1 RFCs were not exactly clear on the use of the
+        * protocol version in record layer. As such, accept any {03,xx} value
+        * to remain compatible with existing implementations.
+        */
+       if (in_data[1] != 0x03) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version "
+                          "%u.%u", in_data[1], in_data[2]);
+               *alert = TLS_ALERT_PROTOCOL_VERSION;
+               return -1;
+       }
+
+       /* TLSCiphertext must not be more than 2^14+2048 bytes */
+       if (TLS_RECORD_HEADER_LEN + rlen > 18432) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)",
+                          (unsigned long) (TLS_RECORD_HEADER_LEN + rlen));
+               *alert = TLS_ALERT_RECORD_OVERFLOW;
+               return -1;
+       }
+
+       in_data += TLS_RECORD_HEADER_LEN;
+       in_len -= TLS_RECORD_HEADER_LEN;
+
+       if (rlen > in_len) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Not all record data included "
+                          "(rlen=%lu > in_len=%lu)",
+                          (unsigned long) rlen, (unsigned long) in_len);
+               return 0;
+       }
+
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
+                   in_data, rlen);
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE &&
+           ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC &&
+           ct != TLS_CONTENT_TYPE_ALERT &&
+           ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Ignore record with unknown "
+                          "content type 0x%x", ct);
+               *alert = TLS_ALERT_UNEXPECTED_MESSAGE;
+               return -1;
+       }
+
+       in_len = rlen;
+
+       if (*out_len < in_len) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Not enough output buffer for "
+                          "processing received record");
+               *alert = TLS_ALERT_INTERNAL_ERROR;
+               return -1;
+       }
+
+       if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
+               size_t plen;
+               if (wpa2_crypto_funcs.crypto_cipher_decrypt) {
+                       if ((int)wpa2_crypto_funcs.crypto_cipher_decrypt(rl->read_cbc, in_data,
+                                                                   out_data, in_len) < 0) {
+                               *alert = TLS_ALERT_DECRYPTION_FAILED;
+                               return -1;
+                       }
+               } else {
+                       wpa_printf(MSG_ERROR, "Fail to register crypto cipher decrypt function. \r\n");
+                       *alert = TLS_ALERT_DECRYPTION_FAILED;
+                       return -1;
+               }
+               plen = in_len;
+               wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted "
+                               "data", out_data, plen);
+
+               if (rl->iv_size) {
+                       /*
+                        * TLS v1.0 defines different alert values for various
+                        * failures. That may information to aid in attacks, so
+                        * use the same bad_record_mac alert regardless of the
+                        * issues.
+                        *
+                        * In addition, instead of returning immediately on
+                        * error, run through the MAC check to make timing
+                        * attacks more difficult.
+                        */
+
+                       if (rl->tls_version >= TLS_VERSION_1_1) {
+                               /* Remove opaque IV[Cipherspec.block_length] */
+                               if (plen < rl->iv_size) {
+                                       wpa_printf(MSG_DEBUG, "TLSv1.1: Not "
+                                                  "enough room for IV");
+                                       force_mac_error = 1;
+                                       goto check_mac;
+                               }
+                               os_memmove(out_data, out_data + rl->iv_size,
+                                          plen - rl->iv_size);
+                               plen -= rl->iv_size;
+                       }
+
+                       /* Verify and remove padding */
+                       if (plen == 0) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: Too short record"
+                                          " (no pad)");
+                               force_mac_error = 1;
+                               goto check_mac;
+                       }
+                       padlen = out_data[plen - 1];
+                       if (padlen >= plen) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad "
+                                          "length (%u, plen=%lu) in "
+                                          "received record",
+                                          padlen, (unsigned long) plen);
+                               force_mac_error = 1;
+                               goto check_mac;
+                       }
+                       for (i = plen - padlen - 1; i < plen - 1; i++) {
+                               if (out_data[i] != padlen) {
+                                       wpa_hexdump(MSG_DEBUG,
+                                                   "TLSv1: Invalid pad in "
+                                                   "received record",
+                                                   out_data + plen - padlen -
+                                                   1, padlen + 1);
+                                       force_mac_error = 1;
+                                       goto check_mac;
+                               }
+                       }
+
+                       plen -= padlen + 1;
+
+                       wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - "
+                                       "Decrypted data with IV and padding "
+                                       "removed", out_data, plen);
+               }
+
+       check_mac:
+               if (plen < rl->hash_size) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no "
+                                  "hash value");
+                       *alert = TLS_ALERT_BAD_RECORD_MAC;
+                       return -1;
+               }
+
+               plen -= rl->hash_size;
+
+               if (wpa2_crypto_funcs.crypto_hash_init) {
+                       hmac = wpa2_crypto_funcs.crypto_hash_init(rl->hash_alg, rl->read_mac_secret, rl->hash_size);
+               } else {
+                       wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_init function!\r\n", __FUNCTION__);
+                       return -1;
+               }
+
+               if (hmac == NULL) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
+                                  "to initialize HMAC");
+                       *alert = TLS_ALERT_INTERNAL_ERROR;
+                       return -1;
+               }
+
+               if (wpa2_crypto_funcs.crypto_hash_update) {
+                       wpa2_crypto_funcs.crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN);
+                       /* type + version + length + fragment */
+                       wpa2_crypto_funcs.crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3);
+                       WPA_PUT_BE16(len, plen);
+                       wpa2_crypto_funcs.crypto_hash_update(hmac, len, 2);
+                       wpa2_crypto_funcs.crypto_hash_update(hmac, out_data, plen);
+               } else {
+                       wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash update function!\r\n", __FUNCTION__);
+                       return -1;
+               }
+               hlen = sizeof(hash);
+               if (wpa2_crypto_funcs.crypto_hash_finish) {
+                       if ((int)wpa2_crypto_funcs.crypto_hash_finish(hmac, hash, (int *)&hlen) < 0) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed to calculate HMAC");
+                               *alert = TLS_ALERT_INTERNAL_ERROR;
+                               return -1;
+                       }
+               } else {
+                       wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__);
+                       *alert = TLS_ALERT_INTERNAL_ERROR;
+                       return -1;
+               }
+               if (hlen != rl->hash_size ||
+                   os_memcmp(hash, out_data + plen, hlen) != 0 ||
+                   force_mac_error) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in "
+                                  "received message (force_mac_error=%d)",
+                                  force_mac_error);
+                       *alert = TLS_ALERT_BAD_RECORD_MAC;
+                       return -1;
+               }
+
+               *out_len = plen;
+       } else {
+               os_memcpy(out_data, in_data, in_len);
+               *out_len = in_len;
+       }
+
+       /* TLSCompressed must not be more than 2^14+1024 bytes */
+       if (TLS_RECORD_HEADER_LEN + *out_len > 17408) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)",
+                          (unsigned long) (TLS_RECORD_HEADER_LEN + *out_len));
+               *alert = TLS_ALERT_RECORD_OVERFLOW;
+               return -1;
+       }
+
+       inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN);
+
+       return TLS_RECORD_HEADER_LEN + rlen;
+}
diff --git a/components/wpa_supplicant/src/wpa2/tls/tlsv1_server.c b/components/wpa_supplicant/src/wpa2/tls/tlsv1_server.c
new file mode 100644 (file)
index 0000000..642b09c
--- /dev/null
@@ -0,0 +1,656 @@
+/*
+ * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246)
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/sha1.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/tls/tlsv1_common.h"
+#include "wpa2/tls/tlsv1_record.h"
+#include "wpa2/tls/tlsv1_server.h"
+#include "wpa2/tls/tlsv1_server_i.h"
+
+/* TODO:
+ * Support for a message fragmented across several records (RFC 2246, 6.2.1)
+ */
+
+
+void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description)
+{
+       conn->alert_level = level;
+       conn->alert_description = description;
+}
+
+
+int tlsv1_server_derive_keys(struct tlsv1_server *conn,
+                            const u8 *pre_master_secret,
+                            size_t pre_master_secret_len)
+{
+       u8 seed[2 * TLS_RANDOM_LEN];
+       u8 key_block[TLS_MAX_KEY_BLOCK_LEN];
+       u8 *pos;
+       size_t key_block_len;
+
+       if (pre_master_secret) {
+               wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret",
+                               pre_master_secret, pre_master_secret_len);
+               os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+               os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+                         TLS_RANDOM_LEN);
+               if (tls_prf(conn->rl.tls_version,
+                           pre_master_secret, pre_master_secret_len,
+                           "master secret", seed, 2 * TLS_RANDOM_LEN,
+                           conn->master_secret, TLS_MASTER_SECRET_LEN)) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive "
+                                  "master_secret");
+                       return -1;
+               }
+               wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret",
+                               conn->master_secret, TLS_MASTER_SECRET_LEN);
+       }
+
+       os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+       os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN);
+       key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len +
+                            conn->rl.iv_size);
+       if (tls_prf(conn->rl.tls_version,
+                   conn->master_secret, TLS_MASTER_SECRET_LEN,
+                   "key expansion", seed, 2 * TLS_RANDOM_LEN,
+                   key_block, key_block_len)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block");
+               return -1;
+       }
+       wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block",
+                       key_block, key_block_len);
+
+       pos = key_block;
+
+       /* client_write_MAC_secret */
+       os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size);
+       pos += conn->rl.hash_size;
+       /* server_write_MAC_secret */
+       os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size);
+       pos += conn->rl.hash_size;
+
+       /* client_write_key */
+       os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len);
+       pos += conn->rl.key_material_len;
+       /* server_write_key */
+       os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len);
+       pos += conn->rl.key_material_len;
+
+       /* client_write_IV */
+       os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
+       pos += conn->rl.iv_size;
+       /* server_write_IV */
+       os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size);
+       pos += conn->rl.iv_size;
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_server_handshake - Process TLS handshake
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @in_data: Input data from TLS peer
+ * @in_len: Input data length
+ * @out_len: Length of the output buffer.
+ * Returns: Pointer to output data, %NULL on failure
+ */
+u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
+                           const u8 *in_data, size_t in_len,
+                           size_t *out_len)
+{
+       const u8 *pos, *end;
+       u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct;
+       size_t in_msg_len;
+       int used;
+
+       if (in_data == NULL || in_len == 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: No input data to server");
+               return NULL;
+       }
+
+       pos = in_data;
+       end = in_data + in_len;
+       in_msg = os_malloc(in_len);
+       if (in_msg == NULL)
+               return NULL;
+
+       /* Each received packet may include multiple records */
+       while (pos < end) {
+               in_msg_len = in_len;
+               used = tlsv1_record_receive(&conn->rl, pos, end - pos,
+                                           in_msg, &in_msg_len, &alert);
+               if (used < 0) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
+                                  "record failed");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+                       goto failed;
+               }
+               if (used == 0) {
+                       /* need more data */
+                       wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
+                                  "yet supported");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+                       goto failed;
+               }
+               ct = pos[0];
+
+               in_pos = in_msg;
+               in_end = in_msg + in_msg_len;
+
+               /* Each received record may include multiple messages of the
+                * same ContentType. */
+               while (in_pos < in_end) {
+                       in_msg_len = in_end - in_pos;
+                       if (tlsv1_server_process_handshake(conn, ct, in_pos,
+                                                          &in_msg_len) < 0)
+                               goto failed;
+                       in_pos += in_msg_len;
+               }
+
+               pos += used;
+       }
+
+       os_free(in_msg);
+       in_msg = NULL;
+
+       msg = tlsv1_server_handshake_write(conn, out_len);
+
+failed:
+       os_free(in_msg);
+       if (conn->alert_level) {
+               if (conn->state == FAILED) {
+                       /* Avoid alert loops */
+                       wpa_printf(MSG_DEBUG, "TLSv1: Drop alert loop");
+                       os_free(msg);
+                       return NULL;
+               }
+               conn->state = FAILED;
+               os_free(msg);
+               msg = tlsv1_server_send_alert(conn, conn->alert_level,
+                                             conn->alert_description,
+                                             out_len);
+       }
+
+       return msg;
+}
+
+
+/**
+ * tlsv1_server_encrypt - Encrypt data into TLS tunnel
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @in_data: Pointer to plaintext data to be encrypted
+ * @in_len: Input buffer length
+ * @out_data: Pointer to output buffer (encrypted TLS data)
+ * @out_len: Maximum out_data length 
+ * Returns: Number of bytes written to out_data, -1 on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * send data in the encrypted tunnel.
+ */
+int tlsv1_server_encrypt(struct tlsv1_server *conn,
+                        const u8 *in_data, size_t in_len,
+                        u8 *out_data, size_t out_len)
+{
+       size_t rlen;
+
+       wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
+                       in_data, in_len);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
+                             out_data, out_len, in_data, in_len, &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       return rlen;
+}
+
+
+/**
+ * tlsv1_server_decrypt - Decrypt data from TLS tunnel
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @in_data: Pointer to input buffer (encrypted TLS data)
+ * @in_len: Input buffer length
+ * @out_data: Pointer to output buffer (decrypted data from TLS tunnel)
+ * @out_len: Maximum out_data length
+ * Returns: Number of bytes written to out_data, -1 on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * receive data from the encrypted tunnel.
+ */
+int tlsv1_server_decrypt(struct tlsv1_server *conn,
+                        const u8 *in_data, size_t in_len,
+                        u8 *out_data, size_t out_len)
+{
+       const u8 *in_end, *pos;
+       int used;
+       u8 alert, *out_end, *out_pos, ct;
+       size_t olen;
+
+       pos = in_data;
+       in_end = in_data + in_len;
+       out_pos = out_data;
+       out_end = out_data + out_len;
+
+       while (pos < in_end) {
+               ct = pos[0];
+               olen = out_end - out_pos;
+               used = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
+                                           out_pos, &olen, &alert);
+               if (used < 0) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
+                                  "failed");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+                       return -1;
+               }
+               if (used == 0) {
+                       /* need more data */
+                       wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
+                                  "yet supported");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+                       return -1;
+               }
+
+               if (ct == TLS_CONTENT_TYPE_ALERT) {
+                       if (olen < 2) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: Alert "
+                                          "underflow");
+                               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                                  TLS_ALERT_DECODE_ERROR);
+                               return -1;
+                       }
+                       wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
+                                  out_pos[0], out_pos[1]);
+                       if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) {
+                               /* Continue processing */
+                               pos += used;
+                               continue;
+                       }
+
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          out_pos[1]);
+                       return -1;
+               }
+
+               if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
+                                  "0x%x", pos[0]);
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_UNEXPECTED_MESSAGE);
+                       return -1;
+               }
+
+               out_pos += olen;
+               if (out_pos > out_end) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough "
+                                  "for processing the received record");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_INTERNAL_ERROR);
+                       return -1;
+               }
+
+               pos += used;
+       }
+
+       return out_pos - out_data;
+}
+
+
+/**
+ * tlsv1_server_global_init - Initialize TLSv1 server
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function must be called before using any other TLSv1 server functions.
+ */
+int tlsv1_server_global_init(void)
+{
+       return crypto_global_init();
+}
+
+
+/**
+ * tlsv1_server_global_deinit - Deinitialize TLSv1 server
+ *
+ * This function can be used to deinitialize the TLSv1 server that was
+ * initialized by calling tlsv1_server_global_init(). No TLSv1 server functions
+ * can be called after this before calling tlsv1_server_global_init() again.
+ */
+void tlsv1_server_global_deinit(void)
+{
+       crypto_global_deinit();
+}
+
+
+/**
+ * tlsv1_server_init - Initialize TLSv1 server connection
+ * @cred: Pointer to server credentials from tlsv1_server_cred_alloc()
+ * Returns: Pointer to TLSv1 server connection data or %NULL on failure
+ */
+struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred)
+{
+       struct tlsv1_server *conn;
+       size_t count;
+       u16 *suites;
+
+       conn = (struct tlsv1_server *)os_zalloc(sizeof(*conn));
+       if (conn == NULL)
+               return NULL;
+
+       conn->cred = cred;
+
+       conn->state = CLIENT_HELLO;
+
+       if (tls_verify_hash_init(&conn->verify) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify "
+                          "hash");
+               os_free(conn);
+               return NULL;
+       }
+
+       count = 0;
+       suites = conn->cipher_suites;
+       suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+       suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+       suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
+       suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
+       suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
+       conn->num_cipher_suites = count;
+
+       return conn;
+}
+
+
+static void tlsv1_server_clear_data(struct tlsv1_server *conn)
+{
+       tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
+       tlsv1_record_change_write_cipher(&conn->rl);
+       tlsv1_record_change_read_cipher(&conn->rl);
+       tls_verify_hash_free(&conn->verify);
+
+       crypto_public_key_free(conn->client_rsa_key);
+       conn->client_rsa_key = NULL;
+
+       os_free(conn->session_ticket);
+       conn->session_ticket = NULL;
+       conn->session_ticket_len = 0;
+       conn->use_session_ticket = 0;
+
+       os_free(conn->dh_secret);
+       conn->dh_secret = NULL;
+       conn->dh_secret_len = 0;
+}
+
+
+/**
+ * tlsv1_server_deinit - Deinitialize TLSv1 server connection
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ */
+void tlsv1_server_deinit(struct tlsv1_server *conn)
+{
+       tlsv1_server_clear_data(conn);
+       os_free(conn);
+}
+
+
+/**
+ * tlsv1_server_established - Check whether connection has been established
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: 1 if connection is established, 0 if not
+ */
+int tlsv1_server_established(struct tlsv1_server *conn)
+{
+       return conn->state == ESTABLISHED;
+}
+
+
+/**
+ * tlsv1_server_prf - Use TLS-PRF to derive keying material
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @label: Label (e.g., description of the key) for PRF
+ * @server_random_first: seed is 0 = client_random|server_random,
+ * 1 = server_random|client_random
+ * @out: Buffer for output data from TLS-PRF
+ * @out_len: Length of the output buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_prf(struct tlsv1_server *conn, const char *label,
+                    int server_random_first, u8 *out, size_t out_len)
+{
+       u8 seed[2 * TLS_RANDOM_LEN];
+
+       if (conn->state != ESTABLISHED)
+               return -1;
+
+       if (server_random_first) {
+               os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+               os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random,
+                         TLS_RANDOM_LEN);
+       } else {
+               os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+               os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+                         TLS_RANDOM_LEN);
+       }
+
+       return tls_prf(conn->rl.tls_version,
+                      conn->master_secret, TLS_MASTER_SECRET_LEN,
+                      label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
+}
+
+
+/**
+ * tlsv1_server_get_cipher - Get current cipher name
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @buf: Buffer for the cipher name
+ * @buflen: buf size
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get the name of the currently used cipher.
+ */
+int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf,
+                           size_t buflen)
+{
+#ifndef ESPRESSIF_USE  
+    char *cipher;
+
+       switch (conn->rl.cipher_suite) {
+       case TLS_RSA_WITH_RC4_128_MD5:
+               cipher = "RC4-MD5";
+               break;
+       case TLS_RSA_WITH_RC4_128_SHA:
+               cipher = "RC4-SHA";
+               break;
+       case TLS_RSA_WITH_DES_CBC_SHA:
+               cipher = "DES-CBC-SHA";
+               break;
+       case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+               cipher = "DES-CBC3-SHA";
+               break;
+       case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+               cipher = "ADH-AES-128-SHA";
+               break;
+       case TLS_RSA_WITH_AES_256_CBC_SHA:
+               cipher = "AES-256-SHA";
+               break;
+       case TLS_RSA_WITH_AES_128_CBC_SHA:
+               cipher = "AES-128-SHA";
+               break;
+       default:
+               return -1;
+       }
+
+       //if (os_strlcpy(buf, cipher, buflen) >= buflen)
+       //      return -1;
+
+       os_memcpy((u8 *)buf, (u8 *)cipher, buflen);
+
+       return 0;
+#else
+    char cipher[20];
+
+    switch (conn->rl.cipher_suite) {
+        case TLS_RSA_WITH_RC4_128_MD5:
+                   strcpy(cipher, "RC4-MD5");
+                   break;
+        case TLS_RSA_WITH_RC4_128_SHA:
+            strcpy(cipher, "RC4-SHA");
+            break;
+        case TLS_RSA_WITH_DES_CBC_SHA:
+            strcpy(cipher, "DES-CBC-SHA");
+            break;
+        case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+            strcpy(cipher, "DES-CBC3-SHA");
+            break;
+        case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+            strcpy(cipher, "ADH-AES-128-SHA");
+            break;
+        case TLS_RSA_WITH_AES_256_CBC_SHA:
+            strcpy(cipher, "AES-256-SHA");
+            break;
+        case TLS_RSA_WITH_AES_128_CBC_SHA:
+            strcpy(cipher, "AES-128-SHA");
+            break;
+        default:
+            return -1;
+       }
+    os_memcpy((u8 *)buf, (u8 *)cipher, buflen);
+
+    return 0;
+#endif
+}
+
+
+/**
+ * tlsv1_server_shutdown - Shutdown TLS connection
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_shutdown(struct tlsv1_server *conn)
+{
+       conn->state = CLIENT_HELLO;
+
+       if (tls_verify_hash_init(&conn->verify) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify "
+                          "hash");
+               return -1;
+       }
+
+       tlsv1_server_clear_data(conn);
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_server_resumed - Was session resumption used
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: 1 if current session used session resumption, 0 if not
+ */
+int tlsv1_server_resumed(struct tlsv1_server *conn)
+{
+       return 0;
+}
+
+
+/**
+ * tlsv1_server_get_keys - Get master key and random data from TLS connection
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @keys: Structure of key/random data (filled on success)
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys)
+{
+       os_memset(keys, 0, sizeof(*keys));
+       if (conn->state == CLIENT_HELLO)
+               return -1;
+
+       keys->client_random = conn->client_random;
+       keys->client_random_len = TLS_RANDOM_LEN;
+
+       if (conn->state != SERVER_HELLO) {
+               keys->server_random = conn->server_random;
+               keys->server_random_len = TLS_RANDOM_LEN;
+               keys->master_key = conn->master_secret;
+               keys->master_key_len = TLS_MASTER_SECRET_LEN;
+       }
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_server_get_keyblock_size - Get TLS key_block size
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: Size of the key_block for the negotiated cipher suite or -1 on
+ * failure
+ */
+int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn)
+{
+       if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO)
+               return -1;
+
+       return 2 * (conn->rl.hash_size + conn->rl.key_material_len +
+                   conn->rl.iv_size);
+}
+
+
+/**
+ * tlsv1_server_set_cipher_list - Configure acceptable cipher suites
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
+ * (TLS_CIPHER_*).
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers)
+{
+       size_t count;
+       u16 *suites;
+
+       /* TODO: implement proper configuration of cipher suites */
+       if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) {
+               count = 0;
+               suites = conn->cipher_suites;
+               suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+               suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+               suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
+               suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
+               suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
+               suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
+               suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
+               suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
+               suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5;
+               suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA;
+               conn->num_cipher_suites = count;
+       }
+
+       return 0;
+}
+
+
+int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer)
+{
+       conn->verify_peer = verify_peer;
+       return 0;
+}
+
+
+void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn,
+                                       tlsv1_server_session_ticket_cb cb,
+                                       void *ctx)
+{
+       wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)",
+                  cb, ctx);
+       conn->session_ticket_cb = cb;
+       conn->session_ticket_cb_ctx = ctx;
+}
diff --git a/components/wpa_supplicant/src/wpa2/tls/tlsv1_server_read.c b/components/wpa_supplicant/src/wpa2/tls/tlsv1_server_read.c
new file mode 100644 (file)
index 0000000..ee477c9
--- /dev/null
@@ -0,0 +1,1278 @@
+/*
+ * TLSv1 server - read handshake message
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/tls/x509v3.h"
+#include "wpa2/tls/tlsv1_common.h"
+#include "wpa2/tls/tlsv1_record.h"
+#include "wpa2/tls/tlsv1_server.h"
+#include "wpa2/tls/tlsv1_server_i.h"
+
+#include "wpa2/eap_peer/eap_i.h"
+
+static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct,
+                                          const u8 *in_data, size_t *in_len);
+static int tls_process_change_cipher_spec(struct tlsv1_server *conn,
+                                         u8 ct, const u8 *in_data,
+                                         size_t *in_len);
+
+
+static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
+                                   const u8 *in_data, size_t *in_len)
+{
+       const u8 *pos, *end, *c;
+       size_t left, len, i, j;
+       u16 cipher_suite;
+       u16 num_suites;
+       int compr_null_found;
+       u16 ext_type, ext_len;
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+                          "received content type 0x%x", ct);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 4)
+               goto decode_error;
+
+       /* HandshakeType msg_type */
+       if (*pos != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+                          "message %d (expected ClientHello)", *pos);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG, "TLSv1: Received ClientHello");
+       pos++;
+       /* uint24 length */
+       len = WPA_GET_BE24(pos);
+       pos += 3;
+       left -= 4;
+
+       if (len > left)
+               goto decode_error;
+
+       /* body - ClientHello */
+
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello", pos, len);
+       end = pos + len;
+
+       /* ProtocolVersion client_version */
+       if (end - pos < 2)
+               goto decode_error;
+       conn->client_version = WPA_GET_BE16(pos);
+       wpa_printf(MSG_DEBUG, "TLSv1: Client version %d.%d",
+                  conn->client_version >> 8, conn->client_version & 0xff);
+       if (conn->client_version < TLS_VERSION_1) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
+                          "ClientHello %u.%u",
+                          conn->client_version >> 8,
+                          conn->client_version & 0xff);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_PROTOCOL_VERSION);
+               return -1;
+       }
+       pos += 2;
+
+       if (TLS_VERSION == TLS_VERSION_1)
+               conn->rl.tls_version = TLS_VERSION_1;
+#ifdef CONFIG_TLSV12
+       else if (conn->client_version >= TLS_VERSION_1_2)
+               conn->rl.tls_version = TLS_VERSION_1_2;
+#endif /* CONFIG_TLSV12 */
+       else if (conn->client_version > TLS_VERSION_1_1)
+               conn->rl.tls_version = TLS_VERSION_1_1;
+       else
+               conn->rl.tls_version = conn->client_version;
+       wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s",
+                  tls_version_str(conn->rl.tls_version));
+
+       /* Random random */
+       if (end - pos < TLS_RANDOM_LEN)
+               goto decode_error;
+
+       os_memcpy(conn->client_random, pos, TLS_RANDOM_LEN);
+       pos += TLS_RANDOM_LEN;
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random",
+                   conn->client_random, TLS_RANDOM_LEN);
+
+       /* SessionID session_id */
+       if (end - pos < 1)
+               goto decode_error;
+       if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN)
+               goto decode_error;
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: client session_id", pos + 1, *pos);
+       pos += 1 + *pos;
+       /* TODO: add support for session resumption */
+
+       /* CipherSuite cipher_suites<2..2^16-1> */
+       if (end - pos < 2)
+               goto decode_error;
+       num_suites = WPA_GET_BE16(pos);
+       pos += 2;
+       if (end - pos < num_suites)
+               goto decode_error;
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: client cipher suites",
+                   pos, num_suites);
+       if (num_suites & 1)
+               goto decode_error;
+       num_suites /= 2;
+
+       cipher_suite = 0;
+       for (i = 0; !cipher_suite && i < conn->num_cipher_suites; i++) {
+               c = pos;
+               for (j = 0; j < num_suites; j++) {
+                       u16 tmp = WPA_GET_BE16(c);
+                       c += 2;
+                       if (!cipher_suite && tmp == conn->cipher_suites[i]) {
+                               cipher_suite = tmp;
+                               break;
+                       }
+               }
+       }
+       pos += num_suites * 2;
+       if (!cipher_suite) {
+               wpa_printf(MSG_INFO, "TLSv1: No supported cipher suite "
+                          "available");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_ILLEGAL_PARAMETER);
+               return -1;
+       }
+
+       if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for "
+                          "record layer");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       conn->cipher_suite = cipher_suite;
+
+       /* CompressionMethod compression_methods<1..2^8-1> */
+       if (end - pos < 1)
+               goto decode_error;
+       num_suites = *pos++;
+       if (end - pos < num_suites)
+               goto decode_error;
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: client compression_methods",
+                   pos, num_suites);
+       compr_null_found = 0;
+       for (i = 0; i < num_suites; i++) {
+               if (*pos++ == TLS_COMPRESSION_NULL)
+                       compr_null_found = 1;
+       }
+       if (!compr_null_found) {
+               wpa_printf(MSG_INFO, "TLSv1: Client does not accept NULL "
+                          "compression");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_ILLEGAL_PARAMETER);
+               return -1;
+       }
+
+       if (end - pos == 1) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected extra octet in the "
+                           "end of ClientHello: 0x%02x", *pos);
+               goto decode_error;
+       }
+
+       if (end - pos >= 2) {
+               /* Extension client_hello_extension_list<0..2^16-1> */
+               ext_len = WPA_GET_BE16(pos);
+               pos += 2;
+
+               wpa_printf(MSG_DEBUG, "TLSv1: %u bytes of ClientHello "
+                          "extensions", ext_len);
+               if (end - pos != ext_len) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientHello "
+                                  "extension list length %u (expected %u)",
+                                  ext_len, (unsigned int) (end - pos));
+                       goto decode_error;
+               }
+
+               /*
+                * struct {
+                *   ExtensionType extension_type (0..65535)
+                *   opaque extension_data<0..2^16-1>
+                * } Extension;
+                */
+
+               while (pos < end) {
+                       if (end - pos < 2) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: Invalid "
+                                          "extension_type field");
+                               goto decode_error;
+                       }
+
+                       ext_type = WPA_GET_BE16(pos);
+                       pos += 2;
+
+                       if (end - pos < 2) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: Invalid "
+                                          "extension_data length field");
+                               goto decode_error;
+                       }
+
+                       ext_len = WPA_GET_BE16(pos);
+                       pos += 2;
+
+                       if (end - pos < ext_len) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: Invalid "
+                                          "extension_data field");
+                               goto decode_error;
+                       }
+
+                       wpa_printf(MSG_DEBUG, "TLSv1: ClientHello Extension "
+                                  "type %u", ext_type);
+                       wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello "
+                                   "Extension data", pos, ext_len);
+
+                       if (ext_type == TLS_EXT_SESSION_TICKET) {
+                               os_free(conn->session_ticket);
+                               conn->session_ticket = os_malloc(ext_len);
+                               if (conn->session_ticket) {
+                                       os_memcpy(conn->session_ticket, pos,
+                                                 ext_len);
+                                       conn->session_ticket_len = ext_len;
+                               }
+                       }
+
+                       pos += ext_len;
+               }
+       }
+
+       *in_len = end - in_data;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: ClientHello OK - proceed to "
+                  "ServerHello");
+       conn->state = SERVER_HELLO;
+
+       return 0;
+
+decode_error:
+       wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ClientHello");
+       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                          TLS_ALERT_DECODE_ERROR);
+       return -1;
+}
+
+
+static int tls_process_certificate(struct tlsv1_server *conn, u8 ct,
+                                  const u8 *in_data, size_t *in_len)
+{
+       const u8 *pos, *end;
+       size_t left, len, list_len, cert_len, idx;
+       u8 type;
+       struct x509_certificate *chain = NULL, *last = NULL, *cert;
+       int reason;
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+                          "received content type 0x%x", ct);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 4) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message "
+                          "(len=%lu)", (unsigned long) left);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       type = *pos++;
+       len = WPA_GET_BE24(pos);
+       pos += 3;
+       left -= 4;
+
+       if (len > left) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message "
+                          "length (len=%lu != left=%lu)",
+                          (unsigned long) len, (unsigned long) left);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       if (type == TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) {
+               if (conn->verify_peer) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Client did not include "
+                                  "Certificate");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_UNEXPECTED_MESSAGE);
+                       return -1;
+               }
+
+               return tls_process_client_key_exchange(conn, ct, in_data,
+                                                      in_len);
+       }
+       if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+                          "message %d (expected Certificate/"
+                          "ClientKeyExchange)", type);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Received Certificate (certificate_list len %lu)",
+                  (unsigned long) len);
+
+       /*
+        * opaque ASN.1Cert<2^24-1>;
+        *
+        * struct {
+        *     ASN.1Cert certificate_list<1..2^24-1>;
+        * } Certificate;
+        */
+
+       end = pos + len;
+
+       if (end - pos < 3) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate "
+                          "(left=%lu)", (unsigned long) left);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       list_len = WPA_GET_BE24(pos);
+       pos += 3;
+
+       if ((size_t) (end - pos) != list_len) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list "
+                          "length (len=%lu left=%lu)",
+                          (unsigned long) list_len,
+                          (unsigned long) (end - pos));
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       idx = 0;
+       while (pos < end) {
+               if (end - pos < 3) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+                                  "certificate_list");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_DECODE_ERROR);
+                       x509_certificate_chain_free(chain);
+                       return -1;
+               }
+
+               cert_len = WPA_GET_BE24(pos);
+               pos += 3;
+
+               if ((size_t) (end - pos) < cert_len) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate "
+                                  "length (len=%lu left=%lu)",
+                                  (unsigned long) cert_len,
+                                  (unsigned long) (end - pos));
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_DECODE_ERROR);
+                       x509_certificate_chain_free(chain);
+                       return -1;
+               }
+
+               wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)",
+                          (unsigned long) idx, (unsigned long) cert_len);
+
+               if (idx == 0) {
+                       crypto_public_key_free(conn->client_rsa_key);
+                       if (tls_parse_cert(pos, cert_len,
+                                          &conn->client_rsa_key)) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+                                          "the certificate");
+                               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                                  TLS_ALERT_BAD_CERTIFICATE);
+                               x509_certificate_chain_free(chain);
+                               return -1;
+                       }
+               }
+
+               cert = x509_certificate_parse(pos, cert_len);
+               if (cert == NULL) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+                                  "the certificate");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_BAD_CERTIFICATE);
+                       x509_certificate_chain_free(chain);
+                       return -1;
+               }
+
+               if (last == NULL)
+                       chain = cert;
+               else
+                       last->next = cert;
+               last = cert;
+
+               idx++;
+               pos += cert_len;
+       }
+
+       if (x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
+                                           &reason, 0) < 0) {
+               int tls_reason;
+               wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
+                          "validation failed (reason=%d)", reason);
+               switch (reason) {
+               case X509_VALIDATE_BAD_CERTIFICATE:
+                       tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+                       break;
+               case X509_VALIDATE_UNSUPPORTED_CERTIFICATE:
+                       tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE;
+                       break;
+               case X509_VALIDATE_CERTIFICATE_REVOKED:
+                       tls_reason = TLS_ALERT_CERTIFICATE_REVOKED;
+                       break;
+               case X509_VALIDATE_CERTIFICATE_EXPIRED:
+                       tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED;
+                       break;
+               case X509_VALIDATE_CERTIFICATE_UNKNOWN:
+                       tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN;
+                       break;
+               case X509_VALIDATE_UNKNOWN_CA:
+                       tls_reason = TLS_ALERT_UNKNOWN_CA;
+                       break;
+               default:
+                       tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+                       break;
+               }
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason);
+               x509_certificate_chain_free(chain);
+               return -1;
+       }
+
+       x509_certificate_chain_free(chain);
+
+       *in_len = end - in_data;
+
+       conn->state = CLIENT_KEY_EXCHANGE;
+
+       return 0;
+}
+
+
+static int tls_process_client_key_exchange_rsa(
+       struct tlsv1_server *conn, const u8 *pos, const u8 *end)
+{
+       u8 *out;
+       size_t outlen, outbuflen;
+       u16 encr_len;
+       int res;
+       int use_random = 0;
+
+       if (end - pos < 2) {
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       encr_len = WPA_GET_BE16(pos);
+       pos += 2;
+       if (pos + encr_len > end) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientKeyExchange "
+                          "format: encr_len=%u left=%u",
+                          encr_len, (unsigned int) (end - pos));
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       outbuflen = outlen = end - pos;
+       out = os_malloc(outlen >= TLS_PRE_MASTER_SECRET_LEN ?
+                       outlen : TLS_PRE_MASTER_SECRET_LEN);
+       if (out == NULL) {
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       /*
+        * struct {
+        *   ProtocolVersion client_version;
+        *   opaque random[46];
+        * } PreMasterSecret;
+        *
+        * struct {
+        *   public-key-encrypted PreMasterSecret pre_master_secret;
+        * } EncryptedPreMasterSecret;
+        */
+
+       /*
+        * Note: To avoid Bleichenbacher attack, we do not report decryption or
+        * parsing errors from EncryptedPreMasterSecret processing to the
+        * client. Instead, a random pre-master secret is used to force the
+        * handshake to fail.
+        */
+
+       if (crypto_private_key_decrypt_pkcs1_v15(conn->cred->key,
+                                                pos, encr_len,
+                                                out, &outlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt "
+                          "PreMasterSecret (encr_len=%u outlen=%lu)",
+                          encr_len, (unsigned long) outlen);
+               use_random = 1;
+       }
+
+       if (!use_random && outlen != TLS_PRE_MASTER_SECRET_LEN) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected PreMasterSecret "
+                          "length %lu", (unsigned long) outlen);
+               use_random = 1;
+       }
+
+       if (!use_random && WPA_GET_BE16(out) != conn->client_version) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Client version in "
+                          "ClientKeyExchange does not match with version in "
+                          "ClientHello");
+               use_random = 1;
+       }
+
+       if (use_random) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Using random premaster secret "
+                          "to avoid revealing information about private key");
+               outlen = TLS_PRE_MASTER_SECRET_LEN;
+               if (os_get_random(out, outlen)) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
+                                  "data");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_INTERNAL_ERROR);
+                       os_free(out);
+                       return -1;
+               }
+       }
+
+       res = tlsv1_server_derive_keys(conn, out, outlen);
+
+       /* Clear the pre-master secret since it is not needed anymore */
+       os_memset(out, 0, outbuflen);
+       os_free(out);
+
+       if (res) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int tls_process_client_key_exchange_dh_anon(
+       struct tlsv1_server *conn, const u8 *pos, const u8 *end)
+{
+       const u8 *dh_yc;
+       u16 dh_yc_len;
+       u8 *shared;
+       size_t shared_len;
+       int res;
+
+       /*
+        * struct {
+        *   select (PublicValueEncoding) {
+        *     case implicit: struct { };
+        *     case explicit: opaque dh_Yc<1..2^16-1>;
+        *   } dh_public;
+        * } ClientDiffieHellmanPublic;
+        */
+
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientDiffieHellmanPublic",
+                   pos, end - pos);
+
+       if (end == pos) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Implicit public value encoding "
+                          "not supported");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       if (end - pos < 3) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Invalid client public value "
+                          "length");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       dh_yc_len = WPA_GET_BE16(pos);
+       dh_yc = pos + 2;
+
+       if (dh_yc + dh_yc_len > end) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Client public value overflow "
+                          "(length %d)", dh_yc_len);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)",
+                   dh_yc, dh_yc_len);
+
+       if (conn->cred == NULL || conn->cred->dh_p == NULL ||
+           conn->dh_secret == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       shared_len = conn->cred->dh_p_len;
+       shared = os_malloc(shared_len);
+       if (shared == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for "
+                          "DH");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       /* shared = Yc^secret mod p */
+       if (wpa2_crypto_funcs.crypto_mod_exp) {
+               if (wpa2_crypto_funcs.crypto_mod_exp(dh_yc, dh_yc_len, conn->dh_secret,
+                                                    conn->dh_secret_len,
+                                                    conn->cred->dh_p, conn->cred->dh_p_len,
+                                                    shared, &shared_len)) {
+                       os_free(shared);
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                               TLS_ALERT_INTERNAL_ERROR);
+                       return -1;
+               }
+       } else {
+               wpa_printf(MSG_ERROR, "Fail to register crypto_mod_exp function!\r\n");
+               os_free(shared);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                               TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange",
+                       shared, shared_len);
+
+       os_memset(conn->dh_secret, 0, conn->dh_secret_len);
+       os_free(conn->dh_secret);
+       conn->dh_secret = NULL;
+
+       res = tlsv1_server_derive_keys(conn, shared, shared_len);
+
+       /* Clear the pre-master secret since it is not needed anymore */
+       os_memset(shared, 0, shared_len);
+       os_free(shared);
+
+       if (res) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct,
+                                          const u8 *in_data, size_t *in_len)
+{
+       const u8 *pos, *end;
+       size_t left, len;
+       u8 type;
+       tls_key_exchange keyx;
+       const struct tls_cipher_suite *suite;
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+                          "received content type 0x%x", ct);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 4) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short ClientKeyExchange "
+                          "(Left=%lu)", (unsigned long) left);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       type = *pos++;
+       len = WPA_GET_BE24(pos);
+       pos += 3;
+       left -= 4;
+
+       if (len > left) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ClientKeyExchange "
+                          "length (len=%lu != left=%lu)",
+                          (unsigned long) len, (unsigned long) left);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       end = pos + len;
+
+       if (type != TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+                          "message %d (expected ClientKeyExchange)", type);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Received ClientKeyExchange");
+
+       wpa_hexdump(MSG_DEBUG, "TLSv1: ClientKeyExchange", pos, len);
+
+       suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+       if (suite == NULL)
+               keyx = TLS_KEY_X_NULL;
+       else
+               keyx = suite->key_exchange;
+
+       if (keyx == TLS_KEY_X_DH_anon &&
+           tls_process_client_key_exchange_dh_anon(conn, pos, end) < 0)
+               return -1;
+
+       if (keyx != TLS_KEY_X_DH_anon &&
+           tls_process_client_key_exchange_rsa(conn, pos, end) < 0)
+               return -1;
+
+       *in_len = end - in_data;
+
+       conn->state = CERTIFICATE_VERIFY;
+
+       return 0;
+}
+
+
+static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
+                                         const u8 *in_data, size_t *in_len)
+{
+       const u8 *pos, *end;
+       size_t left, len;
+       u8 type;
+       size_t hlen, buflen;
+       u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos, *buf;
+       enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
+       u16 slen;
+
+       if (ct == TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
+               if (conn->verify_peer) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Client did not include "
+                                  "CertificateVerify");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_UNEXPECTED_MESSAGE);
+                       return -1;
+               }
+
+               return tls_process_change_cipher_spec(conn, ct, in_data,
+                                                     in_len);
+       }
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+                          "received content type 0x%x", ct);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 4) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateVerify "
+                          "message (len=%lu)", (unsigned long) left);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       type = *pos++;
+       len = WPA_GET_BE24(pos);
+       pos += 3;
+       left -= 4;
+
+       if (len > left) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected CertificateVerify "
+                          "message length (len=%lu != left=%lu)",
+                          (unsigned long) len, (unsigned long) left);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       end = pos + len;
+
+       if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+                          "message %d (expected CertificateVerify)", type);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateVerify");
+
+       /*
+        * struct {
+        *   Signature signature;
+        * } CertificateVerify;
+        */
+
+       hpos = hash;
+
+#ifdef CONFIG_TLSV12
+       if (conn->rl.tls_version == TLS_VERSION_1_2) {
+               /*
+                * RFC 5246, 4.7:
+                * TLS v1.2 adds explicit indication of the used signature and
+                * hash algorithms.
+                *
+                * struct {
+                *   HashAlgorithm hash;
+                *   SignatureAlgorithm signature;
+                * } SignatureAndHashAlgorithm;
+                */
+               if (end - pos < 2) {
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_DECODE_ERROR);
+                       return -1;
+               }
+               if (pos[0] != TLS_HASH_ALG_SHA256 ||
+                   pos[1] != TLS_SIGN_ALG_RSA) {
+                       wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/"
+                                  "signature(%u) algorithm",
+                                  pos[0], pos[1]);
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_INTERNAL_ERROR);
+                       return -1;
+               }
+               pos += 2;
+
+               hlen = SHA256_MAC_LEN;
+               if (wpa2_crypto_funcs.crypto_hash_finish) {
+                       if (conn->verify.sha256_cert == NULL ||
+                               wpa2_crypto_funcs.crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) <
+                               0) {
+                               conn->verify.sha256_cert = NULL;
+                               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                                                  TLS_ALERT_INTERNAL_ERROR);
+                               return -1;
+                       }
+               } else {
+                       conn->verify.sha256_cert = NULL;
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                                          TLS_ALERT_INTERNAL_ERROR);
+                       wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__);
+                       return -1;
+               }
+               conn->verify.sha256_cert = NULL;
+       } else {
+#endif /* CONFIG_TLSV12 */
+
+       if (alg == SIGN_ALG_RSA) {
+               hlen = MD5_MAC_LEN;
+               if (conn->verify.md5_cert == NULL ||
+                   crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0)
+               {
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_INTERNAL_ERROR);
+                       conn->verify.md5_cert = NULL;
+                       crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
+                       conn->verify.sha1_cert = NULL;
+                       return -1;
+               }
+               hpos += MD5_MAC_LEN;
+       } else
+               crypto_hash_finish(conn->verify.md5_cert, NULL, NULL);
+
+       conn->verify.md5_cert = NULL;
+       hlen = SHA1_MAC_LEN;
+       if (conn->verify.sha1_cert == NULL ||
+           crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) {
+               conn->verify.sha1_cert = NULL;
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       conn->verify.sha1_cert = NULL;
+
+       if (alg == SIGN_ALG_RSA)
+               hlen += MD5_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+       }
+#endif /* CONFIG_TLSV12 */
+
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen);
+
+       if (end - pos < 2) {
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+       slen = WPA_GET_BE16(pos);
+       pos += 2;
+       if (end - pos < slen) {
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: Signature", pos, end - pos);
+       if (conn->client_rsa_key == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: No client public key to verify "
+                          "signature");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       buflen = end - pos;
+       buf = os_malloc(end - pos);
+       if (crypto_public_key_decrypt_pkcs1(conn->client_rsa_key,
+                                           pos, end - pos, buf, &buflen) < 0)
+       {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt signature");
+               os_free(buf);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECRYPT_ERROR);
+               return -1;
+       }
+
+       wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature",
+                       buf, buflen);
+
+#ifdef CONFIG_TLSV12
+       if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+               /*
+                * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5
+                *
+                * DigestInfo ::= SEQUENCE {
+                *   digestAlgorithm DigestAlgorithm,
+                *   digest OCTET STRING
+                * }
+                *
+                * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11}
+                *
+                * DER encoded DigestInfo for SHA256 per RFC 3447:
+                * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 ||
+                * H
+                */
+               if (buflen >= 19 + 32 &&
+                   os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01"
+                             "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0)
+               {
+                       wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = "
+                                  "SHA-256");
+                       os_memmove(buf, buf + 19, buflen - 19);
+                       buflen -= 19;
+               } else {
+                       wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized "
+                                  "DigestInfo");
+                       os_free(buf);
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_DECRYPT_ERROR);
+                       return -1;
+               }
+       }
+#endif /* CONFIG_TLSV12 */
+
+       if (buflen != hlen || os_memcmp(buf, hash, buflen) != 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in "
+                          "CertificateVerify - did not match with calculated "
+                          "hash");
+               os_free(buf);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECRYPT_ERROR);
+               return -1;
+       }
+
+       os_free(buf);
+
+       *in_len = end - in_data;
+
+       conn->state = CHANGE_CIPHER_SPEC;
+
+       return 0;
+}
+
+
+static int tls_process_change_cipher_spec(struct tlsv1_server *conn,
+                                         u8 ct, const u8 *in_data,
+                                         size_t *in_len)
+{
+       const u8 *pos;
+       size_t left;
+
+       if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
+                          "received content type 0x%x", ct);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 1) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       if (*pos != TLS_CHANGE_CIPHER_SPEC) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
+                          "received data 0x%x", *pos);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec");
+       if (tlsv1_record_change_read_cipher(&conn->rl) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher "
+                          "for record layer");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       *in_len = pos + 1 - in_data;
+
+       conn->state = CLIENT_FINISHED;
+
+       return 0;
+}
+
+
+static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct,
+                                      const u8 *in_data, size_t *in_len)
+{
+       const u8 *pos, *end;
+       size_t left, len, hlen;
+       u8 verify_data[TLS_VERIFY_DATA_LEN];
+       u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; "
+                          "received content type 0x%x", ct);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 4) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for "
+                          "Finished",
+                          (unsigned long) left);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received "
+                          "type 0x%x", pos[0]);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       len = WPA_GET_BE24(pos + 1);
+
+       pos += 4;
+       left -= 4;
+
+       if (len > left) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished "
+                          "(len=%lu > left=%lu)",
+                          (unsigned long) len, (unsigned long) left);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+       end = pos + len;
+       if (len != TLS_VERIFY_DATA_LEN) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length "
+                          "in Finished: %lu (expected %d)",
+                          (unsigned long) len, TLS_VERIFY_DATA_LEN);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished",
+                   pos, TLS_VERIFY_DATA_LEN);
+
+#ifdef CONFIG_TLSV12
+       if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+               hlen = SHA256_MAC_LEN;
+               if (wpa2_crypto_funcs.crypto_hash_finish) {
+                       if (conn->verify.sha256_client == NULL ||
+                               crypto_hash_finish(conn->verify.sha256_client, hash, &hlen)
+                               < 0) {
+                               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                                                  TLS_ALERT_INTERNAL_ERROR);
+                               conn->verify.sha256_client = NULL;
+                               return -1;
+               }
+               } else {
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                                          TLS_ALERT_INTERNAL_ERROR);
+                       conn->verify.sha256_client = NULL;
+                       wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__);
+                       return -1;
+               }
+               conn->verify.sha256_client = NULL;
+       } else {
+#endif /* CONFIG_TLSV12 */
+
+       hlen = MD5_MAC_LEN;
+       if (conn->verify.md5_client == NULL ||
+           crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) {
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               conn->verify.md5_client = NULL;
+               crypto_hash_finish(conn->verify.sha1_client, NULL, NULL);
+               conn->verify.sha1_client = NULL;
+               return -1;
+       }
+       conn->verify.md5_client = NULL;
+       hlen = SHA1_MAC_LEN;
+       if (conn->verify.sha1_client == NULL ||
+           crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN,
+                              &hlen) < 0) {
+               conn->verify.sha1_client = NULL;
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       conn->verify.sha1_client = NULL;
+       hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+       }
+#endif /* CONFIG_TLSV12 */
+
+       if (tls_prf(conn->rl.tls_version,
+                   conn->master_secret, TLS_MASTER_SECRET_LEN,
+                   "client finished", hash, hlen,
+                   verify_data, TLS_VERIFY_DATA_LEN)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECRYPT_ERROR);
+               return -1;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)",
+                       verify_data, TLS_VERIFY_DATA_LEN);
+
+       if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
+               wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Received Finished");
+
+       *in_len = end - in_data;
+
+       if (conn->use_session_ticket) {
+               /* Abbreviated handshake using session ticket; RFC 4507 */
+               wpa_printf(MSG_DEBUG, "TLSv1: Abbreviated handshake completed "
+                          "successfully");
+               conn->state = ESTABLISHED;
+       } else {
+               /* Full handshake */
+               conn->state = SERVER_CHANGE_CIPHER_SPEC;
+       }
+
+       return 0;
+}
+
+
+int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct,
+                                  const u8 *buf, size_t *len)
+{
+       if (ct == TLS_CONTENT_TYPE_ALERT) {
+               if (*len < 2) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_DECODE_ERROR);
+                       return -1;
+               }
+               wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
+                          buf[0], buf[1]);
+               *len = 2;
+               conn->state = FAILED;
+               return -1;
+       }
+
+       switch (conn->state) {
+       case CLIENT_HELLO:
+               if (tls_process_client_hello(conn, ct, buf, len))
+                       return -1;
+               break;
+       case CLIENT_CERTIFICATE:
+               if (tls_process_certificate(conn, ct, buf, len))
+                       return -1;
+               break;
+       case CLIENT_KEY_EXCHANGE:
+               if (tls_process_client_key_exchange(conn, ct, buf, len))
+                       return -1;
+               break;
+       case CERTIFICATE_VERIFY:
+               if (tls_process_certificate_verify(conn, ct, buf, len))
+                       return -1;
+               break;
+       case CHANGE_CIPHER_SPEC:
+               if (tls_process_change_cipher_spec(conn, ct, buf, len))
+                       return -1;
+               break;
+       case CLIENT_FINISHED:
+               if (tls_process_client_finished(conn, ct, buf, len))
+                       return -1;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d "
+                          "while processing received message",
+                          conn->state);
+               return -1;
+       }
+
+       if (ct == TLS_CONTENT_TYPE_HANDSHAKE)
+               tls_verify_hash_add(&conn->verify, buf, *len);
+
+       return 0;
+}
diff --git a/components/wpa_supplicant/src/wpa2/tls/tlsv1_server_write.c b/components/wpa_supplicant/src/wpa2/tls/tlsv1_server_write.c
new file mode 100644 (file)
index 0000000..55eff1a
--- /dev/null
@@ -0,0 +1,819 @@
+/*
+ * TLSv1 server - write handshake message
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "wpa2/tls/tls.h"
+#include "wpa2/tls/x509v3.h"
+#include "wpa2/tls/tlsv1_common.h"
+#include "wpa2/tls/tlsv1_record.h"
+#include "wpa2/tls/tlsv1_server.h"
+#include "wpa2/tls/tlsv1_server_i.h"
+
+#include "wpa2/eap_peer/eap_i.h"
+
+static size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn)
+{
+       size_t len = 0;
+       struct x509_certificate *cert;
+
+       cert = conn->cred->cert;
+       while (cert) {
+               len += 3 + cert->cert_len;
+               if (x509_certificate_self_signed(cert))
+                       break;
+               cert = x509_certificate_get_subject(conn->cred->trusted_certs,
+                                                   &cert->issuer);
+       }
+
+       return len;
+}
+
+
+static int tls_write_server_hello(struct tlsv1_server *conn,
+                                 u8 **msgpos, u8 *end)
+{
+       u8 *pos, *rhdr, *hs_start, *hs_length;
+       struct os_time now;
+       size_t rlen;
+
+       pos = *msgpos;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHello");
+       rhdr = pos;
+       pos += TLS_RECORD_HEADER_LEN;
+
+       os_get_time(&now);
+       WPA_PUT_BE32(conn->server_random, now.sec);
+       if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) {
+               wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
+                          "server_random");
+               return -1;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random",
+                   conn->server_random, TLS_RANDOM_LEN);
+
+       conn->session_id_len = TLS_SESSION_ID_MAX_LEN;
+       if (random_get_bytes(conn->session_id, conn->session_id_len)) {
+               wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
+                          "session_id");
+               return -1;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id",
+                   conn->session_id, conn->session_id_len);
+
+       /* opaque fragment[TLSPlaintext.length] */
+
+       /* Handshake */
+       hs_start = pos;
+       /* HandshakeType msg_type */
+       *pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO;
+       /* uint24 length (to be filled) */
+       hs_length = pos;
+       pos += 3;
+       /* body - ServerHello */
+       /* ProtocolVersion server_version */
+       WPA_PUT_BE16(pos, conn->rl.tls_version);
+       pos += 2;
+       /* Random random: uint32 gmt_unix_time, opaque random_bytes */
+       os_memcpy(pos, conn->server_random, TLS_RANDOM_LEN);
+       pos += TLS_RANDOM_LEN;
+       /* SessionID session_id */
+       *pos++ = conn->session_id_len;
+       os_memcpy(pos, conn->session_id, conn->session_id_len);
+       pos += conn->session_id_len;
+       /* CipherSuite cipher_suite */
+       WPA_PUT_BE16(pos, conn->cipher_suite);
+       pos += 2;
+       /* CompressionMethod compression_method */
+       *pos++ = TLS_COMPRESSION_NULL;
+
+       if (conn->session_ticket && conn->session_ticket_cb) {
+               int res = conn->session_ticket_cb(
+                       conn->session_ticket_cb_ctx,
+                       conn->session_ticket, conn->session_ticket_len,
+                       conn->client_random, conn->server_random,
+                       conn->master_secret);
+               if (res < 0) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback "
+                                  "indicated failure");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_HANDSHAKE_FAILURE);
+                       return -1;
+               }
+               conn->use_session_ticket = res;
+
+               if (conn->use_session_ticket) {
+                       if (tlsv1_server_derive_keys(conn, NULL, 0) < 0) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
+                                          "derive keys");
+                               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                                  TLS_ALERT_INTERNAL_ERROR);
+                               return -1;
+                       }
+               }
+
+               /*
+                * RFC 4507 specifies that server would include an empty
+                * SessionTicket extension in ServerHello and a
+                * NewSessionTicket message after the ServerHello. However,
+                * EAP-FAST (RFC 4851), i.e., the only user of SessionTicket
+                * extension at the moment, does not use such extensions.
+                *
+                * TODO: Add support for configuring RFC 4507 behavior and make
+                * EAP-FAST disable it.
+                */
+       }
+
+       WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+       tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+                             rhdr, end - rhdr, hs_start, pos - hs_start,
+                             &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       pos = rhdr + rlen;
+
+       *msgpos = pos;
+
+       return 0;
+}
+
+
+static int tls_write_server_certificate(struct tlsv1_server *conn,
+                                       u8 **msgpos, u8 *end)
+{
+       u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start;
+       size_t rlen;
+       struct x509_certificate *cert;
+       const struct tls_cipher_suite *suite;
+
+       suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+       if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Do not send Certificate when "
+                          "using anonymous DH");
+               return 0;
+       }
+
+       pos = *msgpos;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate");
+       rhdr = pos;
+       pos += TLS_RECORD_HEADER_LEN;
+
+       /* opaque fragment[TLSPlaintext.length] */
+
+       /* Handshake */
+       hs_start = pos;
+       /* HandshakeType msg_type */
+       *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE;
+       /* uint24 length (to be filled) */
+       hs_length = pos;
+       pos += 3;
+       /* body - Certificate */
+       /* uint24 length (to be filled) */
+       cert_start = pos;
+       pos += 3;
+       cert = conn->cred->cert;
+       while (cert) {
+               if (pos + 3 + cert->cert_len > end) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space "
+                                  "for Certificate (cert_len=%lu left=%lu)",
+                                  (unsigned long) cert->cert_len,
+                                  (unsigned long) (end - pos));
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_INTERNAL_ERROR);
+                       return -1;
+               }
+               WPA_PUT_BE24(pos, cert->cert_len);
+               pos += 3;
+               os_memcpy(pos, cert->cert_start, cert->cert_len);
+               pos += cert->cert_len;
+
+               if (x509_certificate_self_signed(cert))
+                       break;
+               cert = x509_certificate_get_subject(conn->cred->trusted_certs,
+                                                   &cert->issuer);
+       }
+       if (cert == conn->cred->cert || cert == NULL) {
+               /*
+                * Server was not configured with all the needed certificates
+                * to form a full certificate chain. The client may fail to
+                * validate the chain unless it is configured with all the
+                * missing CA certificates.
+                */
+               wpa_printf(MSG_DEBUG, "TLSv1: Full server certificate chain "
+                          "not configured - validation may fail");
+       }
+       WPA_PUT_BE24(cert_start, pos - cert_start - 3);
+
+       WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+                             rhdr, end - rhdr, hs_start, pos - hs_start,
+                             &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       pos = rhdr + rlen;
+
+       tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+       *msgpos = pos;
+
+       return 0;
+}
+
+
+static int tls_write_server_key_exchange(struct tlsv1_server *conn,
+                                        u8 **msgpos, u8 *end)
+{
+       tls_key_exchange keyx;
+       const struct tls_cipher_suite *suite;
+       u8 *pos, *rhdr, *hs_start, *hs_length;
+       size_t rlen;
+       u8 *dh_ys;
+       size_t dh_ys_len;
+
+       suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+       if (suite == NULL)
+               keyx = TLS_KEY_X_NULL;
+       else
+               keyx = suite->key_exchange;
+
+       if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: No ServerKeyExchange needed");
+               return 0;
+       }
+
+       if (keyx != TLS_KEY_X_DH_anon) {
+               /* TODO? */
+               wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not yet "
+                          "supported with key exchange type %d", keyx);
+               return -1;
+       }
+
+       if (conn->cred == NULL || conn->cred->dh_p == NULL ||
+           conn->cred->dh_g == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available for "
+                          "ServerKeyExhcange");
+               return -1;
+       }
+
+       os_free(conn->dh_secret);
+       conn->dh_secret_len = conn->cred->dh_p_len;
+       conn->dh_secret = os_malloc(conn->dh_secret_len);
+       if (conn->dh_secret == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+                          "memory for secret (Diffie-Hellman)");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       if (random_get_bytes(conn->dh_secret, conn->dh_secret_len)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
+                          "data for Diffie-Hellman");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               os_free(conn->dh_secret);
+               conn->dh_secret = NULL;
+               return -1;
+       }
+
+       if (os_memcmp(conn->dh_secret, conn->cred->dh_p, conn->dh_secret_len) >
+           0)
+               conn->dh_secret[0] = 0; /* make sure secret < p */
+
+       pos = conn->dh_secret;
+       while (pos + 1 < conn->dh_secret + conn->dh_secret_len && *pos == 0)
+               pos++;
+       if (pos != conn->dh_secret) {
+               os_memmove(conn->dh_secret, pos,
+                          conn->dh_secret_len - (pos - conn->dh_secret));
+               conn->dh_secret_len -= pos - conn->dh_secret;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH server's secret value",
+                       conn->dh_secret, conn->dh_secret_len);
+
+       /* Ys = g^secret mod p */
+       dh_ys_len = conn->cred->dh_p_len;
+       dh_ys = os_malloc(dh_ys_len);
+       if (dh_ys == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate memory for "
+                          "Diffie-Hellman");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       if(wpa2_crypto_funcs.crypto_mod_exp) {
+               if (wpa2_crypto_funcs.crypto_mod_exp(conn->cred->dh_g, conn->cred->dh_g_len,
+                                                    conn->dh_secret, conn->dh_secret_len,
+                                                    conn->cred->dh_p, conn->cred->dh_p_len,
+                                                    dh_ys, &dh_ys_len)) {
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_INTERNAL_ERROR);
+                       os_free(dh_ys);
+                       return -1;
+               }
+       } else {
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               os_free(dh_ys);
+               wpa_printf(MSG_ERROR, "Fail to register crypto_mod_exp function!\r\n");
+               return -1;
+       }
+
+
+       wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)",
+                   dh_ys, dh_ys_len);
+
+       /*
+        * struct {
+        *    select (KeyExchangeAlgorithm) {
+        *       case diffie_hellman:
+        *          ServerDHParams params;
+        *          Signature signed_params;
+        *       case rsa:
+        *          ServerRSAParams params;
+        *          Signature signed_params;
+        *    };
+        * } ServerKeyExchange;
+        *
+        * struct {
+        *    opaque dh_p<1..2^16-1>;
+        *    opaque dh_g<1..2^16-1>;
+        *    opaque dh_Ys<1..2^16-1>;
+        * } ServerDHParams;
+        */
+
+       pos = *msgpos;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send ServerKeyExchange");
+       rhdr = pos;
+       pos += TLS_RECORD_HEADER_LEN;
+
+       /* opaque fragment[TLSPlaintext.length] */
+
+       /* Handshake */
+       hs_start = pos;
+       /* HandshakeType msg_type */
+       *pos++ = TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE;
+       /* uint24 length (to be filled) */
+       hs_length = pos;
+       pos += 3;
+
+       /* body - ServerDHParams */
+       /* dh_p */
+       if (pos + 2 + conn->cred->dh_p_len > end) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
+                          "dh_p");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               os_free(dh_ys);
+               return -1;
+       }
+       WPA_PUT_BE16(pos, conn->cred->dh_p_len);
+       pos += 2;
+       os_memcpy(pos, conn->cred->dh_p, conn->cred->dh_p_len);
+       pos += conn->cred->dh_p_len;
+
+       /* dh_g */
+       if (pos + 2 + conn->cred->dh_g_len > end) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
+                          "dh_g");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               os_free(dh_ys);
+               return -1;
+       }
+       WPA_PUT_BE16(pos, conn->cred->dh_g_len);
+       pos += 2;
+       os_memcpy(pos, conn->cred->dh_g, conn->cred->dh_g_len);
+       pos += conn->cred->dh_g_len;
+
+       /* dh_Ys */
+       if (pos + 2 + dh_ys_len > end) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
+                          "dh_Ys");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               os_free(dh_ys);
+               return -1;
+       }
+       WPA_PUT_BE16(pos, dh_ys_len);
+       pos += 2;
+       os_memcpy(pos, dh_ys, dh_ys_len);
+       pos += dh_ys_len;
+       os_free(dh_ys);
+
+       WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+                             rhdr, end - rhdr, hs_start, pos - hs_start,
+                             &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       pos = rhdr + rlen;
+
+       tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+       *msgpos = pos;
+
+       return 0;
+}
+
+
+static int tls_write_server_certificate_request(struct tlsv1_server *conn,
+                                               u8 **msgpos, u8 *end)
+{
+       u8 *pos, *rhdr, *hs_start, *hs_length;
+       size_t rlen;
+
+       if (!conn->verify_peer) {
+               wpa_printf(MSG_DEBUG, "TLSv1: No CertificateRequest needed");
+               return 0;
+       }
+
+       pos = *msgpos;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateRequest");
+       rhdr = pos;
+       pos += TLS_RECORD_HEADER_LEN;
+
+       /* opaque fragment[TLSPlaintext.length] */
+
+       /* Handshake */
+       hs_start = pos;
+       /* HandshakeType msg_type */
+       *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST;
+       /* uint24 length (to be filled) */
+       hs_length = pos;
+       pos += 3;
+       /* body - CertificateRequest */
+
+       /*
+        * enum {
+        *   rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4),
+        *   (255)
+        * } ClientCertificateType;
+        * ClientCertificateType certificate_types<1..2^8-1>
+        */
+       *pos++ = 1;
+       *pos++ = 1; /* rsa_sign */
+
+       /*
+        * opaque DistinguishedName<1..2^16-1>
+        * DistinguishedName certificate_authorities<3..2^16-1>
+        */
+       /* TODO: add support for listing DNs for trusted CAs */
+       WPA_PUT_BE16(pos, 0);
+       pos += 2;
+
+       WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+                             rhdr, end - rhdr, hs_start, pos - hs_start,
+                             &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       pos = rhdr + rlen;
+
+       tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+       *msgpos = pos;
+
+       return 0;
+}
+
+
+static int tls_write_server_hello_done(struct tlsv1_server *conn,
+                                      u8 **msgpos, u8 *end)
+{
+       u8 *pos;
+       size_t rlen;
+       u8 payload[4];
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone");
+
+       /* opaque fragment[TLSPlaintext.length] */
+
+       /* Handshake */
+       pos = payload;
+       /* HandshakeType msg_type */
+       *pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE;
+       /* uint24 length */
+       WPA_PUT_BE24(pos, 0);
+       pos += 3;
+       /* body - ServerHelloDone (empty) */
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+                             *msgpos, end - *msgpos, payload, pos - payload,
+                             &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       tls_verify_hash_add(&conn->verify, payload, pos - payload);
+
+       *msgpos += rlen;
+
+       return 0;
+}
+
+
+static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn,
+                                              u8 **msgpos, u8 *end)
+{
+       size_t rlen;
+       u8 payload[1];
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
+
+       payload[0] = TLS_CHANGE_CIPHER_SPEC;
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
+                             *msgpos, end - *msgpos, payload, sizeof(payload),
+                             &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       if (tlsv1_record_change_write_cipher(&conn->rl) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for "
+                          "record layer");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       *msgpos += rlen;
+
+       return 0;
+}
+
+
+static int tls_write_server_finished(struct tlsv1_server *conn,
+                                    u8 **msgpos, u8 *end)
+{
+       u8 *pos, *hs_start;
+       size_t rlen, hlen;
+       u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN];
+       u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+       pos = *msgpos;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send Finished");
+
+       /* Encrypted Handshake Message: Finished */
+
+#ifdef CONFIG_TLSV12
+       if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+               hlen = SHA256_MAC_LEN;
+               if (wpa2_crypto_funcs.crypto_hash_finish) {
+                       if (conn->verify.sha256_server == NULL ||
+                               wpa2_crypto_funcs.crypto_hash_finish(conn->verify.sha256_server, hash, &hlen)
+                               < 0) {
+                               conn->verify.sha256_server = NULL;
+                               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                                  TLS_ALERT_INTERNAL_ERROR);
+                               return -1;
+                       }
+               } else {
+                       conn->verify.sha256_server = NULL;
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_INTERNAL_ERROR);
+                       wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__);
+                       return -1;
+               }
+               conn->verify.sha256_server = NULL;
+       } else {
+#endif /* CONFIG_TLSV12 */
+
+       hlen = MD5_MAC_LEN;
+       if (conn->verify.md5_server == NULL ||
+           crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) {
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               conn->verify.md5_server = NULL;
+               crypto_hash_finish(conn->verify.sha1_server, NULL, NULL);
+               conn->verify.sha1_server = NULL;
+               return -1;
+       }
+       conn->verify.md5_server = NULL;
+       hlen = SHA1_MAC_LEN;
+       if (conn->verify.sha1_server == NULL ||
+           crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN,
+                              &hlen) < 0) {
+               conn->verify.sha1_server = NULL;
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       conn->verify.sha1_server = NULL;
+       hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
+
+#ifdef CONFIG_TLSV12
+       }
+#endif /* CONFIG_TLSV12 */
+
+       if (tls_prf(conn->rl.tls_version,
+                   conn->master_secret, TLS_MASTER_SECRET_LEN,
+                   "server finished", hash, hlen,
+                   verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
+                       verify_data + 1 + 3, TLS_VERIFY_DATA_LEN);
+
+       /* Handshake */
+       pos = hs_start = verify_data;
+       /* HandshakeType msg_type */
+       *pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
+       /* uint24 length */
+       WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN);
+       pos += 3;
+       pos += TLS_VERIFY_DATA_LEN;
+       tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+                             *msgpos, end - *msgpos, hs_start, pos - hs_start,
+                             &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       *msgpos += rlen;
+
+       return 0;
+}
+
+
+static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len)
+{
+       u8 *msg, *end, *pos;
+       size_t msglen;
+
+       *out_len = 0;
+
+       msglen = 1000 + tls_server_cert_chain_der_len(conn);
+
+       msg = os_malloc(msglen);
+       if (msg == NULL)
+               return NULL;
+
+       pos = msg;
+       end = msg + msglen;
+
+       if (tls_write_server_hello(conn, &pos, end) < 0) {
+               os_free(msg);
+               return NULL;
+       }
+
+       if (conn->use_session_ticket) {
+               /* Abbreviated handshake using session ticket; RFC 4507 */
+               if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 ||
+                   tls_write_server_finished(conn, &pos, end) < 0) {
+                       os_free(msg);
+                       return NULL;
+               }
+
+               *out_len = pos - msg;
+
+               conn->state = CHANGE_CIPHER_SPEC;
+
+               return msg;
+       }
+
+       /* Full handshake */
+       if (tls_write_server_certificate(conn, &pos, end) < 0 ||
+           tls_write_server_key_exchange(conn, &pos, end) < 0 ||
+           tls_write_server_certificate_request(conn, &pos, end) < 0 ||
+           tls_write_server_hello_done(conn, &pos, end) < 0) {
+               os_free(msg);
+               return NULL;
+       }
+
+       *out_len = pos - msg;
+
+       conn->state = CLIENT_CERTIFICATE;
+
+       return msg;
+}
+
+
+static u8 * tls_send_change_cipher_spec(struct tlsv1_server *conn,
+                                       size_t *out_len)
+{
+       u8 *msg, *end, *pos;
+
+       *out_len = 0;
+
+       msg = os_malloc(1000);
+       if (msg == NULL)
+               return NULL;
+
+       pos = msg;
+       end = msg + 1000;
+
+       if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 ||
+           tls_write_server_finished(conn, &pos, end) < 0) {
+               os_free(msg);
+               return NULL;
+       }
+
+       *out_len = pos - msg;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed successfully");
+       conn->state = ESTABLISHED;
+
+       return msg;
+}
+
+
+u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len)
+{
+       switch (conn->state) {
+       case SERVER_HELLO:
+               return tls_send_server_hello(conn, out_len);
+       case SERVER_CHANGE_CIPHER_SPEC:
+               return tls_send_change_cipher_spec(conn, out_len);
+       default:
+               if (conn->state == ESTABLISHED && conn->use_session_ticket) {
+                       /* Abbreviated handshake was already completed. */
+                       return NULL;
+               }
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while "
+                          "generating reply", conn->state);
+               return NULL;
+       }
+}
+
+
+u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level,
+                            u8 description, size_t *out_len)
+{
+       u8 *alert, *pos, *length;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description);
+       *out_len = 0;
+
+       alert = os_malloc(10);
+       if (alert == NULL)
+               return NULL;
+
+       pos = alert;
+
+       /* TLSPlaintext */
+       /* ContentType type */
+       *pos++ = TLS_CONTENT_TYPE_ALERT;
+       /* ProtocolVersion version */
+       WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version :
+                    TLS_VERSION);
+       pos += 2;
+       /* uint16 length (to be filled) */
+       length = pos;
+       pos += 2;
+       /* opaque fragment[TLSPlaintext.length] */
+
+       /* Alert */
+       /* AlertLevel level */
+       *pos++ = level;
+       /* AlertDescription description */
+       *pos++ = description;
+
+       WPA_PUT_BE16(length, pos - length - 2);
+       *out_len = pos - alert;
+
+       return alert;
+}
diff --git a/components/wpa_supplicant/src/wpa2/tls/x509v3.c b/components/wpa_supplicant/src/wpa2/tls/x509v3.c
new file mode 100644 (file)
index 0000000..f7962db
--- /dev/null
@@ -0,0 +1,2013 @@
+/*
+ * X.509v3 certificate parsing and processing (RFC 3280 profile)
+ * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+#include "wpa/wpa.h"
+#include "wpa/common.h"
+#include "crypto/crypto.h"
+#include "wpa2/tls/asn1.h"
+#include "wpa2/tls/x509v3.h"
+
+#include "wpa2/eap_peer/eap_i.h"
+
+static void x509_free_name(struct x509_name *name)
+{
+       size_t i;
+
+       for (i = 0; i < name->num_attr; i++) {
+               os_free(name->attr[i].value);
+               name->attr[i].value = NULL;
+               name->attr[i].type = X509_NAME_ATTR_NOT_USED;
+       }
+       name->num_attr = 0;
+       os_free(name->email);
+       name->email = NULL;
+
+       os_free(name->alt_email);
+       os_free(name->dns);
+       os_free(name->uri);
+       os_free(name->ip);
+       name->alt_email = name->dns = name->uri = NULL;
+       name->ip = NULL;
+       name->ip_len = 0;
+       os_memset(&name->rid, 0, sizeof(name->rid));
+}
+
+
+/**
+ * x509_certificate_free - Free an X.509 certificate
+ * @cert: Certificate to be freed
+ */
+void x509_certificate_free(struct x509_certificate *cert)
+{
+       if (cert == NULL)
+               return;
+       if (cert->next) {
+               wpa_printf(MSG_DEBUG, "X509: x509_certificate_free: cer=%p "
+                          "was still on a list (next=%p)\n",
+                          cert, cert->next);
+       }
+       x509_free_name(&cert->issuer);
+       x509_free_name(&cert->subject);
+       os_free(cert->public_key);
+       os_free(cert->sign_value);
+       os_free(cert);
+}
+
+
+/**
+ * x509_certificate_free - Free an X.509 certificate chain
+ * @cert: Pointer to the first certificate in the chain
+ */
+void x509_certificate_chain_free(struct x509_certificate *cert)
+{
+       struct x509_certificate *next;
+
+       while (cert) {
+               next = cert->next;
+               cert->next = NULL;
+               x509_certificate_free(cert);
+               cert = next;
+       }
+}
+
+
+static int x509_whitespace(char c)
+{
+       return c == ' ' || c == '\t';
+}
+
+
+static void x509_str_strip_whitespace(char *a)
+{
+       char *ipos, *opos;
+       int remove_whitespace = 1;
+
+       ipos = opos = a;
+
+       while (*ipos) {
+               if (remove_whitespace && x509_whitespace(*ipos))
+                       ipos++;
+               else {
+                       remove_whitespace = x509_whitespace(*ipos);
+                       *opos++ = *ipos++;
+               }
+       }
+
+       *opos-- = '\0';
+       if (opos > a && x509_whitespace(*opos))
+               *opos = '\0';
+}
+
+
+static int x509_str_compare(const char *a, const char *b)
+{
+       char *aa, *bb;
+       int ret;
+
+       if (!a && b)
+               return -1;
+       if (a && !b)
+               return 1;
+       if (!a && !b)
+               return 0;
+
+       aa = os_strdup(a);
+       bb = os_strdup(b);
+
+       if (aa == NULL || bb == NULL) {
+               os_free(aa);
+               os_free(bb);
+               return strcasecmp(a, b);
+       }
+
+       x509_str_strip_whitespace(aa);
+       x509_str_strip_whitespace(bb);
+
+       ret = strcasecmp(aa, bb);
+
+       os_free(aa);
+       os_free(bb);
+
+       return ret;
+}
+
+
+/**
+ * x509_name_compare - Compare X.509 certificate names
+ * @a: Certificate name
+ * @b: Certificate name
+ * Returns: <0, 0, or >0 based on whether a is less than, equal to, or
+ * greater than b
+ */
+int x509_name_compare(struct x509_name *a, struct x509_name *b)
+{
+       int res;
+       size_t i;
+
+       if (!a && b)
+               return -1;
+       if (a && !b)
+               return 1;
+       if (!a && !b)
+               return 0;
+       if (a->num_attr < b->num_attr)
+               return -1;
+       if (a->num_attr > b->num_attr)
+               return 1;
+
+       for (i = 0; i < a->num_attr; i++) {
+               if (a->attr[i].type < b->attr[i].type)
+                       return -1;
+               if (a->attr[i].type > b->attr[i].type)
+                       return -1;
+               res = x509_str_compare(a->attr[i].value, b->attr[i].value);
+               if (res)
+                       return res;
+       }
+       res = x509_str_compare(a->email, b->email);
+       if (res)
+               return res;
+
+       return 0;
+}
+
+
+static int x509_parse_algorithm_identifier(
+       const u8 *buf, size_t len,
+       struct x509_algorithm_identifier *id, const u8 **next)
+{
+       struct asn1_hdr hdr;
+       const u8 *pos, *end;
+
+       /*
+        * AlgorithmIdentifier ::= SEQUENCE {
+        *     algorithm            OBJECT IDENTIFIER,
+        *     parameters           ANY DEFINED BY algorithm OPTIONAL
+        * }
+        */
+
+       if (asn1_get_next(buf, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+                          "(AlgorithmIdentifier) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+       pos = hdr.payload;
+       end = pos + hdr.length;
+
+       if (end > buf + len)
+               return -1;
+
+       *next = end;
+
+       if (asn1_get_oid(pos, end - pos, &id->oid, &pos))
+               return -1;
+
+       /* TODO: optional parameters */
+
+       return 0;
+}
+
+
+static int x509_parse_public_key(const u8 *buf, size_t len,
+                                struct x509_certificate *cert,
+                                const u8 **next)
+{
+       struct asn1_hdr hdr;
+       const u8 *pos, *end;
+
+       /*
+        * SubjectPublicKeyInfo ::= SEQUENCE {
+        *     algorithm            AlgorithmIdentifier,
+        *     subjectPublicKey     BIT STRING
+        * }
+        */
+
+       pos = buf;
+       end = buf + len;
+
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+                          "(SubjectPublicKeyInfo) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+       pos = hdr.payload;
+
+       if (pos + hdr.length > end)
+               return -1;
+       end = pos + hdr.length;
+       *next = end;
+
+       if (x509_parse_algorithm_identifier(pos, end - pos,
+                                           &cert->public_key_alg, &pos))
+               return -1;
+
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_BITSTRING) {
+               wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING "
+                          "(subjectPublicKey) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+       if (hdr.length < 1)
+               return -1;
+       pos = hdr.payload;
+       if (*pos) {
+               wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits",
+                          *pos);
+               /*
+                * TODO: should this be rejected? X.509 certificates are
+                * unlikely to use such a construction. Now we would end up
+                * including the extra bits in the buffer which may also be
+                * ok.
+                */
+       }
+       os_free(cert->public_key);
+       cert->public_key = os_malloc(hdr.length - 1);
+       if (cert->public_key == NULL) {
+               wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for "
+                          "public key");
+               return -1;
+       }
+       os_memcpy(cert->public_key, pos + 1, hdr.length - 1);
+       cert->public_key_len = hdr.length - 1;
+       wpa_hexdump(MSG_MSGDUMP, "X509: subjectPublicKey",
+                   cert->public_key, cert->public_key_len);
+
+       return 0;
+}
+
+
+static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name,
+                          const u8 **next)
+{
+       struct asn1_hdr hdr;
+       const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end;
+       struct asn1_oid oid;
+       char *val;
+
+       /*
+        * Name ::= CHOICE { RDNSequence }
+        * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+        * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
+        * AttributeTypeAndValue ::= SEQUENCE {
+        *     type     AttributeType,
+        *     value    AttributeValue
+        * }
+        * AttributeType ::= OBJECT IDENTIFIER
+        * AttributeValue ::= ANY DEFINED BY AttributeType
+        */
+
+       if (asn1_get_next(buf, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+                          "(Name / RDNSequencer) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+       pos = hdr.payload;
+
+       if (pos + hdr.length > buf + len)
+               return -1;
+
+       end = *next = pos + hdr.length;
+
+       while (pos < end) {
+               enum x509_name_attr_type type;
+
+               if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+                   hdr.class != ASN1_CLASS_UNIVERSAL ||
+                   hdr.tag != ASN1_TAG_SET) {
+                       wpa_printf(MSG_DEBUG, "X509: Expected SET "
+                                  "(RelativeDistinguishedName) - found class "
+                                  "%d tag 0x%x", hdr.class, hdr.tag);
+                       x509_free_name(name);
+                       return -1;
+               }
+
+               set_pos = hdr.payload;
+               pos = set_end = hdr.payload + hdr.length;
+
+               if (asn1_get_next(set_pos, set_end - set_pos, &hdr) < 0 ||
+                   hdr.class != ASN1_CLASS_UNIVERSAL ||
+                   hdr.tag != ASN1_TAG_SEQUENCE) {
+                       wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+                                  "(AttributeTypeAndValue) - found class %d "
+                                  "tag 0x%x", hdr.class, hdr.tag);
+                       x509_free_name(name);
+                       return -1;
+               }
+
+               seq_pos = hdr.payload;
+               seq_end = hdr.payload + hdr.length;
+
+               if (asn1_get_oid(seq_pos, seq_end - seq_pos, &oid, &seq_pos)) {
+                       x509_free_name(name);
+                       return -1;
+               }
+
+               if (asn1_get_next(seq_pos, seq_end - seq_pos, &hdr) < 0 ||
+                   hdr.class != ASN1_CLASS_UNIVERSAL) {
+                       wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+                                  "AttributeValue");
+                       x509_free_name(name);
+                       return -1;
+               }
+
+               /* RFC 3280:
+                * MUST: country, organization, organizational-unit,
+                * distinguished name qualifier, state or province name,
+                * common name, serial number.
+                * SHOULD: locality, title, surname, given name, initials,
+                * pseudonym, generation qualifier.
+                * MUST: domainComponent (RFC 2247).
+                */
+               type = X509_NAME_ATTR_NOT_USED;
+               if (oid.len == 4 &&
+                   oid.oid[0] == 2 && oid.oid[1] == 5 && oid.oid[2] == 4) {
+                       /* id-at ::= 2.5.4 */
+                       switch (oid.oid[3]) {
+                       case 3:
+                               /* commonName */
+                               type = X509_NAME_ATTR_CN;
+                               break;
+                       case 6:
+                               /*  countryName */
+                               type = X509_NAME_ATTR_C;
+                               break;
+                       case 7:
+                               /* localityName */
+                               type = X509_NAME_ATTR_L;
+                               break;
+                       case 8:
+                               /* stateOrProvinceName */
+                               type = X509_NAME_ATTR_ST;
+                               break;
+                       case 10:
+                               /* organizationName */
+                               type = X509_NAME_ATTR_O;
+                               break;
+                       case 11:
+                               /* organizationalUnitName */
+                               type = X509_NAME_ATTR_OU;
+                               break;
+                       }
+               } else if (oid.len == 7 &&
+                          oid.oid[0] == 1 && oid.oid[1] == 2 &&
+                          oid.oid[2] == 840 && oid.oid[3] == 113549 &&
+                          oid.oid[4] == 1 && oid.oid[5] == 9 &&
+                          oid.oid[6] == 1) {
+                       /* 1.2.840.113549.1.9.1 - e-mailAddress */
+                       os_free(name->email);
+                       name->email = os_malloc(hdr.length + 1);
+                       if (name->email == NULL) {
+                               x509_free_name(name);
+                               return -1;
+                       }
+                       os_memcpy(name->email, hdr.payload, hdr.length);
+                       name->email[hdr.length] = '\0';
+                       continue;
+               } else if (oid.len == 7 &&
+                          oid.oid[0] == 0 && oid.oid[1] == 9 &&
+                          oid.oid[2] == 2342 && oid.oid[3] == 19200300 &&
+                          oid.oid[4] == 100 && oid.oid[5] == 1 &&
+                          oid.oid[6] == 25) {
+                       /* 0.9.2342.19200300.100.1.25 - domainComponent */
+                       type = X509_NAME_ATTR_DC;
+               }
+
+               if (type == X509_NAME_ATTR_NOT_USED) {
+                       wpa_hexdump(MSG_DEBUG, "X509: Unrecognized OID",
+                                   (u8 *) oid.oid,
+                                   oid.len * sizeof(oid.oid[0]));
+                       wpa_hexdump_ascii(MSG_MSGDUMP, "X509: Attribute Data",
+                                         hdr.payload, hdr.length);
+                       continue;
+               }
+
+               if (name->num_attr == X509_MAX_NAME_ATTRIBUTES) {
+                       wpa_printf(MSG_INFO, "X509: Too many Name attributes");
+                       x509_free_name(name);
+                       return -1;
+               }
+
+               val = (char *)dup_binstr(hdr.payload, hdr.length);
+               if (val == NULL) {
+                       x509_free_name(name);
+                       return -1;
+               }
+               if (os_strlen(val) != hdr.length) {
+                       wpa_printf(MSG_INFO, "X509: Reject certificate with "
+                                  "embedded NUL byte in a string (%s[NUL])",
+                                  val);
+                       os_free(val);
+                       x509_free_name(name);
+                       return -1;
+               }
+
+               name->attr[name->num_attr].type = type;
+               name->attr[name->num_attr].value = val;
+               name->num_attr++;
+       }
+
+       return 0;
+}
+
+
+static char * x509_name_attr_str(enum x509_name_attr_type type)
+{
+#ifndef ESPRESSIF_USE  
+       switch (type) {
+       case X509_NAME_ATTR_NOT_USED:
+               return "[N/A]";
+       case X509_NAME_ATTR_DC:
+               return "DC";
+       case X509_NAME_ATTR_CN:
+               return "CN";
+       case X509_NAME_ATTR_C:
+               return "C";
+       case X509_NAME_ATTR_L:
+               return "L";
+       case X509_NAME_ATTR_ST:
+               return "ST";
+       case X509_NAME_ATTR_O:
+               return "O";
+       case X509_NAME_ATTR_OU:
+               return "OU";
+       }
+       return "?";
+#else
+    static char name_attr[6];
+       switch (type) {
+        case X509_NAME_ATTR_NOT_USED:
+            strcpy(name_attr, "[N/A]");
+        case X509_NAME_ATTR_DC:
+            strcpy(name_attr, "DC");
+        case X509_NAME_ATTR_CN:
+            strcpy(name_attr, "CN");
+        case X509_NAME_ATTR_C:
+            strcpy(name_attr, "C");
+        case X509_NAME_ATTR_L:
+            strcpy(name_attr, "L");
+        case X509_NAME_ATTR_ST:
+            strcpy(name_attr, "ST");
+        case X509_NAME_ATTR_O:
+            strcpy(name_attr, "O");
+        case X509_NAME_ATTR_OU:
+            strcpy(name_attr, "OU");
+        default :
+            strcpy(name_attr, "?");
+       }
+       return name_attr;
+#endif 
+}
+
+
+/**
+ * x509_name_string - Convert an X.509 certificate name into a string
+ * @name: Name to convert
+ * @buf: Buffer for the string
+ * @len: Maximum buffer length
+ */
+void x509_name_string(struct x509_name *name, char *buf, size_t len)
+{
+       char *pos, *end;
+       int ret;
+       size_t i;
+
+       if (len == 0)
+               return;
+
+       pos = buf;
+       end = buf + len;
+
+       for (i = 0; i < name->num_attr; i++) {
+               //ret = os_snprintf(pos, end - pos, "%s=%s, ",
+               ret = sprintf(pos, "%s=%s, ",
+                                 x509_name_attr_str(name->attr[i].type),
+                                 name->attr[i].value);
+               if (ret < 0 || ret >= end - pos)
+                       goto done;
+               pos += ret;
+       }
+
+       if (pos > buf + 1 && pos[-1] == ' ' && pos[-2] == ',') {
+               pos--;
+               *pos = '\0';
+               pos--;
+               *pos = '\0';
+       }
+
+       if (name->email) {
+               //ret = os_snprintf(pos, end - pos, "/emailAddress=%s",
+               ret = sprintf(pos, "/emailAddress=%s",
+                                 name->email);
+               if (ret < 0 || ret >= end - pos)
+                       goto done;
+               pos += ret;
+       }
+
+done:
+       end[-1] = '\0';
+}
+
+
+static int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag,
+                          os_time_t *val)
+{
+#if 0
+       const char *pos;
+       int year, month, day, hour, min, sec;
+
+       /*
+        * Time ::= CHOICE {
+        *     utcTime        UTCTime,
+        *     generalTime    GeneralizedTime
+        * }
+        *
+        * UTCTime: YYMMDDHHMMSSZ
+        * GeneralizedTime: YYYYMMDDHHMMSSZ
+        */
+
+       pos = (const char *) buf;
+
+       switch (asn1_tag) {
+       case ASN1_TAG_UTCTIME:
+               if (len != 13 || buf[12] != 'Z') {
+                       wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized "
+                                         "UTCTime format", buf, len);
+                       return -1;
+               }
+               if (sscanf(pos, "%02d", &year) != 1) {
+                       wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse "
+                                         "UTCTime year", buf, len);
+                       return -1;
+               }
+               if (year < 50)
+                       year += 2000;
+               else
+                       year += 1900;
+               pos += 2;
+               break;
+       case ASN1_TAG_GENERALIZEDTIME:
+               if (len != 15 || buf[14] != 'Z') {
+                       wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized "
+                                         "GeneralizedTime format", buf, len);
+                       return -1;
+               }
+               if (sscanf(pos, "%04d", &year) != 1) {
+                       wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse "
+                                         "GeneralizedTime year", buf, len);
+                       return -1;
+               }
+               pos += 4;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "X509: Expected UTCTime or "
+                          "GeneralizedTime - found tag 0x%x", asn1_tag);
+               return -1;
+       }
+
+       if (sscanf(pos, "%02d", &month) != 1) {
+               wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+                                 "(month)", buf, len);
+               return -1;
+       }
+       pos += 2;
+
+       if (sscanf(pos, "%02d", &day) != 1) {
+               wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+                                 "(day)", buf, len);
+               return -1;
+       }
+       pos += 2;
+
+       if (sscanf(pos, "%02d", &hour) != 1) {
+               wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+                                 "(hour)", buf, len);
+               return -1;
+       }
+       pos += 2;
+
+       if (sscanf(pos, "%02d", &min) != 1) {
+               wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+                                 "(min)", buf, len);
+               return -1;
+       }
+       pos += 2;
+
+       if (sscanf(pos, "%02d", &sec) != 1) {
+               wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+                                 "(sec)", buf, len);
+               return -1;
+       }
+
+       if (os_mktime(year, month, day, hour, min, sec, val) < 0) {
+               wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to convert Time",
+                                 buf, len);
+               if (year < 1970) {
+                       /*
+                        * At least some test certificates have been configured
+                        * to use dates prior to 1970. Set the date to
+                        * beginning of 1970 to handle these case.
+                        */
+                       wpa_printf(MSG_DEBUG, "X509: Year=%d before epoch - "
+                                  "assume epoch as the time", year);
+                       *val = 0;
+                       return 0;
+               }
+               return -1;
+       }
+#endif
+       return 0;
+}
+
+
+static int x509_parse_validity(const u8 *buf, size_t len,
+                              struct x509_certificate *cert, const u8 **next)
+{
+       struct asn1_hdr hdr;
+       const u8 *pos;
+       size_t plen;
+
+       /*
+        * Validity ::= SEQUENCE {
+        *     notBefore      Time,
+        *     notAfter       Time
+        * }
+        *
+        * RFC 3280, 4.1.2.5:
+        * CAs conforming to this profile MUST always encode certificate
+        * validity dates through the year 2049 as UTCTime; certificate
+        * validity dates in 2050 or later MUST be encoded as GeneralizedTime.
+        */
+
+       if (asn1_get_next(buf, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+                          "(Validity) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+       pos = hdr.payload;
+       plen = hdr.length;
+
+       if (pos + plen > buf + len)
+               return -1;
+
+       *next = pos + plen;
+
+       if (asn1_get_next(pos, plen, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           x509_parse_time(hdr.payload, hdr.length, hdr.tag,
+                           &cert->not_before) < 0) {
+               wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notBefore "
+                                 "Time", hdr.payload, hdr.length);
+               return -1;
+       }
+
+       pos = hdr.payload + hdr.length;
+       plen = *next - pos;
+
+       if (asn1_get_next(pos, plen, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           x509_parse_time(hdr.payload, hdr.length, hdr.tag,
+                           &cert->not_after) < 0) {
+               wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notAfter "
+                                 "Time", hdr.payload, hdr.length);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "X509: Validity: notBefore: %lu notAfter: %lu",
+                  (unsigned long) cert->not_before,
+                  (unsigned long) cert->not_after);
+
+       return 0;
+}
+
+
+static int x509_id_ce_oid(struct asn1_oid *oid)
+{
+       /* id-ce arc from X.509 for standard X.509v3 extensions */
+       return oid->len >= 4 &&
+               oid->oid[0] == 2 /* joint-iso-ccitt */ &&
+               oid->oid[1] == 5 /* ds */ &&
+               oid->oid[2] == 29 /* id-ce */;
+}
+
+
+static int x509_parse_ext_key_usage(struct x509_certificate *cert,
+                                   const u8 *pos, size_t len)
+{
+       struct asn1_hdr hdr;
+
+       /*
+        * KeyUsage ::= BIT STRING {
+        *     digitalSignature        (0),
+        *     nonRepudiation          (1),
+        *     keyEncipherment         (2),
+        *     dataEncipherment        (3),
+        *     keyAgreement            (4),
+        *     keyCertSign             (5),
+        *     cRLSign                 (6),
+        *     encipherOnly            (7),
+        *     decipherOnly            (8) }
+        */
+
+       if (asn1_get_next(pos, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_BITSTRING ||
+           hdr.length < 1) {
+               wpa_printf(MSG_DEBUG, "X509: Expected BIT STRING in "
+                          "KeyUsage; found %d tag 0x%x len %d",
+                          hdr.class, hdr.tag, hdr.length);
+               return -1;
+       }
+
+       cert->extensions_present |= X509_EXT_KEY_USAGE;
+       cert->key_usage = asn1_bit_string_to_long(hdr.payload, hdr.length);
+
+       wpa_printf(MSG_DEBUG, "X509: KeyUsage 0x%lx", cert->key_usage);
+
+       return 0;
+}
+
+
+static int x509_parse_ext_basic_constraints(struct x509_certificate *cert,
+                                           const u8 *pos, size_t len)
+{
+       struct asn1_hdr hdr;
+       unsigned long value;
+       size_t left;
+
+       /*
+        * BasicConstraints ::= SEQUENCE {
+        * cA                      BOOLEAN DEFAULT FALSE,
+        * pathLenConstraint       INTEGER (0..MAX) OPTIONAL }
+        */
+
+       if (asn1_get_next(pos, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in "
+                          "BasicConstraints; found %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+
+       cert->extensions_present |= X509_EXT_BASIC_CONSTRAINTS;
+
+       if (hdr.length == 0)
+               return 0;
+
+       if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL) {
+               wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+                          "BasicConstraints");
+               return -1;
+       }
+
+       if (hdr.tag == ASN1_TAG_BOOLEAN) {
+               if (hdr.length != 1) {
+                       wpa_printf(MSG_DEBUG, "X509: Unexpected "
+                                  "Boolean length (%u) in BasicConstraints",
+                                  hdr.length);
+                       return -1;
+               }
+               cert->ca = hdr.payload[0];
+
+               if (hdr.payload + hdr.length == pos + len) {
+                       wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d",
+                                  cert->ca);
+                       return 0;
+               }
+
+               if (asn1_get_next(hdr.payload + hdr.length, len - hdr.length,
+                                 &hdr) < 0 ||
+                   hdr.class != ASN1_CLASS_UNIVERSAL) {
+                       wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+                                  "BasicConstraints");
+                       return -1;
+               }
+       }
+
+       if (hdr.tag != ASN1_TAG_INTEGER) {
+               wpa_printf(MSG_DEBUG, "X509: Expected INTEGER in "
+                          "BasicConstraints; found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+
+       pos = hdr.payload;
+       left = hdr.length;
+       value = 0;
+       while (left) {
+               value <<= 8;
+               value |= *pos++;
+               left--;
+       }
+
+       cert->path_len_constraint = value;
+       cert->extensions_present |= X509_EXT_PATH_LEN_CONSTRAINT;
+
+       wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d "
+                  "pathLenConstraint=%lu",
+                  cert->ca, cert->path_len_constraint);
+
+       return 0;
+}
+
+
+static int x509_parse_alt_name_rfc8222(struct x509_name *name,
+                                      const u8 *pos, size_t len)
+{
+       /* rfc822Name IA5String */
+       wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - rfc822Name", pos, len);
+       os_free(name->alt_email);
+       name->alt_email = (char *)os_zalloc(len + 1);
+       if (name->alt_email == NULL)
+               return -1;
+       os_memcpy(name->alt_email, pos, len);
+       if (os_strlen(name->alt_email) != len) {
+               wpa_printf(MSG_INFO, "X509: Reject certificate with "
+                          "embedded NUL byte in rfc822Name (%s[NUL])",
+                          name->alt_email);
+               os_free(name->alt_email);
+               name->alt_email = NULL;
+               return -1;
+       }
+       return 0;
+}
+
+
+static int x509_parse_alt_name_dns(struct x509_name *name,
+                                  const u8 *pos, size_t len)
+{
+       /* dNSName IA5String */
+       wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - dNSName", pos, len);
+       os_free(name->dns);
+       name->dns = (char *)os_zalloc(len + 1);
+       if (name->dns == NULL)
+               return -1;
+       os_memcpy(name->dns, pos, len);
+       if (os_strlen(name->dns) != len) {
+               wpa_printf(MSG_INFO, "X509: Reject certificate with "
+                          "embedded NUL byte in dNSName (%s[NUL])",
+                          name->dns);
+               os_free(name->dns);
+               name->dns = NULL;
+               return -1;
+       }
+       return 0;
+}
+
+
+static int x509_parse_alt_name_uri(struct x509_name *name,
+                                  const u8 *pos, size_t len)
+{
+       /* uniformResourceIdentifier IA5String */
+       wpa_hexdump_ascii(MSG_MSGDUMP,
+                         "X509: altName - uniformResourceIdentifier",
+                         pos, len);
+       os_free(name->uri);
+       name->uri = (char *)os_zalloc(len + 1);
+       if (name->uri == NULL)
+               return -1;
+       os_memcpy(name->uri, pos, len);
+       if (os_strlen(name->uri) != len) {
+               wpa_printf(MSG_INFO, "X509: Reject certificate with "
+                          "embedded NUL byte in uniformResourceIdentifier "
+                          "(%s[NUL])", name->uri);
+               os_free(name->uri);
+               name->uri = NULL;
+               return -1;
+       }
+       return 0;
+}
+
+
+static int x509_parse_alt_name_ip(struct x509_name *name,
+                                      const u8 *pos, size_t len)
+{
+       /* iPAddress OCTET STRING */
+       wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len);
+       os_free(name->ip);
+       name->ip = os_malloc(len);
+       if (name->ip == NULL)
+               return -1;
+       os_memcpy(name->ip, pos, len);
+       name->ip_len = len;
+       return 0;
+}
+
+
+static int x509_parse_alt_name_rid(struct x509_name *name,
+                                  const u8 *pos, size_t len)
+{
+       char buf[80];
+
+       /* registeredID OBJECT IDENTIFIER */
+       if (asn1_parse_oid(pos, len, &name->rid) < 0)
+               return -1;
+
+       asn1_oid_to_str(&name->rid, buf, sizeof(buf));
+       wpa_printf(MSG_DEBUG, "X509: altName - registeredID: %s", buf);
+
+       return 0;
+}
+
+
+static int x509_parse_ext_alt_name(struct x509_name *name,
+                                  const u8 *pos, size_t len)
+{
+       struct asn1_hdr hdr;
+       const u8 *p, *end;
+
+       /*
+        * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+        *
+        * GeneralName ::= CHOICE {
+        *     otherName                       [0]     OtherName,
+        *     rfc822Name                      [1]     IA5String,
+        *     dNSName                         [2]     IA5String,
+        *     x400Address                     [3]     ORAddress,
+        *     directoryName                   [4]     Name,
+        *     ediPartyName                    [5]     EDIPartyName,
+        *     uniformResourceIdentifier       [6]     IA5String,
+        *     iPAddress                       [7]     OCTET STRING,
+        *     registeredID                    [8]     OBJECT IDENTIFIER }
+        *
+        * OtherName ::= SEQUENCE {
+        *     type-id    OBJECT IDENTIFIER,
+        *     value      [0] EXPLICIT ANY DEFINED BY type-id }
+        *
+        * EDIPartyName ::= SEQUENCE {
+        *     nameAssigner            [0]     DirectoryString OPTIONAL,
+        *     partyName               [1]     DirectoryString }
+        */
+
+       for (p = pos, end = pos + len; p < end; p = hdr.payload + hdr.length) {
+               int res;
+
+               if (asn1_get_next(p, end - p, &hdr) < 0) {
+                       wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+                                  "SubjectAltName item");
+                       return -1;
+               }
+
+               if (hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC)
+                       continue;
+
+               switch (hdr.tag) {
+               case 1:
+                       res = x509_parse_alt_name_rfc8222(name, hdr.payload,
+                                                         hdr.length);
+                       break;
+               case 2:
+                       res = x509_parse_alt_name_dns(name, hdr.payload,
+                                                     hdr.length);
+                       break;
+               case 6:
+                       res = x509_parse_alt_name_uri(name, hdr.payload,
+                                                     hdr.length);
+                       break;
+               case 7:
+                       res = x509_parse_alt_name_ip(name, hdr.payload,
+                                                    hdr.length);
+                       break;
+               case 8:
+                       res = x509_parse_alt_name_rid(name, hdr.payload,
+                                                     hdr.length);
+                       break;
+               case 0: /* TODO: otherName */
+               case 3: /* TODO: x500Address */
+               case 4: /* TODO: directoryName */
+               case 5: /* TODO: ediPartyName */
+               default:
+                       res = 0;
+                       break;
+               }
+               if (res < 0)
+                       return res;
+       }
+
+       return 0;
+}
+
+
+static int x509_parse_ext_subject_alt_name(struct x509_certificate *cert,
+                                          const u8 *pos, size_t len)
+{
+       struct asn1_hdr hdr;
+
+       /* SubjectAltName ::= GeneralNames */
+
+       if (asn1_get_next(pos, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in "
+                          "SubjectAltName; found %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "X509: SubjectAltName");
+       cert->extensions_present |= X509_EXT_SUBJECT_ALT_NAME;
+
+       if (hdr.length == 0)
+               return 0;
+
+       return x509_parse_ext_alt_name(&cert->subject, hdr.payload,
+                                      hdr.length);
+}
+
+
+static int x509_parse_ext_issuer_alt_name(struct x509_certificate *cert,
+                                         const u8 *pos, size_t len)
+{
+       struct asn1_hdr hdr;
+
+       /* IssuerAltName ::= GeneralNames */
+
+       if (asn1_get_next(pos, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in "
+                          "IssuerAltName; found %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "X509: IssuerAltName");
+       cert->extensions_present |= X509_EXT_ISSUER_ALT_NAME;
+
+       if (hdr.length == 0)
+               return 0;
+
+       return x509_parse_ext_alt_name(&cert->issuer, hdr.payload,
+                                      hdr.length);
+}
+
+
+static int x509_parse_extension_data(struct x509_certificate *cert,
+                                    struct asn1_oid *oid,
+                                    const u8 *pos, size_t len)
+{
+       if (!x509_id_ce_oid(oid))
+               return 1;
+
+       /* TODO: add other extensions required by RFC 3280, Ch 4.2:
+        * certificate policies (section 4.2.1.5)
+        * name constraints (section 4.2.1.11)
+        * policy constraints (section 4.2.1.12)
+        * extended key usage (section 4.2.1.13)
+        * inhibit any-policy (section 4.2.1.15)
+        */
+       switch (oid->oid[3]) {
+       case 15: /* id-ce-keyUsage */
+               return x509_parse_ext_key_usage(cert, pos, len);
+       case 17: /* id-ce-subjectAltName */
+               return x509_parse_ext_subject_alt_name(cert, pos, len);
+       case 18: /* id-ce-issuerAltName */
+               return x509_parse_ext_issuer_alt_name(cert, pos, len);
+       case 19: /* id-ce-basicConstraints */
+               return x509_parse_ext_basic_constraints(cert, pos, len);
+       default:
+               return 1;
+       }
+}
+
+
+static int x509_parse_extension(struct x509_certificate *cert,
+                               const u8 *pos, size_t len, const u8 **next)
+{
+       const u8 *end;
+       struct asn1_hdr hdr;
+       struct asn1_oid oid;
+       int critical_ext = 0, res;
+       char buf[80];
+
+       /*
+        * Extension  ::=  SEQUENCE  {
+        *     extnID      OBJECT IDENTIFIER,
+        *     critical    BOOLEAN DEFAULT FALSE,
+        *     extnValue   OCTET STRING
+        * }
+        */
+
+       if (asn1_get_next(pos, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in "
+                          "Extensions: class %d tag 0x%x; expected SEQUENCE",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+       pos = hdr.payload;
+       *next = end = pos + hdr.length;
+
+       if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) {
+               wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data for "
+                          "Extension (expected OID)");
+               return -1;
+       }
+
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           (hdr.tag != ASN1_TAG_BOOLEAN &&
+            hdr.tag != ASN1_TAG_OCTETSTRING)) {
+               wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in "
+                          "Extensions: class %d tag 0x%x; expected BOOLEAN "
+                          "or OCTET STRING", hdr.class, hdr.tag);
+               return -1;
+       }
+
+       if (hdr.tag == ASN1_TAG_BOOLEAN) {
+               if (hdr.length != 1) {
+                       wpa_printf(MSG_DEBUG, "X509: Unexpected "
+                                  "Boolean length (%u)", hdr.length);
+                       return -1;
+               }
+               critical_ext = hdr.payload[0];
+               pos = hdr.payload;
+               if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+                   (hdr.class != ASN1_CLASS_UNIVERSAL &&
+                    hdr.class != ASN1_CLASS_PRIVATE) ||
+                   hdr.tag != ASN1_TAG_OCTETSTRING) {
+                       wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header "
+                                  "in Extensions: class %d tag 0x%x; "
+                                  "expected OCTET STRING",
+                                  hdr.class, hdr.tag);
+                       return -1;
+               }
+       }
+
+       asn1_oid_to_str(&oid, buf, sizeof(buf));
+       wpa_printf(MSG_DEBUG, "X509: Extension: extnID=%s critical=%d",
+                  buf, critical_ext);
+       wpa_hexdump(MSG_MSGDUMP, "X509: extnValue", hdr.payload, hdr.length);
+
+       res = x509_parse_extension_data(cert, &oid, hdr.payload, hdr.length);
+       if (res < 0)
+               return res;
+       if (res == 1 && critical_ext) {
+               wpa_printf(MSG_INFO, "X509: Unknown critical extension %s",
+                          buf);
+               //return -1;  //for wpa2 certification , commenout , ignore the error 
+       }
+
+       return 0;
+}
+
+
+static int x509_parse_extensions(struct x509_certificate *cert,
+                                const u8 *pos, size_t len)
+{
+       const u8 *end;
+       struct asn1_hdr hdr;
+
+       /* Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension */
+
+       if (asn1_get_next(pos, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data "
+                          "for Extensions: class %d tag 0x%x; "
+                          "expected SEQUENCE", hdr.class, hdr.tag);
+               return -1;
+       }
+
+       pos = hdr.payload;
+       end = pos + hdr.length;
+
+       while (pos < end) {
+               if (x509_parse_extension(cert, pos, end - pos, &pos)
+                   < 0)
+                       return -1;
+       }
+
+       return 0;
+}
+
+
+static int x509_parse_tbs_certificate(const u8 *buf, size_t len,
+                                     struct x509_certificate *cert,
+                                     const u8 **next)
+{
+       struct asn1_hdr hdr;
+       const u8 *pos, *end;
+       size_t left;
+       char sbuf[128];
+       unsigned long value;
+
+       /* tbsCertificate TBSCertificate ::= SEQUENCE */
+       if (asn1_get_next(buf, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: tbsCertificate did not start "
+                          "with a valid SEQUENCE - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+       pos = hdr.payload;
+       end = *next = pos + hdr.length;
+
+       /*
+        * version [0]  EXPLICIT Version DEFAULT v1
+        * Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
+        */
+       if (asn1_get_next(pos, end - pos, &hdr) < 0)
+               return -1;
+       pos = hdr.payload;
+
+       if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC) {
+               if (asn1_get_next(pos, end - pos, &hdr) < 0)
+                       return -1;
+
+               if (hdr.class != ASN1_CLASS_UNIVERSAL ||
+                   hdr.tag != ASN1_TAG_INTEGER) {
+                       wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for "
+                                  "version field - found class %d tag 0x%x",
+                                  hdr.class, hdr.tag);
+                       return -1;
+               }
+               if (hdr.length != 1) {
+                       wpa_printf(MSG_DEBUG, "X509: Unexpected version field "
+                                  "length %u (expected 1)", hdr.length);
+                       return -1;
+               }
+               pos = hdr.payload;
+               left = hdr.length;
+               value = 0;
+               while (left) {
+                       value <<= 8;
+                       value |= *pos++;
+                       left--;
+               }
+
+               cert->version = value;
+               if (cert->version != X509_CERT_V1 &&
+                   cert->version != X509_CERT_V2 &&
+                   cert->version != X509_CERT_V3) {
+                       wpa_printf(MSG_DEBUG, "X509: Unsupported version %d",
+                                  cert->version + 1);
+                       return -1;
+               }
+
+               if (asn1_get_next(pos, end - pos, &hdr) < 0)
+                       return -1;
+       } else
+               cert->version = X509_CERT_V1;
+       wpa_printf(MSG_DEBUG, "X509: Version X.509v%d", cert->version + 1);
+
+       /* serialNumber CertificateSerialNumber ::= INTEGER */
+       if (hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_INTEGER) {
+               wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for "
+                          "serialNumber; class=%d tag=0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+
+       pos = hdr.payload;
+       left = hdr.length;
+       while (left) {
+               cert->serial_number <<= 8;
+               cert->serial_number |= *pos++;
+               left--;
+       }
+       wpa_printf(MSG_DEBUG, "X509: serialNumber %lu", cert->serial_number);
+
+       /* signature AlgorithmIdentifier */
+       if (x509_parse_algorithm_identifier(pos, end - pos, &cert->signature,
+                                           &pos))
+               return -1;
+
+       /* issuer Name */
+       if (x509_parse_name(pos, end - pos, &cert->issuer, &pos))
+               return -1;
+       x509_name_string(&cert->issuer, sbuf, sizeof(sbuf));
+       wpa_printf(MSG_DEBUG, "X509: issuer %s", sbuf);
+
+       /* validity Validity */
+       if (x509_parse_validity(pos, end - pos, cert, &pos))
+               return -1;
+
+       /* subject Name */
+       if (x509_parse_name(pos, end - pos, &cert->subject, &pos))
+               return -1;
+       x509_name_string(&cert->subject, sbuf, sizeof(sbuf));
+       wpa_printf(MSG_DEBUG, "X509: subject %s", sbuf);
+
+       /* subjectPublicKeyInfo SubjectPublicKeyInfo */
+       if (x509_parse_public_key(pos, end - pos, cert, &pos))
+               return -1;
+
+       if (pos == end)
+               return 0;
+
+       if (cert->version == X509_CERT_V1)
+               return 0;
+
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
+               wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific"
+                          " tag to parse optional tbsCertificate "
+                          "field(s); parsed class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+
+       if (hdr.tag == 1) {
+               /* issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL */
+               wpa_printf(MSG_DEBUG, "X509: issuerUniqueID");
+               /* TODO: parse UniqueIdentifier ::= BIT STRING */
+
+               if (hdr.payload + hdr.length == end)
+                       return 0;
+
+               if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+                   hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
+                       wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific"
+                                  " tag to parse optional tbsCertificate "
+                                  "field(s); parsed class %d tag 0x%x",
+                                  hdr.class, hdr.tag);
+                       return -1;
+               }
+       }
+
+       if (hdr.tag == 2) {
+               /* subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL */
+               wpa_printf(MSG_DEBUG, "X509: subjectUniqueID");
+               /* TODO: parse UniqueIdentifier ::= BIT STRING */
+
+               if (hdr.payload + hdr.length == end)
+                       return 0;
+
+               if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+                   hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
+                       wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific"
+                                  " tag to parse optional tbsCertificate "
+                                  "field(s); parsed class %d tag 0x%x",
+                                  hdr.class, hdr.tag);
+                       return -1;
+               }
+       }
+
+       if (hdr.tag != 3) {
+               wpa_printf(MSG_DEBUG, "X509: Ignored unexpected "
+                          "Context-Specific tag %d in optional "
+                          "tbsCertificate fields", hdr.tag);
+               return 0;
+       }
+
+       /* extensions      [3]  EXPLICIT Extensions OPTIONAL */
+
+       if (cert->version != X509_CERT_V3) {
+               wpa_printf(MSG_DEBUG, "X509: X.509%d certificate and "
+                          "Extensions data which are only allowed for "
+                          "version 3", cert->version + 1);
+               return -1;
+       }
+
+       if (x509_parse_extensions(cert, hdr.payload, hdr.length) < 0)
+               return -1;
+
+       pos = hdr.payload + hdr.length;
+       if (pos < end) {
+               wpa_hexdump(MSG_DEBUG,
+                           "X509: Ignored extra tbsCertificate data",
+                           pos, end - pos);
+       }
+
+       return 0;
+}
+
+
+static int x509_rsadsi_oid(struct asn1_oid *oid)
+{
+       return oid->len >= 4 &&
+               oid->oid[0] == 1 /* iso */ &&
+               oid->oid[1] == 2 /* member-body */ &&
+               oid->oid[2] == 840 /* us */ &&
+               oid->oid[3] == 113549 /* rsadsi */;
+}
+
+
+static int x509_pkcs_oid(struct asn1_oid *oid)
+{
+       return oid->len >= 5 &&
+               x509_rsadsi_oid(oid) &&
+               oid->oid[4] == 1 /* pkcs */;
+}
+
+
+static int x509_digest_oid(struct asn1_oid *oid)
+{
+       return oid->len >= 5 &&
+               x509_rsadsi_oid(oid) &&
+               oid->oid[4] == 2 /* digestAlgorithm */;
+}
+
+
+static int x509_sha1_oid(struct asn1_oid *oid)
+{
+       return oid->len == 6 &&
+               oid->oid[0] == 1 /* iso */ &&
+               oid->oid[1] == 3 /* identified-organization */ &&
+               oid->oid[2] == 14 /* oiw */ &&
+               oid->oid[3] == 3 /* secsig */ &&
+               oid->oid[4] == 2 /* algorithms */ &&
+               oid->oid[5] == 26 /* id-sha1 */;
+}
+
+
+static int x509_sha256_oid(struct asn1_oid *oid)
+{
+       return oid->len == 9 &&
+               oid->oid[0] == 2 /* joint-iso-itu-t */ &&
+               oid->oid[1] == 16 /* country */ &&
+               oid->oid[2] == 840 /* us */ &&
+               oid->oid[3] == 1 /* organization */ &&
+               oid->oid[4] == 101 /* gov */ &&
+               oid->oid[5] == 3 /* csor */ &&
+               oid->oid[6] == 4 /* nistAlgorithm */ &&
+               oid->oid[7] == 2 /* hashAlgs */ &&
+               oid->oid[8] == 1 /* sha256 */;
+}
+
+
+/**
+ * x509_certificate_parse - Parse a X.509 certificate in DER format
+ * @buf: Pointer to the X.509 certificate in DER format
+ * @len: Buffer length
+ * Returns: Pointer to the parsed certificate or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned certificate by calling
+ * x509_certificate_free().
+ */
+struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len)
+{
+       struct asn1_hdr hdr;
+       const u8 *pos, *end, *hash_start;
+       struct x509_certificate *cert;
+
+       cert = (struct x509_certificate *)os_zalloc(sizeof(*cert) + len);
+       if (cert == NULL)
+               return NULL;
+       os_memcpy(cert + 1, buf, len);
+       cert->cert_start = (u8 *) (cert + 1);
+       cert->cert_len = len;
+
+       pos = buf;
+       end = buf + len;
+
+       /* RFC 3280 - X.509 v3 certificate / ASN.1 DER */
+
+       /* Certificate ::= SEQUENCE */
+       if (asn1_get_next(pos, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Certificate did not start with "
+                          "a valid SEQUENCE - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               x509_certificate_free(cert);
+               return NULL;
+       }
+       pos = hdr.payload;
+
+       if (pos + hdr.length > end) {
+               x509_certificate_free(cert);
+               return NULL;
+       }
+
+       if (pos + hdr.length < end) {
+               wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER "
+                           "encoded certificate",
+                           pos + hdr.length, end - pos + hdr.length);
+               end = pos + hdr.length;
+       }
+
+       hash_start = pos;
+       cert->tbs_cert_start = cert->cert_start + (hash_start - buf);
+       if (x509_parse_tbs_certificate(pos, end - pos, cert, &pos)) {
+               x509_certificate_free(cert);
+               return NULL;
+       }
+       cert->tbs_cert_len = pos - hash_start;
+
+       /* signatureAlgorithm AlgorithmIdentifier */
+       if (x509_parse_algorithm_identifier(pos, end - pos,
+                                           &cert->signature_alg, &pos)) {
+               x509_certificate_free(cert);
+               return NULL;
+       }
+
+       /* signatureValue BIT STRING */
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_BITSTRING) {
+               wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING "
+                          "(signatureValue) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               x509_certificate_free(cert);
+               return NULL;
+       }
+       if (hdr.length < 1) {
+               x509_certificate_free(cert);
+               return NULL;
+       }
+       pos = hdr.payload;
+       if (*pos) {
+               wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits",
+                          *pos);
+               /* PKCS #1 v1.5 10.2.1:
+                * It is an error if the length in bits of the signature S is
+                * not a multiple of eight.
+                */
+               x509_certificate_free(cert);
+               return NULL;
+       }
+       os_free(cert->sign_value);
+       cert->sign_value = os_malloc(hdr.length - 1);
+       if (cert->sign_value == NULL) {
+               wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for "
+                          "signatureValue");
+               x509_certificate_free(cert);
+               return NULL;
+       }
+       os_memcpy(cert->sign_value, pos + 1, hdr.length - 1);
+       cert->sign_value_len = hdr.length - 1;
+       wpa_hexdump(MSG_MSGDUMP, "X509: signature",
+                   cert->sign_value, cert->sign_value_len);
+
+       return cert;
+}
+
+
+/**
+ * x509_certificate_check_signature - Verify certificate signature
+ * @issuer: Issuer certificate
+ * @cert: Certificate to be verified
+ * Returns: 0 if cert has a valid signature that was signed by the issuer,
+ * -1 if not
+ */
+int x509_certificate_check_signature(struct x509_certificate *issuer,
+                                    struct x509_certificate *cert)
+{
+       struct crypto_public_key *pk;
+       u8 *data;
+       const u8 *pos, *end, *next, *da_end;
+       size_t data_len;
+       struct asn1_hdr hdr;
+       struct asn1_oid oid;
+       u8 hash[32];
+       size_t hash_len;
+
+       if (!x509_pkcs_oid(&cert->signature.oid) ||
+           cert->signature.oid.len != 7 ||
+           cert->signature.oid.oid[5] != 1 /* pkcs-1 */) {
+               wpa_printf(MSG_DEBUG, "X509: Unrecognized signature "
+                          "algorithm");
+               return -1;
+       }
+
+       pk = crypto_public_key_import(issuer->public_key,
+                                     issuer->public_key_len);
+       if (pk == NULL)
+               return -1;
+
+       data_len = cert->sign_value_len;
+       data = os_malloc(data_len);
+       if (data == NULL) {
+               crypto_public_key_free(pk);
+               return -1;
+       }
+
+       if (crypto_public_key_decrypt_pkcs1(pk, cert->sign_value,
+                                           cert->sign_value_len, data,
+                                           &data_len) < 0) {
+               wpa_printf(MSG_DEBUG, "X509: Failed to decrypt signature");
+               crypto_public_key_free(pk);
+               os_free(data);
+               return -1;
+       }
+       crypto_public_key_free(pk);
+
+       wpa_hexdump(MSG_MSGDUMP, "X509: Signature data D", data, data_len);
+
+       /*
+        * PKCS #1 v1.5, 10.1.2:
+        *
+        * DigestInfo ::= SEQUENCE {
+        *     digestAlgorithm DigestAlgorithmIdentifier,
+        *     digest Digest
+        * }
+        *
+        * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
+        *
+        * Digest ::= OCTET STRING
+        *
+        */
+       if (asn1_get_next(data, data_len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+                          "(DigestInfo) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               os_free(data);
+               return -1;
+       }
+
+       pos = hdr.payload;
+       end = pos + hdr.length;
+
+       /*
+        * X.509:
+        * AlgorithmIdentifier ::= SEQUENCE {
+        *     algorithm            OBJECT IDENTIFIER,
+        *     parameters           ANY DEFINED BY algorithm OPTIONAL
+        * }
+        */
+
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+                          "(AlgorithmIdentifier) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               os_free(data);
+               return -1;
+       }
+       da_end = hdr.payload + hdr.length;
+
+       if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) {
+               wpa_printf(MSG_DEBUG, "X509: Failed to parse digestAlgorithm");
+               os_free(data);
+               return -1;
+       }
+
+       if (x509_sha1_oid(&oid)) {
+               if (cert->signature.oid.oid[6] !=
+                   5 /* sha-1WithRSAEncryption */) {
+                       wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA1 "
+                                  "does not match with certificate "
+                                  "signatureAlgorithm (%lu)",
+                                  cert->signature.oid.oid[6]);
+                       os_free(data);
+                       return -1;
+               }
+               goto skip_digest_oid;
+       }
+
+       if (x509_sha256_oid(&oid)) {
+               if (cert->signature.oid.oid[6] !=
+                   11 /* sha2561WithRSAEncryption */) {
+                       wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA256 "
+                                  "does not match with certificate "
+                                  "signatureAlgorithm (%lu)",
+                                  cert->signature.oid.oid[6]);
+                       os_free(data);
+                       return -1;
+               }
+               goto skip_digest_oid;
+       }
+
+       if (!x509_digest_oid(&oid)) {
+               wpa_printf(MSG_DEBUG, "X509: Unrecognized digestAlgorithm");
+               os_free(data);
+               return -1;
+       }
+       switch (oid.oid[5]) {
+       case 5: /* md5 */
+               if (cert->signature.oid.oid[6] != 4 /* md5WithRSAEncryption */)
+               {
+                       wpa_printf(MSG_DEBUG, "X509: digestAlgorithm MD5 does "
+                                  "not match with certificate "
+                                  "signatureAlgorithm (%lu)",
+                                  cert->signature.oid.oid[6]);
+                       os_free(data);
+                       return -1;
+               }
+               break;
+       case 2: /* md2 */
+       case 4: /* md4 */
+       default:
+               wpa_printf(MSG_DEBUG, "X509: Unsupported digestAlgorithm "
+                          "(%lu)", oid.oid[5]);
+               os_free(data);
+               return -1;
+       }
+
+skip_digest_oid:
+       /* Digest ::= OCTET STRING */
+       pos = da_end;
+       end = data + data_len;
+
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_OCTETSTRING) {
+               wpa_printf(MSG_DEBUG, "X509: Expected OCTETSTRING "
+                          "(Digest) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               os_free(data);
+               return -1;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "X509: Decrypted Digest",
+                   hdr.payload, hdr.length);
+
+       switch (cert->signature.oid.oid[6]) {
+       case 4: /* md5WithRSAEncryption */
+               md5_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
+                          hash);
+               hash_len = 16;
+               wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (MD5)",
+                           hash, hash_len);
+               break;
+       case 5: /* sha-1WithRSAEncryption */
+               sha1_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
+                           hash);
+               hash_len = 20;
+               wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA1)",
+                           hash, hash_len);
+               break;
+       case 11: /* sha256WithRSAEncryption */
+               if (wpa2_crypto_funcs.sha256_vector) {
+                       wpa2_crypto_funcs.sha256_vector(1, &cert->tbs_cert_start, (int *)&cert->tbs_cert_len,
+                                                       hash);
+               } else {
+                       wpa_printf(MSG_ERROR, "Fail to register sha256 vector function!\r\n");
+                       return -1;
+               }
+               hash_len = 32;
+               wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)",
+                           hash, hash_len);
+               break;
+       case 2: /* md2WithRSAEncryption */
+       case 12: /* sha384WithRSAEncryption */
+       case 13: /* sha512WithRSAEncryption */
+       default:
+               wpa_printf(MSG_INFO, "X509: Unsupported certificate signature "
+                          "algorithm (%lu)", cert->signature.oid.oid[6]);
+               os_free(data);
+               return -1;
+       }
+
+       if (hdr.length != hash_len ||
+           os_memcmp(hdr.payload, hash, hdr.length) != 0) {
+               wpa_printf(MSG_INFO, "X509: Certificate Digest does not match "
+                          "with calculated tbsCertificate hash");
+               os_free(data);
+               return -1;
+       }
+
+       os_free(data);
+
+       wpa_printf(MSG_DEBUG, "X509: Certificate Digest matches with "
+                  "calculated tbsCertificate hash");
+
+       return 0;
+}
+
+
+static int x509_valid_issuer(const struct x509_certificate *cert)
+{
+       if ((cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS) &&
+           !cert->ca) {
+               wpa_printf(MSG_DEBUG, "X509: Non-CA certificate used as an "
+                          "issuer");
+               return -1;
+       }
+
+       if (cert->version == X509_CERT_V3 &&
+           !(cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS)) {
+               wpa_printf(MSG_DEBUG, "X509: v3 CA certificate did not "
+                          "include BasicConstraints extension");
+               return -1;
+       }
+
+       if ((cert->extensions_present & X509_EXT_KEY_USAGE) &&
+           !(cert->key_usage & X509_KEY_USAGE_KEY_CERT_SIGN)) {
+               wpa_printf(MSG_DEBUG, "X509: Issuer certificate did not have "
+                          "keyCertSign bit in Key Usage");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * x509_certificate_chain_validate - Validate X.509 certificate chain
+ * @trusted: List of trusted certificates
+ * @chain: Certificate chain to be validated (first chain must be issued by
+ * signed by the second certificate in the chain and so on)
+ * @reason: Buffer for returning failure reason (X509_VALIDATE_*)
+ * Returns: 0 if chain is valid, -1 if not
+ */
+int x509_certificate_chain_validate(struct x509_certificate *trusted,
+                                   struct x509_certificate *chain,
+                                   int *reason, int disable_time_checks)
+{
+       long unsigned idx;
+       int chain_trusted = 0;
+       struct x509_certificate *cert, *trust;
+       char buf[128];
+       struct os_time now;
+
+       *reason = X509_VALIDATE_OK;
+
+       wpa_printf(MSG_DEBUG, "X509: Validate certificate chain");
+       os_get_time(&now);
+
+       for (cert = chain, idx = 0; cert; cert = cert->next, idx++) {
+               x509_name_string(&cert->subject, buf, sizeof(buf)); 
+               wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf);
+
+               if (chain_trusted)
+                       continue;
+
+               if (!disable_time_checks &&
+                   ((unsigned long) now.sec <
+                    (unsigned long) cert->not_before ||
+                    (unsigned long) now.sec >
+                    (unsigned long) cert->not_after)) {
+                       wpa_printf(MSG_INFO, "X509: Certificate not valid "
+                                  "(now=%lu not_before=%lu not_after=%lu)",
+                                  now.sec, cert->not_before, cert->not_after);
+                       *reason = X509_VALIDATE_CERTIFICATE_EXPIRED;
+                       return -1;
+               }
+
+               if (cert->next) {
+                       if (x509_name_compare(&cert->issuer,
+                                             &cert->next->subject) != 0) {
+                               wpa_printf(MSG_DEBUG, "X509: Certificate "
+                                          "chain issuer name mismatch");
+                               x509_name_string(&cert->issuer, buf,
+                                                sizeof(buf)); 
+                               wpa_printf(MSG_DEBUG, "X509: cert issuer: %s",
+                                          buf);
+                               x509_name_string(&cert->next->subject, buf,
+                                                sizeof(buf)); 
+                               wpa_printf(MSG_DEBUG, "X509: next cert "
+                                          "subject: %s", buf);
+                               *reason = X509_VALIDATE_CERTIFICATE_UNKNOWN;
+                               return -1;
+                       }
+
+                       if (x509_valid_issuer(cert->next) < 0) {
+                               *reason = X509_VALIDATE_BAD_CERTIFICATE;
+                               return -1;
+                       }
+
+                       if ((cert->next->extensions_present &
+                            X509_EXT_PATH_LEN_CONSTRAINT) &&
+                           idx > cert->next->path_len_constraint) {
+                               wpa_printf(MSG_DEBUG, "X509: pathLenConstraint"
+                                          " not met (idx=%lu issuer "
+                                          "pathLenConstraint=%lu)", idx,
+                                          cert->next->path_len_constraint);
+                               *reason = X509_VALIDATE_BAD_CERTIFICATE;
+                               return -1;
+                       }
+
+                       if (x509_certificate_check_signature(cert->next, cert)
+                           < 0) {
+                               wpa_printf(MSG_DEBUG, "X509: Invalid "
+                                          "certificate signature within "
+                                          "chain");
+                               *reason = X509_VALIDATE_BAD_CERTIFICATE;
+                               return -1;
+                       }
+               }
+
+               for (trust = trusted; trust; trust = trust->next) {
+                       if (x509_name_compare(&cert->issuer, &trust->subject)
+                           == 0)
+                               break;
+               }
+
+               if (trust) {
+                       wpa_printf(MSG_DEBUG, "X509: Found issuer from the "
+                                  "list of trusted certificates");
+                       if (x509_valid_issuer(trust) < 0) {
+                               *reason = X509_VALIDATE_BAD_CERTIFICATE;
+                               return -1;
+                       }
+
+                       if (x509_certificate_check_signature(trust, cert) < 0)
+                       {
+                               wpa_printf(MSG_DEBUG, "X509: Invalid "
+                                          "certificate signature");
+                               *reason = X509_VALIDATE_BAD_CERTIFICATE;
+                               return -1;
+                       }
+
+                       wpa_printf(MSG_DEBUG, "X509: Trusted certificate "
+                                  "found to complete the chain");
+                       chain_trusted = 1;
+               }
+       }
+
+       if (!chain_trusted) {
+               wpa_printf(MSG_DEBUG, "X509: Did not find any of the issuers "
+                          "from the list of trusted certificates");
+               if (trusted) {
+                       *reason = X509_VALIDATE_UNKNOWN_CA;
+                       return -1;
+               }
+               wpa_printf(MSG_DEBUG, "X509: Certificate chain validation "
+                          "disabled - ignore unknown CA issue");
+       }
+
+       wpa_printf(MSG_DEBUG, "X509: Certificate chain valid");
+
+       return 0;
+}
+
+
+/**
+ * x509_certificate_get_subject - Get a certificate based on Subject name
+ * @chain: Certificate chain to search through
+ * @name: Subject name to search for
+ * Returns: Pointer to the certificate with the given Subject name or
+ * %NULL on failure
+ */
+struct x509_certificate *
+x509_certificate_get_subject(struct x509_certificate *chain,
+                            struct x509_name *name)
+{
+       struct x509_certificate *cert;
+
+       for (cert = chain; cert; cert = cert->next) {
+               if (x509_name_compare(&cert->subject, name) == 0)
+                       return cert;
+       }
+       return NULL;
+}
+
+
+/**
+ * x509_certificate_self_signed - Is the certificate self-signed?
+ * @cert: Certificate
+ * Returns: 1 if certificate is self-signed, 0 if not
+ */
+int x509_certificate_self_signed(struct x509_certificate *cert)
+{
+       return x509_name_compare(&cert->issuer, &cert->subject) == 0;
+}
diff --git a/components/wpa_supplicant/src/wpa2/utils/base64.c b/components/wpa_supplicant/src/wpa2/utils/base64.c
new file mode 100644 (file)
index 0000000..0340c39
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Base64 encoding/decoding (RFC1341)
+ * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "os.h"
+#include "wpa2/utils/base64.h"
+
+static const unsigned char base64_table[65] =
+       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * base64_encode - Base64 encode
+ * @src: Data to be encoded
+ * @len: Length of the data to be encoded
+ * @out_len: Pointer to output length variable, or %NULL if not used
+ * Returns: Allocated buffer of out_len bytes of encoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer. Returned buffer is
+ * nul terminated to make it easier to use as a C string. The nul terminator is
+ * not included in out_len.
+ */
+unsigned char * base64_encode(const unsigned char *src, size_t len,
+                             size_t *out_len)
+{
+       unsigned char *out, *pos;
+       const unsigned char *end, *in;
+       size_t olen;
+       int line_len;
+
+       olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
+       olen += olen / 72; /* line feeds */
+       olen++; /* nul termination */
+       if (olen < len)
+               return NULL; /* integer overflow */
+       out = os_malloc(olen);
+       if (out == NULL)
+               return NULL;
+
+       end = src + len;
+       in = src;
+       pos = out;
+       line_len = 0;
+       while (end - in >= 3) {
+               *pos++ = base64_table[in[0] >> 2];
+               *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
+               *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
+               *pos++ = base64_table[in[2] & 0x3f];
+               in += 3;
+               line_len += 4;
+               if (line_len >= 72) {
+                       *pos++ = '\n';
+                       line_len = 0;
+               }
+       }
+
+       if (end - in) {
+               *pos++ = base64_table[in[0] >> 2];
+               if (end - in == 1) {
+                       *pos++ = base64_table[(in[0] & 0x03) << 4];
+                       *pos++ = '=';
+               } else {
+                       *pos++ = base64_table[((in[0] & 0x03) << 4) |
+                                             (in[1] >> 4)];
+                       *pos++ = base64_table[(in[1] & 0x0f) << 2];
+               }
+               *pos++ = '=';
+               line_len += 4;
+       }
+
+       if (line_len)
+               *pos++ = '\n';
+
+       *pos = '\0';
+       if (out_len)
+               *out_len = pos - out;
+       return out;
+}
+
+
+/**
+ * base64_decode - Base64 decode
+ * @src: Data to be decoded
+ * @len: Length of the data to be decoded
+ * @out_len: Pointer to output length variable
+ * Returns: Allocated buffer of out_len bytes of decoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer.
+ */
+unsigned char * base64_decode(const unsigned char *src, size_t len,
+                             size_t *out_len)
+{
+       unsigned char dtable[256], *out, *pos, block[4], tmp;
+       size_t i, count, olen;
+       int pad = 0;
+
+       os_memset(dtable, 0x80, 256);
+       for (i = 0; i < sizeof(base64_table) - 1; i++)
+               dtable[base64_table[i]] = (unsigned char) i;
+       dtable['='] = 0;
+
+       count = 0;
+       for (i = 0; i < len; i++) {
+               if (dtable[src[i]] != 0x80)
+                       count++;
+       }
+
+       if (count == 0 || count % 4)
+               return NULL;
+
+       olen = count / 4 * 3;
+       pos = out = os_malloc(olen);
+       if (out == NULL)
+               return NULL;
+
+       count = 0;
+       for (i = 0; i < len; i++) {
+               tmp = dtable[src[i]];
+               if (tmp == 0x80)
+                       continue;
+
+               if (src[i] == '=')
+                       pad++;
+               block[count] = tmp;
+               count++;
+               if (count == 4) {
+                       *pos++ = (block[0] << 2) | (block[1] >> 4);
+                       *pos++ = (block[1] << 4) | (block[2] >> 2);
+                       *pos++ = (block[2] << 6) | block[3];
+                       count = 0;
+                       if (pad) {
+                               if (pad == 1)
+                                       pos--;
+                               else if (pad == 2)
+                                       pos -= 2;
+                               else {
+                                       /* Invalid padding */
+                                       os_free(out);
+                                       return NULL;
+                               }
+                               break;
+                       }
+               }
+       }
+
+       *out_len = pos - out;
+       return out;
+}
diff --git a/components/wpa_supplicant/src/wpa2/utils/ext_password.c b/components/wpa_supplicant/src/wpa2/utils/ext_password.c
new file mode 100644 (file)
index 0000000..3989f94
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * External password backend
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wpa2/utils/ext_password_i.h"
+
+#ifdef CONFIG_EXT_PASSWORD_TEST
+extern struct ext_password_backend ext_password_test;
+#endif /* CONFIG_EXT_PASSWORD_TEST */
+
+#ifdef CONFIG_EXT_PASSWORD
+static const struct ext_password_backend *backends[] = {
+#ifdef CONFIG_EXT_PASSWORD_TEST
+       &ext_password_test,
+#endif /* CONFIG_EXT_PASSWORD_TEST */
+       NULL
+};
+#endif
+
+struct ext_password_data {
+       const struct ext_password_backend *backend;
+       void *priv;
+};
+
+#ifdef CONFIG_EXT_PASSWORD
+struct ext_password_data * ext_password_init(const char *backend,
+                                            const char *params)
+{
+       struct ext_password_data *data;
+       int i;
+
+       data = (struct ext_password_data *)os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+
+       for (i = 0; backends[i]; i++) {
+               if (os_strcmp(backends[i]->name, backend) == 0) {
+                       data->backend = backends[i];
+                       break;
+               }
+       }
+
+       if (!data->backend) {
+               os_free(data);
+               return NULL;
+       }
+
+       data->priv = data->backend->init(params);
+       if (data->priv == NULL) {
+               os_free(data);
+               return NULL;
+       }
+
+       return data;
+}
+
+
+void ext_password_deinit(struct ext_password_data *data)
+{
+       if (data && data->backend && data->priv)
+               data->backend->deinit(data->priv);
+       os_free(data);
+}
+
+
+struct wpabuf * ext_password_get(struct ext_password_data *data,
+                                const char *name)
+{
+       if (data == NULL)
+               return NULL;
+       return data->backend->get(data->priv, name);
+}
+#endif /* CONFIG_EXT_PASSWORD */
+
+struct wpabuf * ext_password_alloc(size_t len)
+{
+       struct wpabuf *buf;
+
+       buf = wpabuf_alloc(len);
+       if (buf == NULL)
+               return NULL;
+
+#ifdef __linux__
+       if (mlock(wpabuf_head(buf), wpabuf_len(buf)) < 0) {
+               wpa_printf(MSG_ERROR, "EXT PW: mlock failed: %s",
+                          strerror(errno));
+       }
+#endif /* __linux__ */
+
+       return buf;
+}
+
+#ifdef CONFIG_EXT_PASSWORD
+void ext_password_free(struct wpabuf *pw)
+{
+       if (pw == NULL)
+               return;
+       os_memset(wpabuf_mhead(pw), 0, wpabuf_len(pw));
+#ifdef __linux__
+       if (munlock(wpabuf_head(pw), wpabuf_len(pw)) < 0) {
+               wpa_printf(MSG_ERROR, "EXT PW: munlock failed: %s",
+                          strerror(errno));
+       }
+#endif /* __linux__ */
+       wpabuf_free(pw);
+}
+#endif /* CONFIG_EXT_PASSWORD */
diff --git a/components/wpa_supplicant/src/wps/eap_common.c b/components/wpa_supplicant/src/wps/eap_common.c
new file mode 100644 (file)
index 0000000..640bd5a
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * EAP common peer/server definitions
+ * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wpa2/eap_peer/eap_defs.h"
+#include "wpa2/eap_peer/eap_common.h"
+
+/**
+ * eap_hdr_len_valid - Validate EAP header length field
+ * @msg: EAP frame (starting with EAP header)
+ * @min_payload: Minimum payload length needed
+ * Returns: 1 for valid header, 0 for invalid
+ *
+ * This is a helper function that does minimal validation of EAP messages. The
+ * length field is verified to be large enough to include the header and not
+ * too large to go beyond the end of the buffer.
+ */
+int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload)
+{
+       const struct eap_hdr *hdr;
+       size_t len;
+
+       if (msg == NULL)
+               return 0;
+
+       hdr = wpabuf_head(msg);
+
+       if (wpabuf_len(msg) < sizeof(*hdr)) {
+               wpa_printf(MSG_INFO,  "EAP: Too short EAP frame");
+               return 0;
+       }
+
+       len = be_to_host16(hdr->length);
+       if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) {
+               wpa_printf(MSG_INFO,  "EAP: Invalid EAP length");
+               return 0;
+       }
+
+       return 1;
+}
+
+
+/**
+ * eap_hdr_validate - Validate EAP header
+ * @vendor: Expected EAP Vendor-Id (0 = IETF)
+ * @eap_type: Expected EAP type number
+ * @msg: EAP frame (starting with EAP header)
+ * @plen: Pointer to variable to contain the returned payload length
+ * Returns: Pointer to EAP payload (after type field), or %NULL on failure
+ *
+ * This is a helper function for EAP method implementations. This is usually
+ * called in the beginning of struct eap_method::process() function to verify
+ * that the received EAP request packet has a valid header. This function is
+ * able to process both legacy and expanded EAP headers and in most cases, the
+ * caller can just use the returned payload pointer (into *plen) for processing
+ * the payload regardless of whether the packet used the expanded EAP header or
+ * not.
+ */
+const u8 * eap_hdr_validate(int vendor, EapType eap_type,
+                           const struct wpabuf *msg, size_t *plen)
+{
+       const struct eap_hdr *hdr;
+       const u8 *pos;
+       size_t len;
+
+       if (!eap_hdr_len_valid(msg, 1))
+               return NULL;
+
+       hdr = wpabuf_head(msg);
+       len = be_to_host16(hdr->length);
+       pos = (const u8 *) (hdr + 1);
+
+       if (*pos == EAP_TYPE_EXPANDED) {
+               int exp_vendor;
+               u32 exp_type;
+               if (len < sizeof(*hdr) + 8) {
+                       wpa_printf(MSG_INFO,  "EAP: Invalid expanded EAP "
+                                  "length");
+                       return NULL;
+               }
+               pos++;
+               exp_vendor = WPA_GET_BE24(pos);
+               pos += 3;
+               exp_type = WPA_GET_BE32(pos);
+               pos += 4;
+               if (exp_vendor != vendor || exp_type != (u32) eap_type) {
+                       wpa_printf(MSG_INFO,  "EAP: Invalid expanded frame "
+                                  "type");
+                       return NULL;
+               }
+
+               *plen = len - sizeof(*hdr) - 8;
+               return pos;
+       } else {
+               if (vendor != EAP_VENDOR_IETF || *pos != eap_type) {
+                       wpa_printf(MSG_INFO,  "EAP: Invalid frame type");
+                       return NULL;
+               }
+               *plen = len - sizeof(*hdr) - 1;
+               return pos + 1;
+       }
+}
+
+
+/**
+ * eap_msg_alloc - Allocate a buffer for an EAP message
+ * @vendor: Vendor-Id (0 = IETF)
+ * @type: EAP type
+ * @payload_len: Payload length in bytes (data after Type)
+ * @code: Message Code (EAP_CODE_*)
+ * @identifier: Identifier
+ * Returns: Pointer to the allocated message buffer or %NULL on error
+ *
+ * This function can be used to allocate a buffer for an EAP message and fill
+ * in the EAP header. This function is automatically using expanded EAP header
+ * if the selected Vendor-Id is not IETF. In other words, most EAP methods do
+ * not need to separately select which header type to use when using this
+ * function to allocate the message buffers. The returned buffer has room for
+ * payload_len bytes and has the EAP header and Type field already filled in.
+ */
+struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
+                             u8 code, u8 identifier)
+{
+       struct wpabuf *buf;
+       struct eap_hdr *hdr;
+       size_t len;
+
+       len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) +
+               payload_len;
+       buf = wpabuf_alloc(len);
+       if (buf == NULL)
+               return NULL;
+
+       hdr = wpabuf_put(buf, sizeof(*hdr));
+       hdr->code = code;
+       hdr->identifier = identifier;
+       hdr->length = host_to_be16(len);
+
+       if (vendor == EAP_VENDOR_IETF) {
+               wpabuf_put_u8(buf, type);
+       } else {
+               wpabuf_put_u8(buf, EAP_TYPE_EXPANDED);
+               wpabuf_put_be24(buf, vendor);
+               wpabuf_put_be32(buf, type);
+       }
+
+       return buf;
+}
+
+
+/**
+ * eap_update_len - Update EAP header length
+ * @msg: EAP message from eap_msg_alloc
+ *
+ * This function updates the length field in the EAP header to match with the
+ * current length for the buffer. This allows eap_msg_alloc() to be used to
+ * allocate a larger buffer than the exact message length (e.g., if exact
+ * message length is not yet known).
+ */
+void eap_update_len(struct wpabuf *msg)
+{
+       struct eap_hdr *hdr;
+       hdr = wpabuf_mhead(msg);
+       if (wpabuf_len(msg) < sizeof(*hdr))
+               return;
+       hdr->length = host_to_be16(wpabuf_len(msg));
+}
+
+
+/**
+ * eap_get_id - Get EAP Identifier from wpabuf
+ * @msg: Buffer starting with an EAP header
+ * Returns: The Identifier field from the EAP header
+ */
+u8 eap_get_id(const struct wpabuf *msg)
+{
+       const struct eap_hdr *eap;
+
+       if (wpabuf_len(msg) < sizeof(*eap))
+               return 0;
+
+       eap = wpabuf_head(msg);
+       return eap->identifier;
+}
+
+
+/**
+ * eap_get_id - Get EAP Type from wpabuf
+ * @msg: Buffer starting with an EAP header
+ * Returns: The EAP Type after the EAP header
+ */
+EapType eap_get_type(const struct wpabuf *msg)
+{
+       if (wpabuf_len(msg) < sizeof(struct eap_hdr) + 1)
+               return EAP_TYPE_NONE;
+
+       return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)];
+}
\ No newline at end of file
diff --git a/components/wpa_supplicant/src/wps/uuid.c b/components/wpa_supplicant/src/wps/uuid.c
new file mode 100644 (file)
index 0000000..9f46824
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Universally Unique IDentifier (UUID)
+ * 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 "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wps/utils/uuid.h"
+
+int uuid_str2bin(const char *str, u8 *bin)
+{
+       const char *pos;
+       u8 *opos;
+
+       pos = str;
+       opos = bin;
+
+       if (hexstr2bin(pos, opos, 4))
+               return -1;
+       pos += 8;
+       opos += 4;
+
+       if (*pos++ != '-' || hexstr2bin(pos, opos, 2))
+               return -1;
+       pos += 4;
+       opos += 2;
+
+       if (*pos++ != '-' || hexstr2bin(pos, opos, 2))
+               return -1;
+       pos += 4;
+       opos += 2;
+
+       if (*pos++ != '-' || hexstr2bin(pos, opos, 2))
+               return -1;
+       pos += 4;
+       opos += 2;
+
+       if (*pos++ != '-' || hexstr2bin(pos, opos, 6))
+               return -1;
+
+       return 0;
+}
+
+
+int uuid_bin2str(const u8 *bin, char *str, size_t max_len)
+{
+       int len;
+       len = snprintf(str, max_len, "%02x%02x%02x%02x-%02x%02x-%02x%02x-"
+                         "%02x%02x-%02x%02x%02x%02x%02x%02x",
+                         bin[0], bin[1], bin[2], bin[3],
+                         bin[4], bin[5], bin[6], bin[7],
+                         bin[8], bin[9], bin[10], bin[11],
+                         bin[12], bin[13], bin[14], bin[15]);
+       if (len < 0 || (size_t) len >= max_len)
+               return -1;
+       return 0;
+}
+
+
+int is_nil_uuid(const u8 *uuid)
+{
+       int i;
+       for (i = 0; i < UUID_LEN; i++)
+               if (uuid[i])
+                       return 0;
+       return 1;
+}
\ No newline at end of file
diff --git a/components/wpa_supplicant/src/wps/wps.c b/components/wpa_supplicant/src/wps/wps.c
new file mode 100644 (file)
index 0000000..2ed83bb
--- /dev/null
@@ -0,0 +1,641 @@
+/*
+ * Wi-Fi Protected Setup
+ * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#include <string.h>
+
+#include "wpa/includes.h"
+#include "wpa/wpa.h"
+#include "wpa/common.h"
+#include "wpa/eapol_common.h"
+#include "wpa/wpa_debug.h"
+#include "wpa/ieee802_11_defs.h"
+
+#include "crypto/dh_group5.h"
+
+#include "wps/wps_i.h"
+#include "wps/wps_dev_attr.h"
+
+#include "wpa2/eap_peer/eap_defs.h"
+#include "wpa2/eap_peer/eap_common.h"
+
+
+/**
+ * wps_process_msg - Process a WPS message
+ * @wps: WPS Registration protocol data from wps_init()
+ * @op_code: Message OP Code
+ * @msg: Message data
+ * Returns: Processing result
+ *
+ * This function is used to process WPS messages with OP Codes WSC_ACK,
+ * WSC_NACK, WSC_MSG, and WSC_Done. The caller (e.g., EAP server/peer) is
+ * responsible for reassembling the messages before calling this function.
+ * Response to this message is built by calling wps_get_msg().
+ */
+enum wps_process_res wps_process_msg(struct wps_data *wps,
+                     enum wsc_op_code op_code,
+                     const struct wpabuf *msg)
+{
+    if (wps->registrar)
+        return wps_registrar_process_msg(wps, op_code, msg);
+    else
+        return wps_enrollee_process_msg(wps, op_code, msg);
+}
+
+
+/**
+ * wps_get_msg - Build a WPS message
+ * @wps: WPS Registration protocol data from wps_init()
+ * @op_code: Buffer for returning message OP Code
+ * Returns: The generated WPS message or %NULL on failure
+ *
+ * This function is used to build a response to a message processed by calling
+ * wps_process_msg(). The caller is responsible for freeing the buffer.
+ */
+struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code)
+{
+    if (wps->registrar)
+        return wps_registrar_get_msg(wps, op_code);
+    else
+        return wps_enrollee_get_msg(wps, op_code);
+}
+
+
+/**
+ * wps_is_selected_pbc_registrar - Check whether WPS IE indicates active PBC
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * Returns: 1 if PBC Registrar is active, 0 if not
+ */
+int wps_is_selected_pbc_registrar(const struct wpabuf *msg, u8 *bssid)
+{
+       struct wps_sm *sm = wps_sm_get();
+    struct wps_parse_attr *attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+    int i = 0;
+
+    /*
+     * In theory, this could also verify that attr.sel_reg_config_methods
+     * includes WPS_CONFIG_PUSHBUTTON, but some deployed AP implementations
+     * do not set Selected Registrar Config Methods attribute properly, so
+     * it is safer to just use Device Password ID here.
+     */
+
+    if (wps_parse_msg(msg, attr) < 0) {
+       os_free(attr);
+       return 0;
+    }
+
+    if(!attr->selected_registrar || *attr->selected_registrar == 0) {
+       if (sm->ignore_sel_reg == false) {
+               os_free(attr);
+           return 0;
+       }
+       else {
+          for (i = 0; i < WPS_MAX_DIS_AP_NUM; i++) {
+               if (0 == os_memcmp(sm->dis_ap_list[i].bssid, bssid, 6)) {
+                       wpa_printf(MSG_DEBUG, "discard ap bssid[%02x:%02x:%02x:%02x:%02x:%02x]\n", \
+                                       bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
+                       os_free(attr);
+                       return 0;
+               }
+          }
+       }
+    }
+
+    if (!attr->dev_password_id ||
+        WPA_GET_BE16(attr->dev_password_id) != DEV_PW_PUSHBUTTON) {
+        os_free(attr);
+        return 0;
+    }
+#if 0
+#ifdef CONFIG_WPS_STRICT
+    if (!attr->sel_reg_config_methods ||
+        !(WPA_GET_BE16(attr->sel_reg_config_methods) &
+          WPS_CONFIG_PUSHBUTTON)) {
+        os_free(attr);
+        return 0;
+        }
+#endif /* CONFIG_WPS_STRICT */
+#endif
+    os_free(attr);
+    return 1;
+}
+
+#ifdef CONFIG_WPS_PIN
+
+static int is_selected_pin_registrar(struct wps_parse_attr *attr, u8 *bssid)
+{
+       struct wps_sm *sm = wps_sm_get();
+       int i = 0;
+
+       if (!sm || !bssid){
+               return 0;
+       }
+    /*
+     * In theory, this could also verify that attr.sel_reg_config_methods
+     * includes WPS_CONFIG_LABEL, WPS_CONFIG_DISPLAY, or WPS_CONFIG_KEYPAD,
+     * but some deployed AP implementations do not set Selected Registrar
+     * Config Methods attribute properly, so it is safer to just use
+     * Device Password ID here.
+     */
+
+    if (!attr->selected_registrar || *attr->selected_registrar == 0) {
+       if (sm->ignore_sel_reg == false) {
+               return 0;
+        }
+       else {
+               for (i = 0; i < WPS_MAX_DIS_AP_NUM; i++) {
+                   if (0 == os_memcmp(sm->dis_ap_list[i].bssid, bssid, 6)) {
+                       wpa_printf(MSG_DEBUG, "discard ap bssid[%02x:%02x:%02x:%02x:%02x:%02x]\n", \
+                           bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
+                       return 0;
+                   }
+               }
+       }
+    }
+    if (attr->dev_password_id != NULL &&
+        WPA_GET_BE16(attr->dev_password_id) == DEV_PW_PUSHBUTTON) {
+        return 0;
+    }
+#ifdef CONFIG_WPS_STRICT
+    if (!attr->sel_reg_config_methods)// ||
+        //!(WPA_GET_BE16(attr->sel_reg_config_methods) &
+          //(WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD)))
+        return 0;
+#endif /* CONFIG_WPS_STRICT */
+    return 1;
+}
+
+
+/**
+ * wps_is_selected_pin_registrar - Check whether WPS IE indicates active PIN
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * Returns: 1 if PIN Registrar is active, 0 if not
+ */
+int wps_is_selected_pin_registrar(const struct wpabuf *msg, u8 *bssid)
+{
+    struct wps_parse_attr *attr;
+    int ret;
+
+    attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+    if (attr == NULL)
+        return -99;
+
+    if (wps_parse_msg(msg, attr) < 0) {
+        os_free(attr);
+        return 0;
+    }
+
+    ret = is_selected_pin_registrar(attr, bssid);
+    os_free(attr);
+
+    return ret;
+}
+#endif
+
+/**
+ * wps_is_addr_authorized - Check whether WPS IE authorizes MAC address
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * @addr: MAC address to search for
+ * @ver1_compat: Whether to use version 1 compatibility mode
+ * Returns: 2 if the specified address is explicit authorized, 1 if address is
+ * authorized (broadcast), 0 if not
+ */
+int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr,
+               int ver1_compat)
+{
+       struct wps_sm *sm = wps_sm_get();
+    struct wps_parse_attr *attr;
+    int ret = 0;
+    unsigned int i;
+    const u8 *pos;
+    const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+    if (!sm){
+       return -10;
+    }
+
+    attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+    if (attr == NULL) {
+        ret = -99;
+        goto _out;
+    }
+
+    if (wps_parse_msg(msg, attr) < 0) {
+        ret = 0;
+        goto _out;
+    }
+
+    if (!attr->version2 && ver1_compat) {
+        /*
+         * Version 1.0 AP - AuthorizedMACs not used, so revert back to
+         * old mechanism of using SelectedRegistrar.
+         */
+#ifdef CONFIG_WPS_PIN
+
+        ret = is_selected_pin_registrar(attr, sm->config.bssid);
+        goto _out;
+#endif
+    }
+
+    if (!attr->authorized_macs) {
+        ret = 0;
+        goto _out;
+    }
+
+    pos = attr->authorized_macs;
+    for (i = 0; i < attr->authorized_macs_len / ETH_ALEN; i++) {
+        if (os_memcmp(pos, addr, ETH_ALEN) == 0) {
+            ret = 2;
+            goto _out;
+        }
+        if (os_memcmp(pos, bcast, ETH_ALEN) == 0) {
+            ret = 1;
+            goto _out;
+        }
+        pos += ETH_ALEN;
+    }
+_out:
+    if (attr)
+        os_free(attr);
+
+    return ret;
+}
+
+
+/**
+ * wps_ap_priority_compar - Prioritize WPS IE from two APs
+ * @wps_a: WPS IE contents from Beacon or Probe Response frame
+ * @wps_b: WPS IE contents from Beacon or Probe Response frame
+ * Returns: 1 if wps_b is considered more likely selection for WPS
+ * provisioning, -1 if wps_a is considered more like, or 0 if no preference
+ */
+int wps_ap_priority_compar(const struct wpabuf *wps_a,
+               const struct wpabuf *wps_b)
+{
+    struct wps_parse_attr *attr_a, *attr_b;
+    int sel_a, sel_b;
+    int ret = 0;
+
+    attr_a = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+    attr_b = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+
+    if (attr_a == NULL || attr_b == NULL) {
+        ret = 0;
+        goto _out;
+    }
+
+    if (wps_a == NULL || wps_parse_msg(wps_a, attr_a) < 0)
+        return 1;
+    if (wps_b == NULL || wps_parse_msg(wps_b, attr_b) < 0)
+        return -1;
+
+    sel_a = attr_a->selected_registrar && *attr_a->selected_registrar != 0;
+    sel_b = attr_b->selected_registrar && *attr_b->selected_registrar != 0;
+
+    if (sel_a && !sel_b) {
+        ret = -1;
+        goto _out;
+    }
+    if (!sel_a && sel_b) {
+        ret = 1;
+        goto _out;
+    }
+
+_out:
+    if (attr_a)
+        os_free(attr_a);
+    if (attr_b)
+        os_free(attr_b);
+    return ret;
+}
+
+
+/**
+ * wps_get_uuid_e - Get UUID-E from WPS IE
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * Returns: Pointer to UUID-E or %NULL if not included
+ *
+ * The returned pointer is to the msg contents and it remains valid only as
+ * long as the msg buffer is valid.
+ */
+const u8 * wps_get_uuid_e(const struct wpabuf *msg)
+{
+    struct wps_parse_attr *attr;
+    const u8 *uuid_e;
+
+    attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+    if (attr == NULL)
+        return NULL;
+
+    if (wps_parse_msg(msg, attr) < 0) {
+        uuid_e = NULL;
+    } else {
+        uuid_e = attr->uuid_e;
+    }
+    os_free(attr);
+    return uuid_e;
+}
+
+
+/**
+ * wps_is_20 - Check whether WPS attributes claim support for WPS 2.0
+ */
+int wps_is_20(const struct wpabuf *msg)
+{
+    struct wps_parse_attr *attr;
+    int ret;
+
+    attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+    if (attr == NULL)
+        return 0;
+
+    if (msg == NULL || wps_parse_msg(msg, attr) < 0) {
+        ret = 0;
+    } else {
+        ret = (attr->version2 != NULL);
+    }
+    os_free(attr);
+    return ret;
+}
+
+
+/**
+ * wps_build_assoc_req_ie - Build WPS IE for (Re)Association Request
+ * @req_type: Value for Request Type attribute
+ * Returns: WPS IE or %NULL on failure
+ *
+ * The caller is responsible for freeing the buffer.
+ */
+struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type)
+{
+    struct wpabuf *ie;
+    u8 *len;
+
+    wpa_printf(MSG_DEBUG,  "WPS: Building WPS IE for (Re)Association "
+           "Request");
+    ie = wpabuf_alloc(100);
+    if (ie == NULL)
+        return NULL;
+
+    wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+    len = wpabuf_put(ie, 1);
+    wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
+
+    if (wps_build_version(ie) ||
+        wps_build_req_type(ie, req_type) ||
+        wps_build_wfa_ext(ie, 0, NULL, 0)) {
+        wpabuf_free(ie);
+        return NULL;
+    }
+
+    *len = wpabuf_len(ie) - 2;
+
+    return ie;
+}
+
+
+/**
+ * wps_build_assoc_resp_ie - Build WPS IE for (Re)Association Response
+ * Returns: WPS IE or %NULL on failure
+ *
+ * The caller is responsible for freeing the buffer.
+ */
+struct wpabuf * wps_build_assoc_resp_ie(void)
+{
+    struct wpabuf *ie;
+    u8 *len;
+
+    wpa_printf(MSG_DEBUG,  "WPS: Building WPS IE for (Re)Association "
+           "Response");
+    ie = wpabuf_alloc(100);
+    if (ie == NULL)
+        return NULL;
+
+    wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+    len = wpabuf_put(ie, 1);
+    wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
+
+    if (wps_build_version(ie) ||
+        wps_build_resp_type(ie, WPS_RESP_AP) ||
+        wps_build_wfa_ext(ie, 0, NULL, 0)) {
+        wpabuf_free(ie);
+        return NULL;
+    }
+
+    *len = wpabuf_len(ie) - 2;
+
+    return ie;
+}
+
+
+/**
+ * wps_build_probe_req_ie - Build WPS IE for Probe Request
+ * @pw_id: Password ID (DEV_PW_PUSHBUTTON for active PBC and DEV_PW_DEFAULT for
+ * most other use cases)
+ * @dev: Device attributes
+ * @uuid: Own UUID
+ * @req_type: Value for Request Type attribute
+ * @num_req_dev_types: Number of requested device types
+ * @req_dev_types: Requested device types (8 * num_req_dev_types octets) or
+ *    %NULL if none
+ * Returns: WPS IE or %NULL on failure
+ *
+ * The caller is responsible for freeing the buffer.
+ */
+struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev,
+                       const u8 *uuid,
+                       enum wps_request_type req_type,
+                       unsigned int num_req_dev_types,
+                       const u8 *req_dev_types)
+{
+    struct wpabuf *ie;
+
+    wpa_printf(MSG_DEBUG,  "WPS: Building WPS IE for Probe Request\n");
+
+    ie = wpabuf_alloc(400);
+    if (ie == NULL) {
+        wpa_printf(MSG_ERROR, "WPS: ie alloc failed.");
+        return NULL;
+    }
+
+    if (wps_build_version(ie) ||
+        wps_build_req_type(ie, req_type) ||
+        wps_build_config_methods(ie, dev->config_methods) ||
+        wps_build_uuid_e(ie, uuid) ||
+        wps_build_primary_dev_type(dev, ie) ||
+        wps_build_rf_bands(dev, ie) ||
+        wps_build_assoc_state(NULL, ie) ||
+        wps_build_config_error(ie, WPS_CFG_NO_ERROR) ||
+        wps_build_dev_password_id(ie, pw_id) ||
+#ifdef CONFIG_WPS2
+        wps_build_manufacturer(dev, ie) ||
+        wps_build_model_name(dev, ie) ||
+        wps_build_model_number(dev, ie) ||
+        wps_build_dev_name(dev, ie) ||
+        wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0) ||
+#endif /* CONFIG_WPS2 */
+        wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types)
+        ||
+        wps_build_secondary_dev_type(dev, ie)
+        ) {
+        wpabuf_free(ie);
+        return NULL;
+    }
+
+#ifndef CONFIG_WPS2
+    if (dev->p2p && wps_build_dev_name(dev, ie)) {
+        wpabuf_free(ie);
+        return NULL;
+    }
+#endif /* CONFIG_WPS2 */
+
+    return wps_ie_encapsulate(ie);
+}
+
+#ifdef CONFIG_WPS_UPNP
+
+void wps_free_pending_msgs(struct upnp_pending_message *msgs)
+{
+    struct upnp_pending_message *p, *prev;
+    p = msgs;
+    while (p) {
+        prev = p;
+        p = p->next;
+        wpabuf_free(prev->msg);
+        os_free(prev);
+    }
+}
+
+#endif
+
+int wps_attr_text(struct wpabuf *data, char *buf, char *end)
+{
+    struct wps_parse_attr *attr;
+    char *pos = buf;
+    int ret;
+
+    attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+    if (attr == NULL)
+        return -99;
+
+    if (wps_parse_msg(data, attr) < 0) {
+        ret = -1;
+        goto _out;
+    }
+
+    if (attr->wps_state) {
+        if (*attr->wps_state == WPS_STATE_NOT_CONFIGURED)
+            ret = snprintf(pos, end - pos,
+                      "wps_state=unconfigured\n");
+        else if (*attr->wps_state == WPS_STATE_CONFIGURED)
+            ret = snprintf(pos, end - pos,
+                      "wps_state=configured\n");
+        else
+            ret = 0;
+        if (ret < 0 || ret >= end - pos) {
+            ret = pos - buf;
+            goto _out;
+        }
+        pos += ret;
+    }
+
+    if (attr->ap_setup_locked && *attr->ap_setup_locked) {
+        ret = snprintf(pos, end - pos,
+                  "wps_ap_setup_locked=1\n");
+        if (ret < 0 || ret >= end - pos) {
+            ret = pos - buf;
+            goto _out;
+        }
+        pos += ret;
+    }
+
+    if (attr->selected_registrar && *attr->selected_registrar) {
+        ret = snprintf(pos, end - pos,
+                  "wps_selected_registrar=1\n");
+        if (ret < 0 || ret >= end - pos) {
+            ret = pos - buf;
+            goto _out;
+        }
+        pos += ret;
+    }
+
+    if (attr->dev_password_id) {
+        ret = snprintf(pos, end - pos,
+                  "wps_device_password_id=%u\n",
+                  WPA_GET_BE16(attr->dev_password_id));
+        if (ret < 0 || ret >= end - pos) {
+            ret = pos - buf;
+            goto _out;
+        }
+        pos += ret;
+    }
+
+    if (attr->sel_reg_config_methods) {
+        ret = snprintf(pos, end - pos,
+                  "wps_selected_registrar_config_methods="
+                  "0x%04x\n",
+                  WPA_GET_BE16(attr->sel_reg_config_methods));
+        if (ret < 0 || ret >= end - pos) {
+            ret = pos - buf;
+            goto _out;
+        }
+        pos += ret;
+    }
+
+    if (attr->primary_dev_type) {
+        char devtype[WPS_DEV_TYPE_BUFSIZE];
+        ret = snprintf(pos, end - pos,
+                  "wps_primary_device_type=%s\n",
+                  wps_dev_type_bin2str(attr->primary_dev_type,
+                               devtype,
+                               sizeof(devtype)));
+        if (ret < 0 || ret >= end - pos) {
+            ret = pos - buf;
+            goto _out;
+        }
+        pos += ret;
+    }
+
+    if (attr->dev_name) {
+        char *str = (char *)os_malloc(attr->dev_name_len + 1);
+        size_t i;
+        if (str == NULL) {
+            ret = pos - buf;
+            goto _out;
+        }
+        for (i = 0; i < attr->dev_name_len; i++) {
+            if (attr->dev_name[i] < 32)
+                str[i] = '_';
+            else
+                str[i] = attr->dev_name[i];
+        }
+        str[i] = '\0';
+        ret = snprintf(pos, end - pos, "wps_device_name=%s\n", str);
+        os_free(str);
+        if (ret < 0 || ret >= end - pos) {
+            ret = pos - buf;
+            goto _out;
+        }
+        pos += ret;
+    }
+
+    if (attr->config_methods) {
+        ret = snprintf(pos, end - pos,
+                  "wps_config_methods=0x%04x\n",
+                  WPA_GET_BE16(attr->config_methods));
+        if (ret < 0 || ret >= end - pos) {
+            ret = pos - buf;
+            goto _out;
+        }
+        pos += ret;
+    }
+
+    ret = pos - buf;
+_out:
+    if (attr)
+        os_free(attr);
+    return ret;
+}
diff --git a/components/wpa_supplicant/src/wps/wps_attr_build.c b/components/wpa_supplicant/src/wps/wps_attr_build.c
new file mode 100644 (file)
index 0000000..42e2d6b
--- /dev/null
@@ -0,0 +1,438 @@
+/*
+ * Wi-Fi Protected Setup - attribute building
+ * 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 "wpa/includes.h"
+#include "wpa/common.h"
+#include "wpa/wpa_debug.h"
+
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/dh_group5.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+
+#include "wpa/ieee802_11_defs.h"
+#include "wps/wps_i.h"
+#include "soc/dport_reg.h"
+
+int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg, wps_key_mode_t mode)
+{
+       struct wpabuf *pubkey;
+
+       if (mode != WPS_CALC_KEY_NO_CALC) {
+
+               wpa_printf(MSG_DEBUG,  "WPS:  * Public Key");
+               wpabuf_free(wps->dh_privkey);
+               if (wps->dev_pw_id != DEV_PW_DEFAULT && wps->wps->dh_privkey) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Using pre-configured DH keys");
+                       wps->dh_privkey = wpabuf_dup(wps->wps->dh_privkey);
+                       wps->dh_ctx = wps->wps->dh_ctx;
+                       wps->wps->dh_ctx = NULL;
+                       pubkey = wpabuf_dup(wps->wps->dh_pubkey);
+#ifdef CONFIG_WPS_NFC
+               } else if (wps->dev_pw_id >= 0x10 && wps->wps->ap &&
+                               wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Using NFC password token DH keys");
+                       wps->dh_privkey = wpabuf_dup(wps->wps->ap_nfc_dh_privkey);
+                       pubkey = wpabuf_dup(wps->wps->ap_nfc_dh_pubkey);
+                       wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, pubkey);
+#endif /* CONFIG_WPS_NFC */
+               } else {
+                       wpa_printf(MSG_DEBUG,  "WPS: Generate new DH keys");
+                       wps->dh_privkey = NULL;
+                       dh5_free(wps->dh_ctx);
+
+                       wpa_printf(MSG_DEBUG, "build public key start\n");
+
+                       wps->dh_ctx = dh5_init(&wps->dh_privkey, &pubkey);
+
+                       wpa_printf(MSG_DEBUG, "build public key finish\n");
+
+                       pubkey = wpabuf_zeropad(pubkey, 192);
+               }
+               if (wps->dh_ctx == NULL || wps->dh_privkey == NULL || pubkey == NULL) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Failed to initialize "
+                                       "Diffie-Hellman handshake");
+                       wpabuf_free(pubkey);
+                       return -1;
+               }
+               wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey);
+               wpa_hexdump_buf(MSG_DEBUG, "WPS: DH own Public Key", pubkey);
+
+               if (wps->registrar) {
+                       wpabuf_free(wps->dh_pubkey_r);
+                       wps->dh_pubkey_r = pubkey;
+               } else {
+                       wpabuf_free(wps->dh_pubkey_e);
+                       wps->dh_pubkey_e = pubkey;
+               }
+
+       }
+
+       if (mode != WPS_CALC_KEY_PRE_CALC) {
+               if (wps->registrar)
+                       pubkey = wps->dh_pubkey_r;
+               else
+                       pubkey = wps->dh_pubkey_e;
+
+               wpabuf_put_be16(msg, ATTR_PUBLIC_KEY);
+               wpabuf_put_be16(msg, wpabuf_len(pubkey));
+               wpabuf_put_buf(msg, pubkey);
+       }
+
+       return 0;
+}
+
+
+int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * Request Type");
+       wpabuf_put_be16(msg, ATTR_REQUEST_TYPE);
+       wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, type);
+       return 0;
+}
+
+
+int wps_build_resp_type(struct wpabuf *msg, enum wps_response_type type)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * Response Type (%d)", type);
+       wpabuf_put_be16(msg, ATTR_RESPONSE_TYPE);
+       wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, type);
+       return 0;
+}
+
+
+int wps_build_config_methods(struct wpabuf *msg, u16 methods)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * Config Methods (%x)", methods);
+       wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, methods);
+       return 0;
+}
+
+
+int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * UUID-E");
+       wpabuf_put_be16(msg, ATTR_UUID_E);
+       wpabuf_put_be16(msg, WPS_UUID_LEN);
+       wpabuf_put_data(msg, uuid, WPS_UUID_LEN);
+       return 0;
+}
+
+
+int wps_build_dev_password_id(struct wpabuf *msg, u16 id)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * Device Password ID (%d)", id);
+       wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, id);
+       return 0;
+}
+
+
+int wps_build_config_error(struct wpabuf *msg, u16 err)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * Configuration Error (%d)", err);
+       wpabuf_put_be16(msg, ATTR_CONFIG_ERROR);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, err);
+       return 0;
+}
+
+
+int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg)
+{
+       u8 hash[SHA256_MAC_LEN];
+       const u8 *addr[2];
+       size_t len[2];
+
+       if (wps->last_msg == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: Last message not available for "
+                          "building authenticator");
+               return -1;
+       }
+
+       /* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*)
+        * (M_curr* is M_curr without the Authenticator attribute)
+        */
+       addr[0] = wpabuf_head(wps->last_msg);
+       len[0] = wpabuf_len(wps->last_msg);
+       addr[1] = wpabuf_head(msg);
+       len[1] = wpabuf_len(msg);
+       if (wps_crypto_funcs.hmac_sha256_vector) {
+               wps_crypto_funcs.hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, (int *)len, hash);
+       } else {
+               wpa_printf(MSG_ERROR, "Fail to register hmac sha256 vector!\r\n");
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG,  "WPS:  * Authenticator");
+       wpabuf_put_be16(msg, ATTR_AUTHENTICATOR);
+       wpabuf_put_be16(msg, WPS_AUTHENTICATOR_LEN);
+       wpabuf_put_data(msg, hash, WPS_AUTHENTICATOR_LEN);
+
+       return 0;
+}
+
+
+int wps_build_version(struct wpabuf *msg)
+{
+       /*
+        * Note: This attribute is deprecated and set to hardcoded 0x10 for
+        * backwards compatibility reasons. The real version negotiation is
+        * done with Version2.
+        */
+       wpa_printf(MSG_DEBUG,  "WPS:  * Version (hardcoded 0x10)");
+       wpabuf_put_be16(msg, ATTR_VERSION);
+       wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, 0x10);
+       return 0;
+}
+
+
+int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
+                     const u8 *auth_macs, size_t auth_macs_count)
+{
+#ifdef CONFIG_WPS2
+       u8 *len;
+
+       wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
+       len = wpabuf_put(msg, 2); /* to be filled */
+       wpabuf_put_be24(msg, WPS_VENDOR_ID_WFA);
+
+       wpa_printf(MSG_DEBUG,  "WPS:  * Version2 (0x%x)", WPS_VERSION);
+       wpabuf_put_u8(msg, WFA_ELEM_VERSION2);
+       wpabuf_put_u8(msg, 1);
+       wpabuf_put_u8(msg, WPS_VERSION);
+
+       if (req_to_enroll) {
+               wpa_printf(MSG_DEBUG,  "WPS:  * Request to Enroll (1)");
+               wpabuf_put_u8(msg, WFA_ELEM_REQUEST_TO_ENROLL);
+               wpabuf_put_u8(msg, 1);
+               wpabuf_put_u8(msg, 1);
+       }
+
+       if (auth_macs && auth_macs_count) {
+               size_t i;
+               wpa_printf(MSG_DEBUG,  "WPS:  * AuthorizedMACs (count=%d)",
+                          (int) auth_macs_count);
+               wpabuf_put_u8(msg, WFA_ELEM_AUTHORIZEDMACS);
+               wpabuf_put_u8(msg, auth_macs_count * ETH_ALEN);
+               wpabuf_put_data(msg, auth_macs, auth_macs_count * ETH_ALEN);
+               for (i = 0; i < auth_macs_count; i++)
+                       wpa_printf(MSG_DEBUG,  "WPS:    AuthorizedMAC: " MACSTR,
+                                  MAC2STR(&auth_macs[i * ETH_ALEN]));
+       }
+
+       WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2);
+#endif /* CONFIG_WPS2 */
+
+#ifdef CONFIG_WPS_TESTING
+       if (WPS_VERSION > 0x20) {
+               wpa_printf(MSG_DEBUG,  "WPS:  * Extensibility Testing - extra "
+                          "attribute");
+               wpabuf_put_be16(msg, ATTR_EXTENSIBILITY_TEST);
+               wpabuf_put_be16(msg, 1);
+               wpabuf_put_u8(msg, 42);
+       }
+#endif /* CONFIG_WPS_TESTING */
+       return 0;
+}
+
+
+int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * Message Type (%d)", msg_type);
+       wpabuf_put_be16(msg, ATTR_MSG_TYPE);
+       wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, msg_type);
+       return 0;
+}
+
+
+int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * Enrollee Nonce");
+       wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE);
+       wpabuf_put_be16(msg, WPS_NONCE_LEN);
+       wpabuf_put_data(msg, wps->nonce_e, WPS_NONCE_LEN);
+       return 0;
+}
+
+
+int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * Registrar Nonce");
+       wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
+       wpabuf_put_be16(msg, WPS_NONCE_LEN);
+       wpabuf_put_data(msg, wps->nonce_r, WPS_NONCE_LEN);
+       return 0;
+}
+
+
+int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+       u16 auth_types = WPS_AUTH_TYPES;
+#ifdef CONFIG_WPS2
+       auth_types &= ~WPS_AUTH_SHARED;
+#endif /* CONFIG_WPS2 */
+       wpa_printf(MSG_DEBUG,  "WPS:  * Authentication Type Flags");
+       wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, auth_types);
+       return 0;
+}
+
+
+int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+       u16 encr_types = WPS_ENCR_TYPES;
+#ifdef CONFIG_WPS2
+       encr_types &= ~WPS_ENCR_WEP;
+#endif /* CONFIG_WPS2 */
+       wpa_printf(MSG_DEBUG,  "WPS:  * Encryption Type Flags");
+       wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, encr_types);
+       return 0;
+}
+
+
+int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * Connection Type Flags");
+       wpabuf_put_be16(msg, ATTR_CONN_TYPE_FLAGS);
+       wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, WPS_CONN_ESS);
+       return 0;
+}
+
+
+int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * Association State");
+       wpabuf_put_be16(msg, ATTR_ASSOC_STATE);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, WPS_ASSOC_NOT_ASSOC);
+       return 0;
+}
+
+
+int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg)
+{
+       u8 hash[SHA256_MAC_LEN];
+
+       wpa_printf(MSG_DEBUG,  "WPS:  * Key Wrap Authenticator");
+       if (wps_crypto_funcs.hmac_sha256) {
+               wps_crypto_funcs.hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, wpabuf_head(msg),
+                                            wpabuf_len(msg), hash);
+       } else {
+               wpa_printf(MSG_ERROR, "Fail to register hmac sha256 function!\r\n");
+               return -1;
+       }
+       wpabuf_put_be16(msg, ATTR_KEY_WRAP_AUTH);
+       wpabuf_put_be16(msg, WPS_KWA_LEN);
+       wpabuf_put_data(msg, hash, WPS_KWA_LEN);
+       return 0;
+}
+
+
+int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
+                           struct wpabuf *plain)
+{
+       size_t pad_len;
+       const size_t block_size = 16;
+       u8 *iv, *data;
+
+       wpa_printf(MSG_DEBUG,  "WPS:  * Encrypted Settings");
+
+       /* PKCS#5 v2.0 pad */
+       pad_len = block_size - wpabuf_len(plain) % block_size;
+       os_memset(wpabuf_put(plain, pad_len), pad_len, pad_len);
+
+       wpabuf_put_be16(msg, ATTR_ENCR_SETTINGS);
+       wpabuf_put_be16(msg, block_size + wpabuf_len(plain));
+
+       iv = wpabuf_put(msg, block_size);
+       if (random_get_bytes(iv, block_size) < 0)
+               return -1;
+
+       data = wpabuf_put(msg, 0);
+       wpabuf_put_buf(msg, plain);
+       wpa_printf(MSG_DEBUG,  "WPS:  * AES 128 Encrypted Settings");
+       if (wps_crypto_funcs.aes_128_encrypt) {
+               if (wps_crypto_funcs.aes_128_encrypt(wps->keywrapkey, iv, data, wpabuf_len(plain)))
+                       return -1;
+       } else {
+               wpa_printf(MSG_ERROR, "Fail to register aes_128_encrypt function!\r\n");
+               return -1;
+       }
+       return 0;
+}
+
+
+#ifdef CONFIG_WPS_OOB
+int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id,
+                        const struct wpabuf *pubkey, const u8 *dev_pw,
+                        size_t dev_pw_len)
+{
+       size_t hash_len;
+       const u8 *addr[1];
+       u8 pubkey_hash[WPS_HASH_LEN];
+
+       addr[0] = wpabuf_head(pubkey);
+       hash_len = wpabuf_len(pubkey);
+       if (wps_crypto_funcs.sha256_vector) {
+               wps_crypto_funcs.sha256_vector(1, addr, &hash_len, pubkey_hash);
+       } else {
+               wpa_printf(MSG_ERROR, "Fail to register sha256 vector function!\r\n");
+               return -1;
+       }
+       wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD);
+       wpabuf_put_be16(msg, WPS_OOB_PUBKEY_HASH_LEN + 2 + dev_pw_len);
+       wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+       wpabuf_put_be16(msg, dev_pw_id);
+       wpabuf_put_data(msg, dev_pw, dev_pw_len);
+
+       return 0;
+}
+#endif /* CONFIG_WPS_OOB */
+
+
+/* Encapsulate WPS IE data with one (or more, if needed) IE headers */
+struct wpabuf * wps_ie_encapsulate(struct wpabuf *data)
+{
+       struct wpabuf *ie;
+       const u8 *pos, *end;
+
+       ie = wpabuf_alloc(wpabuf_len(data) + 100);
+       if (ie == NULL) {
+               wpabuf_free(data);
+               return NULL;
+       }
+
+       pos = wpabuf_head(data);
+       end = pos + wpabuf_len(data);
+
+       while (end > pos) {
+               size_t frag_len = end - pos;
+               if (frag_len > 251)
+                       frag_len = 251;
+               wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+               wpabuf_put_u8(ie, 4 + frag_len);
+               wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
+               wpabuf_put_data(ie, pos, frag_len);
+               pos += frag_len;
+       }
+
+       wpabuf_free(data);
+
+       return ie;
+}
\ No newline at end of file
diff --git a/components/wpa_supplicant/src/wps/wps_attr_parse.c b/components/wpa_supplicant/src/wps/wps_attr_parse.c
new file mode 100644 (file)
index 0000000..a8cf766
--- /dev/null
@@ -0,0 +1,640 @@
+/*
+ * Wi-Fi Protected Setup - attribute parsing
+ * 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 "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wps/wps_defs.h"
+#include "wps/wps_attr_parse.h"
+
+#ifndef CONFIG_WPS_STRICT
+#define WPS_WORKAROUNDS
+#endif /* CONFIG_WPS_STRICT */
+
+
+static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr,
+                                         u8 id, u8 len, const u8 *pos)
+{
+       wpa_printf(MSG_DEBUG, "WPS: WFA subelement id=%u len=%u",
+                  id, len);
+       switch (id) {
+       case WFA_ELEM_VERSION2:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Version2 length "
+                                  "%u", len);
+                       return -1;
+               }
+               attr->version2 = pos;
+               break;
+       case WFA_ELEM_AUTHORIZEDMACS:
+               attr->authorized_macs = pos;
+               attr->authorized_macs_len = len;
+               break;
+       case WFA_ELEM_NETWORK_KEY_SHAREABLE:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Network Key "
+                                  "Shareable length %u", len);
+                       return -1;
+               }
+               attr->network_key_shareable = pos;
+               break;
+       case WFA_ELEM_REQUEST_TO_ENROLL:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Request to Enroll "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->request_to_enroll = pos;
+               break;
+       case WFA_ELEM_SETTINGS_DELAY_TIME:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Settings Delay "
+                                  "Time length %u", len);
+                       return -1;
+               }
+               attr->settings_delay_time = pos;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "WPS: Skipped unknown WFA Vendor "
+                          "Extension subelement %u", id);
+               break;
+       }
+
+       return 0;
+}
+
+
+static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos,
+                                   u16 len)
+{
+       const u8 *end = pos + len;
+       u8 id, elen;
+
+       while (pos + 2 < end) {
+               id = *pos++;
+               elen = *pos++;
+               if (pos + elen > end)
+                       break;
+               if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0)
+                       return -1;
+               pos += elen;
+       }
+
+       return 0;
+}
+
+
+static int wps_parse_vendor_ext(struct wps_parse_attr *attr, const u8 *pos,
+                               u16 len)
+{
+       u32 vendor_id;
+
+       if (len < 3) {
+               wpa_printf(MSG_DEBUG,  "WPS: Skip invalid Vendor Extension");
+               return 0;
+       }
+
+       vendor_id = WPA_GET_BE24(pos);
+       switch (vendor_id) {
+       case WPS_VENDOR_ID_WFA:
+               return wps_parse_vendor_ext_wfa(attr, pos + 3, len - 3);
+       }
+
+       /* Handle unknown vendor extensions */
+
+       wpa_printf(MSG_DEBUG, "WPS: Unknown Vendor Extension (Vendor ID %u)",
+                  vendor_id);
+
+       if (len > WPS_MAX_VENDOR_EXT_LEN) {
+               wpa_printf(MSG_DEBUG,  "WPS: Too long Vendor Extension (%u)",
+                          len);
+               return -1;
+       }
+
+       if (attr->num_vendor_ext >= MAX_WPS_PARSE_VENDOR_EXT) {
+               wpa_printf(MSG_DEBUG,  "WPS: Skipped Vendor Extension "
+                          "attribute (max %d vendor extensions)",
+                          MAX_WPS_PARSE_VENDOR_EXT);
+               return -1;
+       }
+       attr->vendor_ext[attr->num_vendor_ext] = pos;
+       attr->vendor_ext_len[attr->num_vendor_ext] = len;
+       attr->num_vendor_ext++;
+
+       return 0;
+}
+
+
+static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
+                       const u8 *pos, u16 len)
+{
+       switch (type) {
+       case ATTR_VERSION:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Version length %u",
+                                  len);
+                       return -1;
+               }
+               attr->version = pos;
+               break;
+       case ATTR_MSG_TYPE:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Message Type "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->msg_type = pos;
+               break;
+       case ATTR_ENROLLEE_NONCE:
+               if (len != WPS_NONCE_LEN) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Enrollee Nonce "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->enrollee_nonce = pos;
+               break;
+       case ATTR_REGISTRAR_NONCE:
+               if (len != WPS_NONCE_LEN) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Registrar Nonce "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->registrar_nonce = pos;
+               break;
+       case ATTR_UUID_E:
+               if (len != WPS_UUID_LEN) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid UUID-E length %u",
+                                  len);
+                       return -1;
+               }
+               attr->uuid_e = pos;
+               break;
+       case ATTR_UUID_R:
+               if (len != WPS_UUID_LEN) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid UUID-R length %u",
+                                  len);
+                       return -1;
+               }
+               attr->uuid_r = pos;
+               break;
+       case ATTR_AUTH_TYPE_FLAGS:
+               if (len != 2) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Authentication "
+                                  "Type Flags length %u", len);
+                       return -1;
+               }
+               attr->auth_type_flags = pos;
+               break;
+       case ATTR_ENCR_TYPE_FLAGS:
+               if (len != 2) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Encryption Type "
+                                  "Flags length %u", len);
+                       return -1;
+               }
+               attr->encr_type_flags = pos;
+               break;
+       case ATTR_CONN_TYPE_FLAGS:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Connection Type "
+                                  "Flags length %u", len);
+                       return -1;
+               }
+               attr->conn_type_flags = pos;
+               break;
+       case ATTR_CONFIG_METHODS:
+               if (len != 2) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Config Methods "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->config_methods = pos;
+               break;
+       case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
+               if (len != 2) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Selected "
+                                  "Registrar Config Methods length %u", len);
+                       return -1;
+               }
+               attr->sel_reg_config_methods = pos;
+               break;
+       case ATTR_PRIMARY_DEV_TYPE:
+               if (len != WPS_DEV_TYPE_LEN) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Primary Device "
+                                  "Type length %u", len);
+                       return -1;
+               }
+               attr->primary_dev_type = pos;
+               break;
+       case ATTR_RF_BANDS:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid RF Bands length "
+                                  "%u", len);
+                       return -1;
+               }
+               attr->rf_bands = pos;
+               break;
+       case ATTR_ASSOC_STATE:
+               if (len != 2) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Association State "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->assoc_state = pos;
+               break;
+       case ATTR_CONFIG_ERROR:
+               if (len != 2) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Configuration "
+                                  "Error length %u", len);
+                       return -1;
+               }
+               attr->config_error = pos;
+               break;
+       case ATTR_DEV_PASSWORD_ID:
+               if (len != 2) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Device Password "
+                                  "ID length %u", len);
+                       return -1;
+               }
+               attr->dev_password_id = pos;
+               break;
+       case ATTR_OOB_DEVICE_PASSWORD:
+               if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
+                   WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
+                   len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
+                   WPS_OOB_DEVICE_PASSWORD_LEN) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid OOB Device "
+                                  "Password length %u", len);
+                       return -1;
+               }
+               attr->oob_dev_password = pos;
+               attr->oob_dev_password_len = len;
+               break;
+       case ATTR_OS_VERSION:
+               if (len != 4) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid OS Version length "
+                                  "%u", len);
+                       return -1;
+               }
+               attr->os_version = pos;
+               break;
+       case ATTR_WPS_STATE:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Wi-Fi Protected "
+                                  "Setup State length %u", len);
+                       return -1;
+               }
+               attr->wps_state = pos;
+               break;
+       case ATTR_AUTHENTICATOR:
+               if (len != WPS_AUTHENTICATOR_LEN) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Authenticator "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->authenticator = pos;
+               break;
+       case ATTR_R_HASH1:
+               if (len != WPS_HASH_LEN) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid R-Hash1 length %u",
+                                  len);
+                       return -1;
+               }
+               attr->r_hash1 = pos;
+               break;
+       case ATTR_R_HASH2:
+               if (len != WPS_HASH_LEN) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid R-Hash2 length %u",
+                                  len);
+                       return -1;
+               }
+               attr->r_hash2 = pos;
+               break;
+       case ATTR_E_HASH1:
+               if (len != WPS_HASH_LEN) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid E-Hash1 length %u",
+                                  len);
+                       return -1;
+               }
+               attr->e_hash1 = pos;
+               break;
+       case ATTR_E_HASH2:
+               if (len != WPS_HASH_LEN) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid E-Hash2 length %u",
+                                  len);
+                       return -1;
+               }
+               attr->e_hash2 = pos;
+               break;
+       case ATTR_R_SNONCE1:
+               if (len != WPS_SECRET_NONCE_LEN) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid R-SNonce1 length "
+                                  "%u", len);
+                       return -1;
+               }
+               attr->r_snonce1 = pos;
+               break;
+       case ATTR_R_SNONCE2:
+               if (len != WPS_SECRET_NONCE_LEN) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid R-SNonce2 length "
+                                  "%u", len);
+                       return -1;
+               }
+               attr->r_snonce2 = pos;
+               break;
+       case ATTR_E_SNONCE1:
+               if (len != WPS_SECRET_NONCE_LEN) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid E-SNonce1 length "
+                                  "%u", len);
+                       return -1;
+               }
+               attr->e_snonce1 = pos;
+               break;
+       case ATTR_E_SNONCE2:
+               if (len != WPS_SECRET_NONCE_LEN) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid E-SNonce2 length "
+                                  "%u", len);
+                       return -1;
+               }
+               attr->e_snonce2 = pos;
+               break;
+       case ATTR_KEY_WRAP_AUTH:
+               if (len != WPS_KWA_LEN) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Key Wrap "
+                                  "Authenticator length %u", len);
+                       return -1;
+               }
+               attr->key_wrap_auth = pos;
+               break;
+       case ATTR_AUTH_TYPE:
+               if (len != 2) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Authentication "
+                                  "Type length %u", len);
+                       return -1;
+               }
+               attr->auth_type = pos;
+               break;
+       case ATTR_ENCR_TYPE:
+               if (len != 2) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Encryption "
+                                  "Type length %u", len);
+                       return -1;
+               }
+               attr->encr_type = pos;
+               break;
+       case ATTR_NETWORK_INDEX:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Network Index "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->network_idx = pos;
+               break;
+       case ATTR_NETWORK_KEY_INDEX:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Network Key Index "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->network_key_idx = pos;
+               break;
+       case ATTR_MAC_ADDR:
+               if (len != ETH_ALEN) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid MAC Address "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->mac_addr = pos;
+               break;
+       case ATTR_KEY_PROVIDED_AUTO:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Key Provided "
+                                  "Automatically length %u", len);
+                       return -1;
+               }
+               attr->key_prov_auto = pos;
+               break;
+       case ATTR_802_1X_ENABLED:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid 802.1X Enabled "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->dot1x_enabled = pos;
+               break;
+       case ATTR_SELECTED_REGISTRAR:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Selected Registrar"
+                                  " length %u", len);
+                       return -1;
+               }
+               attr->selected_registrar = pos;
+               break;
+       case ATTR_REQUEST_TYPE:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Request Type "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->request_type = pos;
+               break;
+       case ATTR_RESPONSE_TYPE:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Response Type "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->response_type = pos;
+               break;
+       case ATTR_MANUFACTURER:
+               attr->manufacturer = pos;
+               attr->manufacturer_len = len;
+               break;
+       case ATTR_MODEL_NAME:
+               attr->model_name = pos;
+               attr->model_name_len = len;
+               break;
+       case ATTR_MODEL_NUMBER:
+               attr->model_number = pos;
+               attr->model_number_len = len;
+               break;
+       case ATTR_SERIAL_NUMBER:
+               attr->serial_number = pos;
+               attr->serial_number_len = len;
+               break;
+       case ATTR_DEV_NAME:
+               attr->dev_name = pos;
+               attr->dev_name_len = len;
+               break;
+       case ATTR_PUBLIC_KEY:
+               attr->public_key = pos;
+               attr->public_key_len = len;
+               break;
+       case ATTR_ENCR_SETTINGS:
+               attr->encr_settings = pos;
+               attr->encr_settings_len = len;
+               break;
+       case ATTR_CRED:
+               if (attr->num_cred >= MAX_CRED_COUNT) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Skipped Credential "
+                                  "attribute (max %d credentials)",
+                                  MAX_CRED_COUNT);
+                       break;
+               }
+               attr->cred[attr->num_cred] = pos;
+               attr->cred_len[attr->num_cred] = len;
+               attr->num_cred++;
+               break;
+       case ATTR_SSID:
+               attr->ssid = pos;
+               attr->ssid_len = len;
+               break;
+       case ATTR_NETWORK_KEY:
+               attr->network_key = pos;
+               attr->network_key_len = len;
+               break;
+       case ATTR_EAP_TYPE:
+               attr->eap_type = pos;
+               attr->eap_type_len = len;
+               break;
+       case ATTR_EAP_IDENTITY:
+               attr->eap_identity = pos;
+               attr->eap_identity_len = len;
+               break;
+       case ATTR_AP_SETUP_LOCKED:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid AP Setup Locked "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->ap_setup_locked = pos;
+               break;
+       case ATTR_REQUESTED_DEV_TYPE:
+               if (len != WPS_DEV_TYPE_LEN) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Requested Device "
+                                  "Type length %u", len);
+                       return -1;
+               }
+               if (attr->num_req_dev_type >= MAX_REQ_DEV_TYPE_COUNT) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Skipped Requested Device "
+                                  "Type attribute (max %u types)",
+                                  MAX_REQ_DEV_TYPE_COUNT);
+                       break;
+               }
+               attr->req_dev_type[attr->num_req_dev_type] = pos;
+               attr->num_req_dev_type++;
+               break;
+       case ATTR_SECONDARY_DEV_TYPE_LIST:
+               if (len > WPS_SEC_DEV_TYPE_MAX_LEN ||
+                   (len % WPS_DEV_TYPE_LEN) > 0) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid Secondary Device "
+                                  "Type length %u", len);
+                       return -1;
+               }
+               attr->sec_dev_type_list = pos;
+               attr->sec_dev_type_list_len = len;
+               break;
+       case ATTR_VENDOR_EXT:
+               if (wps_parse_vendor_ext(attr, pos, len) < 0)
+                       return -1;
+               break;
+       case ATTR_AP_CHANNEL:
+               if (len != 2) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid AP Channel "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->ap_channel = pos;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG,  "WPS: Unsupported attribute type 0x%x "
+                          "len=%u", type, len);
+               break;
+       }
+
+       return 0;
+}
+
+
+int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
+{
+       const u8 *pos, *end;
+       u16 type, len;
+#ifdef WPS_WORKAROUNDS
+       u16 prev_type = 0;
+#endif /* WPS_WORKAROUNDS */
+
+       os_memset(attr, 0, sizeof(*attr));
+       pos = wpabuf_head(msg);
+       end = pos + wpabuf_len(msg);
+
+       while (pos < end) {
+               if (end - pos < 4) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid message - "
+                                  "%lu bytes remaining",
+                                  (unsigned long) (end - pos));
+                       return -1;
+               }
+
+               type = WPA_GET_BE16(pos);
+               pos += 2;
+               len = WPA_GET_BE16(pos);
+               pos += 2;
+               wpa_printf(MSG_DEBUG, "WPS: attr type=0x%x len=%u",
+                          type, len);
+               if (len > end - pos) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Attribute overflow");
+                       wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Message data", msg);
+#ifdef WPS_WORKAROUNDS
+                       /*
+                        * Some deployed APs seem to have a bug in encoding of
+                        * Network Key attribute in the Credential attribute
+                        * where they add an extra octet after the Network Key
+                        * attribute at least when open network is being
+                        * provisioned.
+                        */
+                       if ((type & 0xff00) != 0x1000 &&
+                           prev_type == ATTR_NETWORK_KEY) {
+                               wpa_printf(MSG_DEBUG,  "WPS: Workaround - try "
+                                          "to skip unexpected octet after "
+                                          "Network Key");
+                               pos -= 3;
+                               continue;
+                       }
+#endif /* WPS_WORKAROUNDS */
+                       return -1;
+               }
+
+#ifdef WPS_WORKAROUNDS
+               if (type == 0 && len == 0) {
+                       /*
+                        * Mac OS X 10.6 seems to be adding 0x00 padding to the
+                        * end of M1. Skip those to avoid interop issues.
+                        */
+                       int i;
+                       for (i = 0; i < end - pos; i++) {
+                               if (pos[i])
+                                       break;
+                       }
+                       if (i == end - pos) {
+                               wpa_printf(MSG_DEBUG,  "WPS: Workaround - skip "
+                                          "unexpected message padding");
+                               break;
+                       }
+               }
+#endif /* WPS_WORKAROUNDS */
+
+               if (wps_set_attr(attr, type, pos, len) < 0)
+                       return -1;
+
+#ifdef WPS_WORKAROUNDS
+               prev_type = type;
+#endif /* WPS_WORKAROUNDS */
+               pos += len;
+       }
+
+       return 0;
+}
\ No newline at end of file
diff --git a/components/wpa_supplicant/src/wps/wps_attr_process.c b/components/wpa_supplicant/src/wps/wps_attr_process.c
new file mode 100644 (file)
index 0000000..cd2c6d4
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ * Wi-Fi Protected Setup - attribute processing
+ * 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 "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/sha256.h"
+#include "wps/wps_i.h"
+
+
+int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
+                             const struct wpabuf *msg)
+{
+       u8 hash[SHA256_MAC_LEN];
+       const u8 *addr[2];
+       size_t len[2];
+
+       if (authenticator == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Authenticator attribute "
+                          "included");
+               return -1;
+       }
+
+       if (wps->last_msg == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: Last message not available for "
+                          "validating authenticator");
+               return -1;
+       }
+
+       /* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*)
+        * (M_curr* is M_curr without the Authenticator attribute)
+        */
+       addr[0] = wpabuf_head(wps->last_msg);
+       len[0] = wpabuf_len(wps->last_msg);
+       addr[1] = wpabuf_head(msg);
+       len[1] = wpabuf_len(msg) - 4 - WPS_AUTHENTICATOR_LEN;
+       if (wps_crypto_funcs.hmac_sha256_vector) {
+               wps_crypto_funcs.hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, (int *)len, hash);
+       } else {
+               wpa_printf(MSG_ERROR, "Fail to register hmac_sha256_vector function!\r\n");
+               return -1;
+       }
+       if (os_memcmp(hash, authenticator, WPS_AUTHENTICATOR_LEN) != 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: Incorrect Authenticator");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg,
+                             const u8 *key_wrap_auth)
+{
+       u8 hash[SHA256_MAC_LEN];
+       const u8 *head;
+       size_t len;
+
+       if (key_wrap_auth == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No KWA in decrypted attribute");
+               return -1;
+       }
+
+       head = wpabuf_head(msg);
+       len = wpabuf_len(msg) - 4 - WPS_KWA_LEN;
+       if (head + len != key_wrap_auth - 4) {
+               wpa_printf(MSG_DEBUG,  "WPS: KWA not in the end of the "
+                          "decrypted attribute");
+               return -1;
+       }
+
+       if (wps_crypto_funcs.hmac_sha256) {
+               wps_crypto_funcs.hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, head, len, hash);
+       } else {
+               wpa_printf(MSG_ERROR, "Fail to register hmac sha256 function!\r\n");
+               return -1;
+       }
+       if (os_memcmp(hash, key_wrap_auth, WPS_KWA_LEN) != 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: Invalid KWA");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int wps_process_cred_network_idx(struct wps_credential *cred,
+                                       const u8 *idx)
+{
+       if (idx == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: Credential did not include "
+                          "Network Index");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG,  "WPS: Network Index: %d", *idx);
+
+       return 0;
+}
+
+
+static int wps_process_cred_ssid(struct wps_credential *cred, const u8 *ssid,
+                                size_t ssid_len)
+{
+       if (ssid == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: Credential did not include SSID");
+               return -1;
+       }
+
+       /* Remove zero-padding since some Registrar implementations seem to use
+        * hardcoded 32-octet length for this attribute */
+       while (ssid_len > 0 && ssid[ssid_len - 1] == 0)
+               ssid_len--;
+
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", ssid, ssid_len);
+       if (ssid_len <= sizeof(cred->ssid)) {
+               os_memcpy(cred->ssid, ssid, ssid_len);
+               cred->ssid_len = ssid_len;
+       }
+
+       return 0;
+}
+
+
+static int wps_process_cred_auth_type(struct wps_credential *cred,
+                                     const u8 *auth_type)
+{
+       if (auth_type == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: Credential did not include "
+                          "Authentication Type");
+               return -1;
+       }
+
+       cred->auth_type = WPA_GET_BE16(auth_type);
+       wpa_printf(MSG_DEBUG,  "WPS: Authentication Type: 0x%x",
+                  cred->auth_type);
+
+       return 0;
+}
+
+
+static int wps_process_cred_encr_type(struct wps_credential *cred,
+                                     const u8 *encr_type)
+{
+       if (encr_type == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: Credential did not include "
+                          "Encryption Type");
+               return -1;
+       }
+
+       cred->encr_type = WPA_GET_BE16(encr_type);
+       wpa_printf(MSG_DEBUG,  "WPS: Encryption Type: 0x%x",
+                  cred->encr_type);
+
+       return 0;
+}
+
+
+static int wps_process_cred_network_key_idx(struct wps_credential *cred,
+                                           const u8 *key_idx)
+{
+       if (key_idx == NULL)
+               return 0; /* optional attribute */
+
+       wpa_printf(MSG_DEBUG,  "WPS: Network Key Index: %d", *key_idx);
+       cred->key_idx = *key_idx;
+
+       return 0;
+}
+
+
+static int wps_process_cred_network_key(struct wps_credential *cred,
+                                       const u8 *key, size_t key_len)
+{
+       if (key == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: Credential did not include "
+                          "Network Key");
+               if (cred->auth_type == WPS_WIFI_AUTH_OPEN &&
+                   cred->encr_type == WPS_ENCR_NONE) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Workaround - Allow "
+                                  "missing mandatory Network Key attribute "
+                                  "for open network");
+                       return 0;
+               }
+               return -1;
+       }
+
+       wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", key, key_len);
+       if (key_len <= sizeof(cred->key)) {
+               os_memcpy(cred->key, key, key_len);
+               cred->key_len = key_len;
+       }
+
+       return 0;
+}
+
+
+static int wps_process_cred_mac_addr(struct wps_credential *cred,
+                                    const u8 *mac_addr)
+{
+       if (mac_addr == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: Credential did not include "
+                          "MAC Address");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG,  "WPS: MAC Address " MACSTR, MAC2STR(mac_addr));
+       os_memcpy(cred->mac_addr, mac_addr, ETH_ALEN);
+
+       return 0;
+}
+
+
+static int wps_process_cred_eap_type(struct wps_credential *cred,
+                                    const u8 *eap_type, size_t eap_type_len)
+{
+       if (eap_type == NULL)
+               return 0; /* optional attribute */
+
+       wpa_hexdump(MSG_DEBUG, "WPS: EAP Type", eap_type, eap_type_len);
+
+       return 0;
+}
+
+
+static int wps_process_cred_eap_identity(struct wps_credential *cred,
+                                        const u8 *identity,
+                                        size_t identity_len)
+{
+       if (identity == NULL)
+               return 0; /* optional attribute */
+
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: EAP Identity",
+                         identity, identity_len);
+
+       return 0;
+}
+
+
+static int wps_process_cred_key_prov_auto(struct wps_credential *cred,
+                                         const u8 *key_prov_auto)
+{
+       if (key_prov_auto == NULL)
+               return 0; /* optional attribute */
+
+       wpa_printf(MSG_DEBUG,  "WPS: Key Provided Automatically: %d",
+                  *key_prov_auto);
+
+       return 0;
+}
+
+
+static int wps_process_cred_802_1x_enabled(struct wps_credential *cred,
+                                          const u8 *dot1x_enabled)
+{
+       if (dot1x_enabled == NULL)
+               return 0; /* optional attribute */
+
+       wpa_printf(MSG_DEBUG,  "WPS: 802.1X Enabled: %d", *dot1x_enabled);
+
+       return 0;
+}
+
+
+static int wps_process_cred_ap_channel(struct wps_credential *cred,
+                                      const u8 *ap_channel)
+{
+       if (ap_channel == NULL)
+               return 0; /* optional attribute */
+
+       cred->ap_channel = WPA_GET_BE16(ap_channel);
+       wpa_printf(MSG_DEBUG,  "WPS: AP Channel: %u", cred->ap_channel);
+
+       return 0;
+}
+
+
+static int wps_workaround_cred_key(struct wps_credential *cred)
+{
+       if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK) &&
+           cred->key_len > 8 && cred->key_len < 64 &&
+           cred->key[cred->key_len - 1] == 0) {
+#ifdef CONFIG_WPS_STRICT
+               wpa_printf(MSG_INFO, "WPS: WPA/WPA2-Personal passphrase uses "
+                          "forbidden NULL termination");
+               wpa_hexdump_ascii_key(MSG_INFO, "WPS: Network Key",
+                                     cred->key, cred->key_len);
+               return -1;
+#else /* CONFIG_WPS_STRICT */
+               /*
+                * A deployed external registrar is known to encode ASCII
+                * passphrases incorrectly. Remove the extra NULL termination
+                * to fix the encoding.
+                */
+               wpa_printf(MSG_DEBUG,  "WPS: Workaround - remove NULL "
+                          "termination from ASCII passphrase");
+               cred->key_len--;
+#endif /* CONFIG_WPS_STRICT */
+       }
+       return 0;
+}
+
+
+int wps_process_cred(struct wps_parse_attr *attr,
+                    struct wps_credential *cred)
+{
+       wpa_printf(MSG_DEBUG,  "WPS: Process Credential");
+
+       /* TODO: support multiple Network Keys */
+       if (wps_process_cred_network_idx(cred, attr->network_idx) ||
+           wps_process_cred_ssid(cred, attr->ssid, attr->ssid_len) ||
+           wps_process_cred_auth_type(cred, attr->auth_type) ||
+           wps_process_cred_encr_type(cred, attr->encr_type) ||
+           wps_process_cred_network_key_idx(cred, attr->network_key_idx) ||
+           wps_process_cred_network_key(cred, attr->network_key,
+                                        attr->network_key_len) ||
+           wps_process_cred_mac_addr(cred, attr->mac_addr) ||
+           wps_process_cred_eap_type(cred, attr->eap_type,
+                                     attr->eap_type_len) ||
+           wps_process_cred_eap_identity(cred, attr->eap_identity,
+                                         attr->eap_identity_len) ||
+           wps_process_cred_key_prov_auto(cred, attr->key_prov_auto) ||
+           wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled) ||
+           wps_process_cred_ap_channel(cred, attr->ap_channel))
+               return -1;
+
+       return wps_workaround_cred_key(cred);
+}
+
+
+int wps_process_ap_settings(struct wps_parse_attr *attr,
+                           struct wps_credential *cred)
+{
+       wpa_printf(MSG_DEBUG,  "WPS: Processing AP Settings");
+       os_memset(cred, 0, sizeof(*cred));
+       /* TODO: optional attributes New Password and Device Password ID */
+       if (wps_process_cred_ssid(cred, attr->ssid, attr->ssid_len) ||
+           wps_process_cred_auth_type(cred, attr->auth_type) ||
+           wps_process_cred_encr_type(cred, attr->encr_type) ||
+           wps_process_cred_network_key_idx(cred, attr->network_key_idx) ||
+           wps_process_cred_network_key(cred, attr->network_key,
+                                        attr->network_key_len) ||
+           wps_process_cred_mac_addr(cred, attr->mac_addr))
+               return -1;
+
+       return wps_workaround_cred_key(cred);
+}
\ No newline at end of file
diff --git a/components/wpa_supplicant/src/wps/wps_common.c b/components/wpa_supplicant/src/wps/wps_common.c
new file mode 100644 (file)
index 0000000..8a462a4
--- /dev/null
@@ -0,0 +1,671 @@
+/*
+ * Wi-Fi Protected Setup - common functionality
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#include <string.h>
+
+#include "wpa/includes.h"
+#include "wpa/common.h"
+
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/dh_group5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+
+#include "wps/wps_i.h"
+
+void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
+            const char *label, u8 *res, size_t res_len)
+{
+       u8 i_buf[4], key_bits[4];
+       const u8 *addr[4];
+       size_t len[4];
+       int i, iter;
+       u8 hash[SHA256_MAC_LEN], *opos;
+       size_t left;
+
+       WPA_PUT_BE32(key_bits, res_len * 8);
+
+       addr[0] = i_buf;
+       len[0] = sizeof(i_buf);
+       addr[1] = label_prefix;
+       len[1] = label_prefix_len;
+       addr[2] = (const u8 *) label;
+       len[2] = os_strlen(label);
+       addr[3] = key_bits;
+       len[3] = sizeof(key_bits);
+
+       iter = (res_len + SHA256_MAC_LEN - 1) / SHA256_MAC_LEN;
+       opos = res;
+       left = res_len;
+
+       for (i = 1; i <= iter; i++) {
+               WPA_PUT_BE32(i_buf, i);
+               if (wps_crypto_funcs.hmac_sha256_vector) {
+                       wps_crypto_funcs.hmac_sha256_vector(key, SHA256_MAC_LEN, 4, addr, (int *)len, hash);
+               } else {
+                       wpa_printf(MSG_ERROR, "In function %s, fail to reigster hmac sha256 vector function!\r\n", __FUNCTION__);
+                       return ;
+               }
+               if (i < iter) {
+                       os_memcpy(opos, hash, SHA256_MAC_LEN);
+                       opos += SHA256_MAC_LEN;
+                       left -= SHA256_MAC_LEN;
+               } else
+                       os_memcpy(opos, hash, left);
+       }
+}
+
+
+int wps_derive_keys(struct wps_data *wps)
+{
+       struct wpabuf *pubkey, *dh_shared;
+       u8 dhkey[SHA256_MAC_LEN], kdk[SHA256_MAC_LEN];
+       const u8 *addr[3];
+       size_t len[3];
+       u8 keys[WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN + WPS_EMSK_LEN];
+
+       if (wps->dh_privkey == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: Own DH private key not available");
+               return -1;
+       }
+
+       pubkey = wps->registrar ? wps->dh_pubkey_e : wps->dh_pubkey_r;
+       if (pubkey == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: Peer DH public key not available");
+               return -1;
+       }
+
+       wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey);
+       wpa_hexdump_buf(MSG_DEBUG, "WPS: DH peer Public Key", pubkey);
+       dh_shared = dh5_derive_shared(wps->dh_ctx, pubkey, wps->dh_privkey);
+       dh5_free(wps->dh_ctx);
+       wps->dh_ctx = NULL;
+       dh_shared = wpabuf_zeropad(dh_shared, 192);
+       if (dh_shared == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: Failed to derive DH shared key");
+               return -1;
+       }
+
+       /* Own DH private key is not needed anymore */
+/*
+ * due to the public key calculated when wps start, it will not calculate anymore even when we build M1 message, also calculate the key need take a long time
+ * which would cause WPS fail, so we clean the key after WPS finished .
+ */
+#ifndef ESP32_WORKAROUND
+       wpabuf_free(wps->dh_privkey);
+       wps->dh_privkey = NULL;
+#endif //ESP32_WORKAROUND
+
+       wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH shared key", dh_shared);
+
+       /* DHKey = SHA-256(g^AB mod p) */
+       addr[0] = wpabuf_head(dh_shared);
+       len[0] = wpabuf_len(dh_shared);
+
+       if (wps_crypto_funcs.sha256_vector) {
+               wps_crypto_funcs.sha256_vector(1, addr, (int *)len, dhkey);
+       } else {
+               wpa_printf(MSG_ERROR, "In function %s, Fail to register sha256 vector function!\r\n", __FUNCTION__);
+               return -1;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "WPS: DHKey", dhkey, sizeof(dhkey));
+       wpabuf_free(dh_shared);
+
+       /* KDK = HMAC-SHA-256_DHKey(N1 || EnrolleeMAC || N2) */
+       addr[0] = wps->nonce_e;
+       len[0] = WPS_NONCE_LEN;
+       addr[1] = wps->mac_addr_e;
+       len[1] = ETH_ALEN;
+       addr[2] = wps->nonce_r;
+       len[2] = WPS_NONCE_LEN;
+       if (wps_crypto_funcs.hmac_sha256_vector) {
+               wps_crypto_funcs.hmac_sha256_vector(dhkey, sizeof(dhkey), 3, addr, (int *)len, kdk);
+       } else {
+               wpa_printf(MSG_ERROR, "In function %s, Fail to register hmac sha256 vector function!\r\n", __FUNCTION__);
+               return -1;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "WPS: KDK", kdk, sizeof(kdk));
+
+       wps_kdf(kdk, NULL, 0, "Wi-Fi Easy and Secure Key Derivation",
+               keys, sizeof(keys));
+       os_memcpy(wps->authkey, keys, WPS_AUTHKEY_LEN);
+       os_memcpy(wps->keywrapkey, keys + WPS_AUTHKEY_LEN, WPS_KEYWRAPKEY_LEN);
+       os_memcpy(wps->emsk, keys + WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN,
+                 WPS_EMSK_LEN);
+
+       wpa_hexdump_key(MSG_DEBUG, "WPS: AuthKey",
+                       wps->authkey, WPS_AUTHKEY_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "WPS: KeyWrapKey",
+                       wps->keywrapkey, WPS_KEYWRAPKEY_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "WPS: EMSK", wps->emsk, WPS_EMSK_LEN);
+
+       return 0;
+}
+
+
+void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
+                   size_t dev_passwd_len)
+{
+       u8 hash[SHA256_MAC_LEN];
+
+       if (wps_crypto_funcs.hmac_sha256) {
+               wps_crypto_funcs.hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, dev_passwd,
+                                            (dev_passwd_len + 1) / 2, hash);
+       } else {
+               wpa_printf(MSG_ERROR, "In function %s, fail to register hmac_sha256 function!\r\n", __FUNCTION__);
+               return ;
+       }
+       os_memcpy(wps->psk1, hash, WPS_PSK_LEN);
+       if (wps_crypto_funcs.hmac_sha256) {
+               wps_crypto_funcs.hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN,
+                                            dev_passwd + (dev_passwd_len + 1) / 2,
+                                            dev_passwd_len / 2, hash);
+       } else {
+               wpa_printf(MSG_ERROR, "In function %s, fail to register hmac_sha256 function!\r\n", __FUNCTION__);
+               return ;
+       }
+       os_memcpy(wps->psk2, hash, WPS_PSK_LEN);
+
+       wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Device Password",
+                             dev_passwd, dev_passwd_len);
+       wpa_hexdump_key(MSG_DEBUG, "WPS: PSK1", wps->psk1, WPS_PSK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "WPS: PSK2", wps->psk2, WPS_PSK_LEN);
+}
+
+
+struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
+                                         size_t encr_len)
+{
+       struct wpabuf *decrypted;
+       const size_t block_size = 16;
+       size_t i;
+       u8 pad;
+       const u8 *pos;
+
+       /* AES-128-CBC */
+       if (encr == NULL || encr_len < 2 * block_size || encr_len % block_size)
+       {
+               wpa_printf(MSG_DEBUG,  "WPS: No Encrypted Settings received");
+               return NULL;
+       }
+
+       decrypted = wpabuf_alloc(encr_len - block_size);
+       if (decrypted == NULL)
+               return NULL;
+
+       wpa_hexdump(MSG_MSGDUMP, "WPS: Encrypted Settings", encr, encr_len);
+       wpabuf_put_data(decrypted, encr + block_size, encr_len - block_size);
+       wpa_printf(MSG_DEBUG,  "WPS: AES Decrypt setting");
+       if (wps_crypto_funcs.aes_128_decrypt) {
+               if (wps_crypto_funcs.aes_128_decrypt(wps->keywrapkey, encr, wpabuf_mhead(decrypted),
+                                                    wpabuf_len(decrypted))) {
+                       wpabuf_free(decrypted);
+                       return NULL;
+           }
+       } else {
+                wpa_printf(MSG_ERROR, "In function %s, fail to register aes 128 decrypt function!\r\n", __FUNCTION__);
+               return NULL;
+       }
+
+       wpa_hexdump_buf_key(MSG_MSGDUMP, "WPS: Decrypted Encrypted Settings",
+                           decrypted);
+
+       pos = wpabuf_head_u8(decrypted) + wpabuf_len(decrypted) - 1;
+       pad = *pos;
+       if (pad > wpabuf_len(decrypted)) {
+               wpa_printf(MSG_DEBUG,  "WPS: Invalid PKCS#5 v2.0 pad value");
+               wpabuf_free(decrypted);
+               return NULL;
+       }
+       for (i = 0; i < pad; i++) {
+               if (*pos-- != pad) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalid PKCS#5 v2.0 pad "
+                                  "string");
+                       wpabuf_free(decrypted);
+                       return NULL;
+               }
+       }
+       decrypted->used -= pad;
+
+       return decrypted;
+}
+
+#ifdef CONFIG_WPS_PIN
+/**
+ * wps_pin_checksum - Compute PIN checksum
+ * @pin: Seven digit PIN (i.e., eight digit PIN without the checksum digit)
+ * Returns: Checksum digit
+ */
+unsigned int wps_pin_checksum(unsigned int pin)
+{
+       unsigned int accum = 0;
+       while (pin) {
+               accum += 3 * (pin % 10);
+               pin /= 10;
+               accum += pin % 10;
+               pin /= 10;
+       }
+
+       return (10 - accum % 10) % 10;
+}
+
+
+/**
+ * wps_pin_valid - Check whether a PIN has a valid checksum
+ * @pin: Eight digit PIN (i.e., including the checksum digit)
+ * Returns: 1 if checksum digit is valid, or 0 if not
+ */
+unsigned int wps_pin_valid(unsigned int pin)
+{
+       return wps_pin_checksum(pin / 10) == (pin % 10);
+}
+
+
+/**
+ * wps_generate_pin - Generate a random PIN
+ * Returns: Eight digit PIN (i.e., including the checksum digit)
+ */
+unsigned int wps_generate_pin(void)
+{
+       unsigned int val;
+
+       /* Generate seven random digits for the PIN */
+       if (random_get_bytes((unsigned char *) &val, sizeof(val)) < 0) {
+               return -1;
+       }
+       val %= 10000000;
+
+       /* Append checksum digit */
+       return val * 10 + wps_pin_checksum(val);
+}
+
+
+int wps_pin_str_valid(const char *pin)
+{
+       const char *p;
+       size_t len;
+
+       p = pin;
+       while (*p >= '0' && *p <= '9')
+               p++;
+       if (*p != '\0')
+               return 0;
+
+       len = p - pin;
+       return len == 4 || len == 8;
+}
+#endif
+
+void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
+                   u16 config_error, u16 error_indication)
+{
+       union wps_event_data *data;
+
+        data = (union wps_event_data *)os_zalloc(sizeof(union wps_event_data));
+       if (data == NULL)
+               return;
+
+       if (wps->event_cb == NULL) {
+               os_free(data);
+               return;
+       }
+
+       os_memset(data, 0, sizeof(union wps_event_data));
+       data->fail.msg = msg;
+       data->fail.config_error = config_error;
+       data->fail.error_indication = error_indication;
+       wps->event_cb(wps->cb_ctx, WPS_EV_FAIL, data);
+       os_free(data);
+}
+
+
+void wps_success_event(struct wps_context *wps)
+{
+       if (wps->event_cb == NULL)
+               return;
+
+       wps->event_cb(wps->cb_ctx, WPS_EV_SUCCESS, NULL);
+}
+
+
+void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part)
+{
+       union wps_event_data *data;
+
+        data = (union wps_event_data *)os_zalloc(sizeof(union wps_event_data));
+       if (data == NULL)
+               return;
+
+       if (wps->event_cb == NULL) {
+               os_free(data);
+               return;
+       }
+
+       os_memset(data, 0, sizeof(union wps_event_data));
+       data->pwd_auth_fail.enrollee = enrollee;
+       data->pwd_auth_fail.part = part;
+       wps->event_cb(wps->cb_ctx, WPS_EV_PWD_AUTH_FAIL, data);
+       os_free(data);
+}
+
+
+void wps_pbc_overlap_event(struct wps_context *wps)
+{
+       if (wps->event_cb == NULL)
+               return;
+
+       wps->event_cb(wps->cb_ctx, WPS_EV_PBC_OVERLAP, NULL);
+}
+
+
+void wps_pbc_timeout_event(struct wps_context *wps)
+{
+       if (wps->event_cb == NULL)
+               return;
+
+       wps->event_cb(wps->cb_ctx, WPS_EV_PBC_TIMEOUT, NULL);
+}
+
+
+#ifdef CONFIG_WPS_OOB
+
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
+{
+       struct wps_data *data;
+       struct wpabuf *plain;
+
+       data = (struct wps_data *)os_zalloc(sizeof(struct wps_data));
+       if (data == NULL)
+               return NULL;
+
+       plain = wpabuf_alloc(500);
+       if (plain == NULL) {
+               os_free(data);
+               wpa_printf(MSG_ERROR,  "WPS: Failed to allocate memory for OOB "
+                          "credential");
+               return NULL;
+       }
+
+       os_memset(data, 0, sizeof(struct wps_data));
+       data->wps = wps;
+       data->auth_type = wps->auth_types;
+       data->encr_type = wps->encr_types;
+       if (wps_build_version(plain) ||
+           wps_build_cred(data, plain) ||
+           wps_build_wfa_ext(plain, 0, NULL, 0)) {
+               wpabuf_free(plain);
+               os_free(data);
+               return NULL;
+       }
+
+       os_free(data);
+       return plain;
+}
+
+#ifdef CONFIG_WPS_NFC
+
+struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id,
+                                      const struct wpabuf *pubkey,
+                                      const struct wpabuf *dev_pw)
+{
+       struct wpabuf *data;
+
+       data = wpabuf_alloc(200);
+       if (data == NULL)
+               return NULL;
+
+       if (wps_build_version(data) ||
+           wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
+                                wpabuf_head(dev_pw), wpabuf_len(dev_pw)) ||
+           wps_build_wfa_ext(data, 0, NULL, 0)) {
+               wpa_printf(MSG_ERROR,  "WPS: Failed to build NFC password "
+                          "token");
+               wpabuf_free(data);
+               return NULL;
+       }
+
+       return data;
+}
+
+#endif
+
+int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr)
+{
+       struct wpabuf msg;
+       size_t i;
+
+       for (i = 0; i < attr->num_cred; i++) {
+               struct wps_credential local_cred;
+               struct wps_parse_attr cattr;
+
+               os_memset(&local_cred, 0, sizeof(local_cred));
+               wpabuf_set(&msg, attr->cred[i], attr->cred_len[i]);
+               if (wps_parse_msg(&msg, &cattr) < 0 ||
+                   wps_process_cred(&cattr, &local_cred)) {
+                       wpa_printf(MSG_ERROR,  "WPS: Failed to parse OOB "
+                                  "credential");
+                       return -1;
+               }
+               wps->cred_cb(wps->cb_ctx, &local_cred);
+       }
+
+       return 0;
+}
+
+
+#endif /* CONFIG_WPS_OOB */
+
+
+int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN])
+{
+       const char *pos;
+
+       /* <categ>-<OUI>-<subcateg> */
+       WPA_PUT_BE16(dev_type, atoi(str));
+       pos = (char *)os_strchr(str, '-');
+       if (pos == NULL)
+               return -1;
+       pos++;
+       if (hexstr2bin(pos, &dev_type[2], 4))
+               return -1;
+       pos = (char *)os_strchr(pos, '-');
+       if (pos == NULL)
+               return -1;
+       pos++;
+       WPA_PUT_BE16(&dev_type[6], atoi(pos));
+
+
+       return 0;
+}
+
+
+char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
+                           size_t buf_len)
+{
+       int ret;
+
+       ret = snprintf(buf, buf_len, "%u-%08X-%u",
+                         WPA_GET_BE16(dev_type), WPA_GET_BE32(&dev_type[2]),
+                         WPA_GET_BE16(&dev_type[6]));
+       if (ret < 0 || (unsigned int) ret >= buf_len)
+               return NULL;
+
+       return buf;
+}
+
+
+void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid)
+{
+       const u8 *addr[2];
+       size_t len[2];
+       u8 hash[SHA1_MAC_LEN];
+       u8 nsid[16] = {
+               0x52, 0x64, 0x80, 0xf8,
+               0xc9, 0x9b,
+               0x4b, 0xe5,
+               0xa6, 0x55,
+               0x58, 0xed, 0x5f, 0x5d, 0x60, 0x84
+       };
+
+       addr[0] = nsid;
+       len[0] = sizeof(nsid);
+       addr[1] = mac_addr;
+       len[1] = 6;
+       sha1_vector(2, addr, len, hash);
+       os_memcpy(uuid, hash, 16);
+
+       /* Version: 5 = named-based version using SHA-1 */
+       uuid[6] = (5 << 4) | (uuid[6] & 0x0f);
+
+       /* Variant specified in RFC 4122 */
+       uuid[8] = 0x80 | (uuid[8] & 0x3f);
+}
+
+
+u16 wps_config_methods_str2bin(const char *str)
+{
+       u16 methods = 0;
+
+       if (str == NULL) {
+               /* Default to enabling methods based on build configuration */
+               methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+#ifdef CONFIG_WPS2
+               methods |= WPS_CONFIG_VIRT_DISPLAY;
+#endif /* CONFIG_WPS2 */
+#ifdef CONFIG_WPS_NFC
+               methods |= WPS_CONFIG_NFC_INTERFACE;
+#endif /* CONFIG_WPS_NFC */
+       } else {
+               if (os_strstr(str, "ethernet"))
+                       methods |= WPS_CONFIG_ETHERNET;
+               if (os_strstr(str, "label"))
+                       methods |= WPS_CONFIG_LABEL;
+               if (os_strstr(str, "display"))
+                       methods |= WPS_CONFIG_DISPLAY;
+               if (os_strstr(str, "ext_nfc_token"))
+                       methods |= WPS_CONFIG_EXT_NFC_TOKEN;
+               if (os_strstr(str, "int_nfc_token"))
+                       methods |= WPS_CONFIG_INT_NFC_TOKEN;
+               if (os_strstr(str, "nfc_interface"))
+                       methods |= WPS_CONFIG_NFC_INTERFACE;
+               if (os_strstr(str, "push_button"))
+                       methods |= WPS_CONFIG_PUSHBUTTON;
+               if (os_strstr(str, "keypad"))
+                       methods |= WPS_CONFIG_KEYPAD;
+#ifdef CONFIG_WPS2
+               if (os_strstr(str, "virtual_display"))
+                       methods |= WPS_CONFIG_VIRT_DISPLAY;
+               if (os_strstr(str, "physical_display"))
+                       methods |= WPS_CONFIG_PHY_DISPLAY;
+               if (os_strstr(str, "virtual_push_button"))
+                       methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+               if (os_strstr(str, "physical_push_button"))
+                       methods |= WPS_CONFIG_PHY_PUSHBUTTON;
+#endif /* CONFIG_WPS2 */
+       }
+
+       return methods;
+}
+
+
+struct wpabuf * wps_build_wsc_ack(struct wps_data *wps)
+{
+       struct wpabuf *msg;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Building Message WSC_ACK");
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL)
+               return NULL;
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_WSC_ACK) ||
+           wps_build_enrollee_nonce(wps, msg) ||
+           wps_build_registrar_nonce(wps, msg) ||
+           wps_build_wfa_ext(msg, 0, NULL, 0)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       return msg;
+}
+
+
+struct wpabuf * wps_build_wsc_nack(struct wps_data *wps)
+{
+       struct wpabuf *msg;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Building Message WSC_NACK");
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL)
+               return NULL;
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_WSC_NACK) ||
+           wps_build_enrollee_nonce(wps, msg) ||
+           wps_build_registrar_nonce(wps, msg) ||
+           wps_build_config_error(msg, wps->config_error) ||
+           wps_build_wfa_ext(msg, 0, NULL, 0)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       return msg;
+}
+
+
+#ifdef CONFIG_WPS_NFC
+struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
+                                 struct wpabuf **privkey,
+                                 struct wpabuf **dev_pw)
+{
+       struct wpabuf *priv = NULL, *pub = NULL, *pw, *ret;
+       void *dh_ctx;
+       u16 val;
+
+       pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN);
+       if (pw == NULL)
+               return NULL;
+
+       if (random_get_bytes(wpabuf_put(pw, WPS_OOB_DEVICE_PASSWORD_LEN),
+                            WPS_OOB_DEVICE_PASSWORD_LEN) ||
+           random_get_bytes((u8 *) &val, sizeof(val))) {
+               wpabuf_free(pw);
+               return NULL;
+       }
+
+       dh_ctx = dh5_init(&priv, &pub);
+       if (dh_ctx == NULL) {
+               wpabuf_free(pw);
+               return NULL;
+       }
+       dh5_free(dh_ctx);
+
+       *id = 0x10 + val % 0xfff0;
+       wpabuf_free(*pubkey);
+       *pubkey = pub;
+       wpabuf_free(*privkey);
+       *privkey = priv;
+       wpabuf_free(*dev_pw);
+       *dev_pw = pw;
+
+       ret = wps_build_nfc_pw_token(*id, *pubkey, *dev_pw);
+       if (ndef && ret) {
+               struct wpabuf *tmp;
+               tmp = ndef_build_wifi(ret);
+               wpabuf_free(ret);
+               if (tmp == NULL)
+                       return NULL;
+               ret = tmp;
+       }
+
+       return ret;
+}
+#endif /* CONFIG_WPS_NFC */
diff --git a/components/wpa_supplicant/src/wps/wps_dev_attr.c b/components/wpa_supplicant/src/wps/wps_dev_attr.c
new file mode 100644 (file)
index 0000000..89a14c9
--- /dev/null
@@ -0,0 +1,451 @@
+/*
+ * Wi-Fi Protected Setup - device attributes
+ * 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 "wpa/includes.h"
+#include "wpa/common.h"
+
+#include "wps/wps_i.h"
+#include "wps/wps_dev_attr.h"
+
+int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg)
+{
+       size_t len;
+       wpa_printf(MSG_DEBUG,  "WPS:  * Manufacturer");
+       wpabuf_put_be16(msg, ATTR_MANUFACTURER);
+       len = dev->manufacturer ? os_strlen(dev->manufacturer) : 0;
+#ifndef CONFIG_WPS_STRICT
+       if (len == 0) {
+               /*
+                * Some deployed WPS implementations fail to parse zero-length
+                * attributes. As a workaround, send a space character if the
+                * device attribute string is empty.
+                */
+               wpabuf_put_be16(msg, 1);
+               wpabuf_put_u8(msg, ' ');
+               return 0;
+       }
+#endif /* CONFIG_WPS_STRICT */
+       wpabuf_put_be16(msg, len);
+       wpabuf_put_data(msg, dev->manufacturer, len);
+       return 0;
+}
+
+
+int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg)
+{
+       size_t len;
+       wpa_printf(MSG_DEBUG,  "WPS:  * Model Name");
+       wpabuf_put_be16(msg, ATTR_MODEL_NAME);
+       len = dev->model_name ? os_strlen(dev->model_name) : 0;
+#ifndef CONFIG_WPS_STRICT
+       if (len == 0) {
+               /*
+                * Some deployed WPS implementations fail to parse zero-length
+                * attributes. As a workaround, send a space character if the
+                * device attribute string is empty.
+                */
+               wpabuf_put_be16(msg, 1);
+               wpabuf_put_u8(msg, ' ');
+               return 0;
+       }
+#endif /* CONFIG_WPS_STRICT */
+       wpabuf_put_be16(msg, len);
+       wpabuf_put_data(msg, dev->model_name, len);
+       return 0;
+}
+
+
+int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg)
+{
+       size_t len;
+       wpa_printf(MSG_DEBUG,  "WPS:  * Model Number");
+       wpabuf_put_be16(msg, ATTR_MODEL_NUMBER);
+       len = dev->model_number ? os_strlen(dev->model_number) : 0;
+#ifndef CONFIG_WPS_STRICT
+       if (len == 0) {
+               /*
+                * Some deployed WPS implementations fail to parse zero-length
+                * attributes. As a workaround, send a space character if the
+                * device attribute string is empty.
+                */
+               wpabuf_put_be16(msg, 1);
+               wpabuf_put_u8(msg, ' ');
+               return 0;
+       }
+#endif /* CONFIG_WPS_STRICT */
+       wpabuf_put_be16(msg, len);
+       wpabuf_put_data(msg, dev->model_number, len);
+       return 0;
+}
+
+
+static int wps_build_serial_number(struct wps_device_data *dev,
+                                  struct wpabuf *msg)
+{
+       size_t len;
+       wpa_printf(MSG_DEBUG,  "WPS:  * Serial Number");
+       wpabuf_put_be16(msg, ATTR_SERIAL_NUMBER);
+       len = dev->serial_number ? os_strlen(dev->serial_number) : 0;
+#ifndef CONFIG_WPS_STRICT
+       if (len == 0) {
+               /*
+                * Some deployed WPS implementations fail to parse zero-length
+                * attributes. As a workaround, send a space character if the
+                * device attribute string is empty.
+                */
+               wpabuf_put_be16(msg, 1);
+               wpabuf_put_u8(msg, ' ');
+               return 0;
+       }
+#endif /* CONFIG_WPS_STRICT */
+       wpabuf_put_be16(msg, len);
+       wpabuf_put_data(msg, dev->serial_number, len);
+       return 0;
+}
+
+
+int wps_build_primary_dev_type(struct wps_device_data *dev, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * Primary Device Type");
+       wpabuf_put_be16(msg, ATTR_PRIMARY_DEV_TYPE);
+       wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN);
+       wpabuf_put_data(msg, dev->pri_dev_type, WPS_DEV_TYPE_LEN);
+       return 0;
+}
+
+
+int wps_build_secondary_dev_type(struct wps_device_data *dev,
+                                 struct wpabuf *msg)
+{
+       if (!dev->num_sec_dev_types)
+               return 0;
+
+       wpa_printf(MSG_DEBUG,  "WPS:  * Secondary Device Type");
+       wpabuf_put_be16(msg, ATTR_SECONDARY_DEV_TYPE_LIST);
+       wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN * dev->num_sec_dev_types);
+       wpabuf_put_data(msg, dev->sec_dev_type,
+                       WPS_DEV_TYPE_LEN * dev->num_sec_dev_types);
+
+       return 0;
+}
+
+
+int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg,
+                          unsigned int num_req_dev_types,
+                          const u8 *req_dev_types)
+{
+       unsigned int i;
+
+       for (i = 0; i < num_req_dev_types; i++) {
+               wpa_hexdump(MSG_DEBUG, "WPS: * Requested Device Type",
+                           req_dev_types + i * WPS_DEV_TYPE_LEN,
+                           WPS_DEV_TYPE_LEN);
+               wpabuf_put_be16(msg, ATTR_REQUESTED_DEV_TYPE);
+               wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN);
+               wpabuf_put_data(msg, req_dev_types + i * WPS_DEV_TYPE_LEN,
+                               WPS_DEV_TYPE_LEN);
+       }
+
+       return 0;
+}
+
+
+int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg)
+{
+       size_t len;
+       wpa_printf(MSG_DEBUG,  "WPS:  * Device Name");
+       wpabuf_put_be16(msg, ATTR_DEV_NAME);
+       len = dev->device_name ? os_strlen(dev->device_name) : 0;
+#ifndef CONFIG_WPS_STRICT
+       if (len == 0) {
+               /*
+                * Some deployed WPS implementations fail to parse zero-length
+                * attributes. As a workaround, send a space character if the
+                * device attribute string is empty.
+                */
+               wpabuf_put_be16(msg, 1);
+               wpabuf_put_u8(msg, ' ');
+               return 0;
+       }
+#endif /* CONFIG_WPS_STRICT */
+       wpabuf_put_be16(msg, len);
+       wpabuf_put_data(msg, dev->device_name, len);
+       return 0;
+}
+
+
+int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg)
+{
+       if (wps_build_manufacturer(dev, msg) ||
+           wps_build_model_name(dev, msg) ||
+           wps_build_model_number(dev, msg) ||
+           wps_build_serial_number(dev, msg) ||
+           wps_build_primary_dev_type(dev, msg) ||
+           wps_build_dev_name(dev, msg))
+               return -1;
+       return 0;
+}
+
+
+int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * OS Version");
+       wpabuf_put_be16(msg, ATTR_OS_VERSION);
+       wpabuf_put_be16(msg, 4);
+       wpabuf_put_be32(msg, 0x80000000 | dev->os_version);
+       return 0;
+}
+
+
+int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg)
+{
+       if (dev->vendor_ext_m1 != NULL) {
+               wpa_hexdump(MSG_DEBUG, "WPS:  * Vendor Extension M1",
+                           wpabuf_head_u8(dev->vendor_ext_m1),
+                           wpabuf_len(dev->vendor_ext_m1));
+               wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
+               wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext_m1));
+               wpabuf_put_buf(msg, dev->vendor_ext_m1);
+       }
+       return 0;
+}
+
+
+int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * RF Bands (%x)", dev->rf_bands);
+       wpabuf_put_be16(msg, ATTR_RF_BANDS);
+       wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, dev->rf_bands);
+       return 0;
+}
+
+
+int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg)
+{
+       int i;
+
+       for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+               if (dev->vendor_ext[i] == NULL)
+                       continue;
+               wpa_hexdump(MSG_DEBUG, "WPS:  * Vendor Extension",
+                           wpabuf_head_u8(dev->vendor_ext[i]),
+                           wpabuf_len(dev->vendor_ext[i]));
+               wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
+               wpabuf_put_be16(msg, wpabuf_len(dev->vendor_ext[i]));
+               wpabuf_put_buf(msg, dev->vendor_ext[i]);
+       }
+
+       return 0;
+}
+
+
+static int wps_process_manufacturer(struct wps_device_data *dev, const u8 *str,
+                                   size_t str_len)
+{
+       if (str == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Manufacturer received");
+               return -1;
+       }
+
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer", str, str_len);
+
+       os_free(dev->manufacturer);
+       dev->manufacturer = (char *)os_malloc(str_len + 1);
+       if (dev->manufacturer == NULL)
+               return -1;
+       os_memcpy(dev->manufacturer, str, str_len);
+       dev->manufacturer[str_len] = '\0';
+
+       return 0;
+}
+
+
+static int wps_process_model_name(struct wps_device_data *dev, const u8 *str,
+                                 size_t str_len)
+{
+       if (str == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Model Name received");
+               return -1;
+       }
+
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name", str, str_len);
+
+       os_free(dev->model_name);
+       dev->model_name = (char *)os_malloc(str_len + 1);
+       if (dev->model_name == NULL)
+               return -1;
+       os_memcpy(dev->model_name, str, str_len);
+       dev->model_name[str_len] = '\0';
+
+       return 0;
+}
+
+
+static int wps_process_model_number(struct wps_device_data *dev, const u8 *str,
+                                   size_t str_len)
+{
+       if (str == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Model Number received");
+               return -1;
+       }
+
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number", str, str_len);
+
+       os_free(dev->model_number);
+       dev->model_number = (char *)os_malloc(str_len + 1);
+       if (dev->model_number == NULL)
+               return -1;
+       os_memcpy(dev->model_number, str, str_len);
+       dev->model_number[str_len] = '\0';
+
+       return 0;
+}
+
+
+static int wps_process_serial_number(struct wps_device_data *dev,
+                                    const u8 *str, size_t str_len)
+{
+       if (str == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Serial Number received");
+               return -1;
+       }
+
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number", str, str_len);
+
+       os_free(dev->serial_number);
+       dev->serial_number = (char *)os_malloc(str_len + 1);
+       if (dev->serial_number == NULL)
+               return -1;
+       os_memcpy(dev->serial_number, str, str_len);
+       dev->serial_number[str_len] = '\0';
+
+       return 0;
+}
+
+
+static int wps_process_dev_name(struct wps_device_data *dev, const u8 *str,
+                               size_t str_len)
+{
+       if (str == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Device Name received");
+               return -1;
+       }
+
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name", str, str_len);
+
+       os_free(dev->device_name);
+       dev->device_name = (char *)os_malloc(str_len + 1);
+       if (dev->device_name == NULL)
+               return -1;
+       os_memcpy(dev->device_name, str, str_len);
+       dev->device_name[str_len] = '\0';
+
+       return 0;
+}
+
+
+static int wps_process_primary_dev_type(struct wps_device_data *dev,
+                                       const u8 *dev_type)
+{
+#if 0
+#ifndef CONFIG_NO_STDOUT_DEBUG
+       char devtype[WPS_DEV_TYPE_BUFSIZE];
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+#endif
+       if (dev_type == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Primary Device Type received");
+               return -1;
+       }
+
+       os_memcpy(dev->pri_dev_type, dev_type, WPS_DEV_TYPE_LEN);
+       //wpa_printf(MSG_DEBUG,  "WPS: Primary Device Type: %s",
+       //         wps_dev_type_bin2str(dev->pri_dev_type, devtype,
+       //                              sizeof(devtype)));
+
+       return 0;
+}
+
+
+int wps_process_device_attrs(struct wps_device_data *dev,
+                            struct wps_parse_attr *attr)
+{
+       if (wps_process_manufacturer(dev, attr->manufacturer,
+                                    attr->manufacturer_len) ||
+           wps_process_model_name(dev, attr->model_name,
+                                  attr->model_name_len) ||
+           wps_process_model_number(dev, attr->model_number,
+                                    attr->model_number_len) ||
+           wps_process_serial_number(dev, attr->serial_number,
+                                     attr->serial_number_len) ||
+           wps_process_primary_dev_type(dev, attr->primary_dev_type) ||
+           wps_process_dev_name(dev, attr->dev_name, attr->dev_name_len))
+               return -1;
+       return 0;
+}
+
+
+int wps_process_os_version(struct wps_device_data *dev, const u8 *ver)
+{
+       if (ver == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No OS Version received");
+               return -1;
+       }
+
+       dev->os_version = WPA_GET_BE32(ver);
+       wpa_printf(MSG_DEBUG,  "WPS: OS Version %08x", dev->os_version);
+
+       return 0;
+}
+
+
+int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands)
+{
+       if (bands == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No RF Bands received");
+               return -1;
+       }
+
+       dev->rf_bands = *bands;
+       wpa_printf(MSG_DEBUG,  "WPS: Enrollee RF Bands 0x%x", dev->rf_bands);
+
+       return 0;
+}
+
+
+void wps_device_data_dup(struct wps_device_data *dst,
+                        const struct wps_device_data *src)
+{
+       if (src->device_name)
+               dst->device_name = os_strdup(src->device_name);
+       if (src->manufacturer)
+               dst->manufacturer = os_strdup(src->manufacturer);
+       if (src->model_name)
+               dst->model_name = os_strdup(src->model_name);
+       if (src->model_number)
+               dst->model_number = os_strdup(src->model_number);
+       if (src->serial_number)
+               dst->serial_number = os_strdup(src->serial_number);
+       os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN);
+       dst->os_version = src->os_version;
+       dst->rf_bands = src->rf_bands;
+}
+
+
+void wps_device_data_free(struct wps_device_data *dev)
+{
+       os_free(dev->device_name);
+       dev->device_name = NULL;
+       os_free(dev->manufacturer);
+       dev->manufacturer = NULL;
+       os_free(dev->model_name);
+       dev->model_name = NULL;
+       os_free(dev->model_number);
+       dev->model_number = NULL;
+       os_free(dev->serial_number);
+       dev->serial_number = NULL;
+}
diff --git a/components/wpa_supplicant/src/wps/wps_enrollee.c b/components/wpa_supplicant/src/wps/wps_enrollee.c
new file mode 100644 (file)
index 0000000..6a4f9bd
--- /dev/null
@@ -0,0 +1,1574 @@
+/*
+ * Wi-Fi Protected Setup - Enrollee
+ * 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 "rom/ets_sys.h"
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "wps/wps_i.h"
+#include "wps/wps.h"
+#include "wps/wps_dev_attr.h"
+
+#include "soc/dport_reg.h"
+
+
+static int wps_build_mac_addr(struct wps_data *wps, struct wpabuf *msg) {
+       wpa_printf(MSG_DEBUG,  "WPS:  * MAC Address");
+       wpabuf_put_be16(msg, ATTR_MAC_ADDR);
+       wpabuf_put_be16(msg, ETH_ALEN);
+       wpabuf_put_data(msg, wps->mac_addr_e, ETH_ALEN);
+       return 0;
+}
+
+
+static int wps_build_wps_state(struct wps_data *wps, struct wpabuf *msg)
+{
+       u8 state;
+       if (wps->wps->ap)
+               state = wps->wps->wps_state;
+       else
+               state = WPS_STATE_NOT_CONFIGURED;
+       wpa_printf(MSG_DEBUG,  "WPS:  * Wi-Fi Protected Setup State (%d)",
+                  state);
+       wpabuf_put_be16(msg, ATTR_WPS_STATE);
+       wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, state);
+       return 0;
+}
+
+
+static int wps_build_e_hash(struct wps_data *wps, struct wpabuf *msg)
+{
+       u8 *hash;
+       const u8 *addr[4];
+       size_t len[4];
+
+       if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
+               return -1;
+       wpa_hexdump(MSG_DEBUG, "WPS: E-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: E-S2",
+                   wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN);
+
+       if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: DH public keys not available for "
+                          "E-Hash derivation");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG,  "WPS:  * E-Hash1");
+       wpabuf_put_be16(msg, ATTR_E_HASH1);
+       wpabuf_put_be16(msg, SHA256_MAC_LEN);
+       hash = wpabuf_put(msg, SHA256_MAC_LEN);
+       /* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */
+       addr[0] = wps->snonce;
+       len[0] = WPS_SECRET_NONCE_LEN;
+       addr[1] = wps->psk1;
+       len[1] = WPS_PSK_LEN;
+       addr[2] = wpabuf_head(wps->dh_pubkey_e);
+       len[2] = wpabuf_len(wps->dh_pubkey_e);
+       addr[3] = wpabuf_head(wps->dh_pubkey_r);
+       len[3] = wpabuf_len(wps->dh_pubkey_r);
+       if (wps_crypto_funcs.hmac_sha256_vector) {
+               wps_crypto_funcs.hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, (int *)len, hash);
+       } else {
+               wpa_printf(MSG_ERROR, "In function %s, fail to register hmac_sha256_vector function!\r\n", __FUNCTION__);
+               return -1;
+       }
+       wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", hash, SHA256_MAC_LEN);
+
+       wpa_printf(MSG_DEBUG,  "WPS:  * E-Hash2");
+       wpabuf_put_be16(msg, ATTR_E_HASH2);
+       wpabuf_put_be16(msg, SHA256_MAC_LEN);
+       hash = wpabuf_put(msg, SHA256_MAC_LEN);
+       /* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */
+       addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN;
+       addr[1] = wps->psk2;
+       if (wps_crypto_funcs.hmac_sha256_vector) {
+               wps_crypto_funcs.hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, (int *)len, hash);
+       } else {
+               wpa_printf(MSG_ERROR, "In function %s, fail to register hmac_sha256_vector function!\r\n", __FUNCTION__);
+               return -1;
+       }
+       wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", hash, SHA256_MAC_LEN);
+
+       return 0;
+}
+
+
+static int wps_build_e_snonce1(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * E-SNonce1");
+       wpabuf_put_be16(msg, ATTR_E_SNONCE1);
+       wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+       wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN);
+       return 0;
+}
+
+
+static int wps_build_e_snonce2(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * E-SNonce2");
+       wpabuf_put_be16(msg, ATTR_E_SNONCE2);
+       wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+       wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN,
+                       WPS_SECRET_NONCE_LEN);
+       return 0;
+}
+
+
+static struct wpabuf * wps_build_m1(struct wps_data *wps)
+{
+       struct wpabuf *msg;
+       u16 config_methods;
+
+       if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0)
+               return NULL;
+       wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
+                   wps->nonce_e, WPS_NONCE_LEN);
+
+       wpa_printf(MSG_DEBUG,  "WPS: Building Message M1");
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL)
+               return NULL;
+
+       config_methods = wps->wps->config_methods;
+       if (wps->wps->ap && !wps->pbc_in_m1 &&
+           (wps->dev_password_len != 0 ||
+            (config_methods & WPS_CONFIG_DISPLAY))) {
+               /*
+                * These are the methods that the AP supports as an Enrollee
+                * for adding external Registrars, so remove PushButton.
+                *
+                * As a workaround for Windows 7 mechanism for probing WPS
+                * capabilities from M1, leave PushButton option if no PIN
+                * method is available or if WPS configuration enables PBC
+                * workaround.
+                */
+               config_methods &= ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+               config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+                                   WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
+       }
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_M1) ||
+           wps_build_uuid_e(msg, wps->uuid_e) ||
+           wps_build_mac_addr(wps, msg) ||
+           wps_build_enrollee_nonce(wps, msg) ||
+           wps_build_public_key(wps, msg, WPS_CALC_KEY_NO_CALC) ||
+           wps_build_auth_type_flags(wps, msg) ||
+           wps_build_encr_type_flags(wps, msg) ||
+           wps_build_conn_type_flags(wps, msg) ||
+           wps_build_config_methods(msg, config_methods) ||
+           wps_build_wps_state(wps, msg) ||
+           wps_build_device_attrs(&wps->wps->dev, msg) ||
+           wps_build_rf_bands(&wps->wps->dev, msg) ||
+           wps_build_assoc_state(wps, msg) ||
+           wps_build_dev_password_id(msg, wps->dev_pw_id) ||
+           wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
+           wps_build_os_version(&wps->wps->dev, msg) ||
+           wps_build_wfa_ext(msg, 0, NULL, 0) ||
+           wps_build_vendor_ext_m1(&wps->wps->dev, msg)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       wps->state = RECV_M2;
+       return msg;
+}
+
+
+static struct wpabuf * wps_build_m3(struct wps_data *wps)
+{
+       struct wpabuf *msg;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Building Message M3");
+
+       if (wps->dev_password == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Device Password available");
+               return NULL;
+       }
+       wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL)
+               return NULL;
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_M3) ||
+           wps_build_registrar_nonce(wps, msg) ||
+           wps_build_e_hash(wps, msg) ||
+           wps_build_wfa_ext(msg, 0, NULL, 0) ||
+           wps_build_authenticator(wps, msg)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       wps->state = RECV_M4;
+       return msg;
+}
+
+
+static struct wpabuf * wps_build_m5(struct wps_data *wps)
+{
+       struct wpabuf *msg, *plain;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Building Message M5");
+
+       plain = wpabuf_alloc(200);
+       if (plain == NULL)
+               return NULL;
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL) {
+               wpabuf_free(plain);
+               return NULL;
+       }
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_M5) ||
+           wps_build_registrar_nonce(wps, msg) ||
+           wps_build_e_snonce1(wps, plain) ||
+           wps_build_key_wrap_auth(wps, plain) ||
+           wps_build_encr_settings(wps, msg, plain) ||
+           wps_build_wfa_ext(msg, 0, NULL, 0) ||
+           wps_build_authenticator(wps, msg)) {
+               wpabuf_free(plain);
+               wpabuf_free(msg);
+               return NULL;
+       }
+       wpabuf_free(plain);
+
+       wps->state = RECV_M6;
+       return msg;
+}
+
+
+static int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * SSID");
+       wpabuf_put_be16(msg, ATTR_SSID);
+       wpabuf_put_be16(msg, wps->wps->ssid_len);
+       wpabuf_put_data(msg, wps->wps->ssid, wps->wps->ssid_len);
+       return 0;
+}
+
+
+static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
+{
+       u16 auth_type = wps->wps->auth_types;
+
+       /* Select the best authentication type */
+       if (auth_type & WPS_AUTH_WPA2PSK)
+               auth_type = WPS_AUTH_WPA2PSK;
+       else if (auth_type & WPS_AUTH_WPAPSK)
+               auth_type = WPS_AUTH_WPAPSK;
+       else if (auth_type & WPS_WIFI_AUTH_OPEN)
+               auth_type = WPS_WIFI_AUTH_OPEN;
+       else if (auth_type & WPS_AUTH_SHARED)
+               auth_type = WPS_AUTH_SHARED;
+
+       wpa_printf(MSG_DEBUG,  "WPS:  * Authentication Type (0x%x)", auth_type);
+       wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, auth_type);
+       return 0;
+}
+
+
+static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
+{
+       u16 encr_type = wps->wps->encr_types;
+
+       /* Select the best encryption type */
+       if (wps->wps->auth_types & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
+               if (encr_type & WPS_ENCR_AES)
+                       encr_type = WPS_ENCR_AES;
+               else if (encr_type & WPS_ENCR_TKIP)
+                       encr_type = WPS_ENCR_TKIP;
+       } else {
+               if (encr_type & WPS_ENCR_WEP)
+                       encr_type = WPS_ENCR_WEP;
+               else if (encr_type & WPS_ENCR_NONE)
+                       encr_type = WPS_ENCR_NONE;
+       }
+
+       wpa_printf(MSG_DEBUG,  "WPS:  * Encryption Type (0x%x)", encr_type);
+       wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, encr_type);
+       return 0;
+}
+
+
+static int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * Network Key");
+       wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
+       wpabuf_put_be16(msg, wps->wps->network_key_len);
+       wpabuf_put_data(msg, wps->wps->network_key, wps->wps->network_key_len);
+       return 0;
+}
+
+
+static int wps_build_cred_mac_addr(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * MAC Address (AP BSSID)");
+       wpabuf_put_be16(msg, ATTR_MAC_ADDR);
+       wpabuf_put_be16(msg, ETH_ALEN);
+       wpabuf_put_data(msg, wps->wps->dev.mac_addr, ETH_ALEN);
+       return 0;
+}
+
+
+static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *plain)
+{
+       if (wps->wps->ap_settings) {
+               wpa_printf(MSG_DEBUG,  "WPS:  * AP Settings (pre-configured)");
+               wpabuf_put_data(plain, wps->wps->ap_settings,
+                               wps->wps->ap_settings_len);
+               return 0;
+       }
+
+       return wps_build_cred_ssid(wps, plain) ||
+               wps_build_cred_mac_addr(wps, plain) ||
+               wps_build_cred_auth_type(wps, plain) ||
+               wps_build_cred_encr_type(wps, plain) ||
+               wps_build_cred_network_key(wps, plain);
+}
+
+
+static struct wpabuf * wps_build_m7(struct wps_data *wps)
+{
+       struct wpabuf *msg, *plain;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Building Message M7");
+
+       plain = wpabuf_alloc(500 + wps->wps->ap_settings_len);
+       if (plain == NULL)
+               return NULL;
+
+       msg = wpabuf_alloc(1000 + wps->wps->ap_settings_len);
+       if (msg == NULL) {
+               wpabuf_free(plain);
+               return NULL;
+       }
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_M7) ||
+           wps_build_registrar_nonce(wps, msg) ||
+           wps_build_e_snonce2(wps, plain) ||
+           (wps->wps->ap && wps_build_ap_settings(wps, plain)) ||
+           wps_build_key_wrap_auth(wps, plain) ||
+           wps_build_encr_settings(wps, msg, plain) ||
+           wps_build_wfa_ext(msg, 0, NULL, 0) ||
+           wps_build_authenticator(wps, msg)) {
+               wpabuf_free(plain);
+               wpabuf_free(msg);
+               return NULL;
+       }
+       wpabuf_free(plain);
+
+       if (wps->wps->ap && wps->wps->registrar) {
+               /*
+                * If the Registrar is only learning our current configuration,
+                * it may not continue protocol run to successful completion.
+                * Store information here to make sure it remains available.
+                */
+               wps_device_store(wps->wps->registrar, &wps->peer_dev,
+                                wps->uuid_r);
+       }
+
+       wps->state = RECV_M8;
+       return msg;
+}
+
+
+static struct wpabuf * wps_build_wsc_done(struct wps_data *wps)
+{
+       struct wpabuf *msg;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Building Message WSC_Done");
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL)
+               return NULL;
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_WSC_DONE) ||
+           wps_build_enrollee_nonce(wps, msg) ||
+           wps_build_registrar_nonce(wps, msg) ||
+           wps_build_wfa_ext(msg, 0, NULL, 0)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       if (wps->wps->ap)
+               wps->state = RECV_ACK;
+       else {
+               wps_success_event(wps->wps);
+               wps->state = WPS_FINISHED;
+       }
+       return msg;
+}
+
+
+struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps,
+                                    enum wsc_op_code *op_code)
+{
+       struct wpabuf *msg;
+
+       switch (wps->state) {
+       case SEND_M1:
+               msg = wps_build_m1(wps);
+               *op_code = WSC_MSG;
+               break;
+       case SEND_M3:
+               msg = wps_build_m3(wps);
+               *op_code = WSC_MSG;
+               break;
+       case SEND_M5:
+               msg = wps_build_m5(wps);
+               *op_code = WSC_MSG;
+               break;
+       case SEND_M7:
+               msg = wps_build_m7(wps);
+               *op_code = WSC_MSG;
+               break;
+       case RECEIVED_M2D:
+               if (wps->wps->ap) {
+                       msg = wps_build_wsc_nack(wps);
+                       *op_code = WSC_NACK;
+                       break;
+               }
+               msg = wps_build_wsc_ack(wps);
+               *op_code = WSC_ACK;
+               if (msg) {
+                       /* Another M2/M2D may be received */
+                       wps->state = RECV_M2;
+               }
+               break;
+       case SEND_WSC_NACK:
+               msg = wps_build_wsc_nack(wps);
+               *op_code = WSC_NACK;
+               break;
+       case WPS_MSG_DONE:
+               msg = wps_build_wsc_done(wps);
+               *op_code = WSC_Done;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG,  "WPS: Unsupported state %d for building "
+                          "a message", wps->state);
+               msg = NULL;
+               break;
+       }
+
+       if (*op_code == WSC_MSG && msg) {
+               /* Save a copy of the last message for Authenticator derivation
+                */
+               wpabuf_free(wps->last_msg);
+               wps->last_msg = wpabuf_dup(msg);
+       }
+
+       return msg;
+}
+
+
+static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
+{
+       if (r_nonce == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Registrar Nonce received");
+               return -1;
+       }
+
+       os_memcpy(wps->nonce_r, r_nonce, WPS_NONCE_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
+                   wps->nonce_r, WPS_NONCE_LEN);
+
+       return 0;
+}
+
+
+static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
+{
+       if (e_nonce == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Enrollee Nonce received");
+               return -1;
+       }
+
+       if (os_memcmp(wps->nonce_e, e_nonce, WPS_NONCE_LEN) != 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: Invalid Enrollee Nonce received");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int wps_process_uuid_r(struct wps_data *wps, const u8 *uuid_r)
+{
+       if (uuid_r == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No UUID-R received");
+               return -1;
+       }
+
+       os_memcpy(wps->uuid_r, uuid_r, WPS_UUID_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
+
+       return 0;
+}
+
+
+static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
+                             size_t pk_len)
+{
+       if (pk == NULL || pk_len == 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Public Key received");
+               return -1;
+       }
+
+       wpabuf_free(wps->dh_pubkey_r);
+       wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
+       if (wps->dh_pubkey_r == NULL)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "process pubkey start\n");
+
+       if (wps_derive_keys(wps) < 0) {
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "process pubkey finish\n");
+
+       return 0;
+}
+
+
+static int wps_process_r_hash1(struct wps_data *wps, const u8 *r_hash1)
+{
+       if (r_hash1 == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No R-Hash1 received");
+               return -1;
+       }
+
+       os_memcpy(wps->peer_hash1, r_hash1, WPS_HASH_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", wps->peer_hash1, WPS_HASH_LEN);
+
+       return 0;
+}
+
+
+static int wps_process_r_hash2(struct wps_data *wps, const u8 *r_hash2)
+{
+       if (r_hash2 == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No R-Hash2 received");
+               return -1;
+       }
+
+       os_memcpy(wps->peer_hash2, r_hash2, WPS_HASH_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", wps->peer_hash2, WPS_HASH_LEN);
+
+       return 0;
+}
+
+
+static int wps_process_r_snonce1(struct wps_data *wps, const u8 *r_snonce1)
+{
+       u8 hash[SHA256_MAC_LEN];
+       const u8 *addr[4];
+       size_t len[4];
+
+       if (r_snonce1 == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No R-SNonce1 received");
+               return -1;
+       }
+
+       wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce1", r_snonce1,
+                       WPS_SECRET_NONCE_LEN);
+
+       /* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
+       addr[0] = r_snonce1;
+       len[0] = WPS_SECRET_NONCE_LEN;
+       addr[1] = wps->psk1;
+       len[1] = WPS_PSK_LEN;
+       addr[2] = wpabuf_head(wps->dh_pubkey_e);
+       len[2] = wpabuf_len(wps->dh_pubkey_e);
+       addr[3] = wpabuf_head(wps->dh_pubkey_r);
+       len[3] = wpabuf_len(wps->dh_pubkey_r);
+
+       if (wps_crypto_funcs.hmac_sha256_vector) {
+               wps_crypto_funcs.hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, (int *)len, hash);
+       } else {
+               wpa_printf(MSG_ERROR, "In function %s, fail to register hmac_sha256_vector function!\r\n", __FUNCTION__);
+               return -1;
+       }
+       if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: R-Hash1 derived from R-S1 does "
+                          "not match with the pre-committed value");
+               wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
+               wps_pwd_auth_fail_event(wps->wps, 1, 1);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG,  "WPS: Registrar proved knowledge of the first "
+                  "half of the device password");
+
+       return 0;
+}
+
+
+static int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2)
+{
+       u8 hash[SHA256_MAC_LEN];
+       const u8 *addr[4];
+       size_t len[4];
+
+       if (r_snonce2 == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No R-SNonce2 received");
+               return -1;
+       }
+
+       wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce2", r_snonce2,
+                       WPS_SECRET_NONCE_LEN);
+
+       /* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
+       addr[0] = r_snonce2;
+       len[0] = WPS_SECRET_NONCE_LEN;
+       addr[1] = wps->psk2;
+       len[1] = WPS_PSK_LEN;
+       addr[2] = wpabuf_head(wps->dh_pubkey_e);
+       len[2] = wpabuf_len(wps->dh_pubkey_e);
+       addr[3] = wpabuf_head(wps->dh_pubkey_r);
+       len[3] = wpabuf_len(wps->dh_pubkey_r);
+
+       if (wps_crypto_funcs.hmac_sha256_vector) {
+               wps_crypto_funcs.hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, (int *)len, hash);
+       } else {
+               wpa_printf(MSG_ERROR, "In function %s, fail to regiset hmac_sha256_vector function!\r\n", __FUNCTION__);
+               return -1;
+       }
+
+       if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: R-Hash2 derived from R-S2 does "
+                          "not match with the pre-committed value");
+               wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
+               wps_pwd_auth_fail_event(wps->wps, 1, 2);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG,  "WPS: Registrar proved knowledge of the second "
+                  "half of the device password");
+
+       return 0;
+}
+
+
+static int wps_process_cred_e(struct wps_data *wps, const u8 *cred,
+                             size_t cred_len, int wps2)
+{
+       struct wps_parse_attr *attr;
+       struct wpabuf msg;
+       int ret = 0;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Received Credential");
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL)
+               return -99;
+
+       os_memset(&wps->cred, 0, sizeof(wps->cred));
+       wpabuf_set(&msg, cred, cred_len);
+       if (wps_parse_msg(&msg, attr) < 0 ||
+           wps_process_cred(attr, &wps->cred)) {
+               ret = -1;
+               goto _out;
+       }
+
+       if (os_memcmp(wps->cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
+           0) {
+               wpa_printf(MSG_DEBUG,  "WPS: MAC Address in the Credential ("
+                          MACSTR ") does not match with own address (" MACSTR
+                          ")", MAC2STR(wps->cred.mac_addr),
+                          MAC2STR(wps->wps->dev.mac_addr));
+               /*
+                * In theory, this could be consider fatal error, but there are
+                * number of deployed implementations using other address here
+                * due to unclarity in the specification. For interoperability
+                * reasons, allow this to be processed since we do not really
+                * use the MAC Address information for anything.
+                */
+#ifdef CONFIG_WPS_STRICT
+               if (wps2) {
+                       wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
+                                  "MAC Address in AP Settings");
+                       ret = -1;
+                       goto _out;
+               }
+#endif /* CONFIG_WPS_STRICT */
+       }
+
+#ifdef CONFIG_WPS2
+       if (!(wps->cred.encr_type &
+             (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) {
+               if (wps->cred.encr_type & WPS_ENCR_WEP) {
+                       wpa_printf(MSG_INFO, "WPS: Reject Credential "
+                                  "due to WEP configuration");
+                       wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
+                       ret = -2;
+                       goto _out;
+               }
+
+               wpa_printf(MSG_INFO, "WPS: Reject Credential due to "
+                          "invalid encr_type 0x%x", wps->cred.encr_type);
+               ret = -1;
+               goto _out;
+       }
+#endif /* CONFIG_WPS2 */
+
+           wps_ssid_save(wps->cred.ssid, wps->cred.ssid_len);
+        wps_key_save((char *)wps->cred.key, wps->cred.key_len);
+
+       if (wps->wps->cred_cb) {
+               wps->cred.cred_attr = cred - 4;
+               wps->cred.cred_attr_len = cred_len + 4;
+               ret = wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred);
+               wps->cred.cred_attr = NULL;
+               wps->cred.cred_attr_len = 0;
+       }
+
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+static int wps_process_creds(struct wps_data *wps, const u8 *cred[],
+                            size_t cred_len[], size_t num_cred, int wps2)
+{
+       size_t i;
+       int ok = 0;
+
+       if (wps->wps->ap)
+               return 0;
+
+       if (num_cred == 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Credential attributes "
+                          "received");
+               return -1;
+       }
+
+       for (i = 0; i < num_cred; i++) {
+               int res;
+               res = wps_process_cred_e(wps, cred[i], cred_len[i], wps2);
+               if (res == 0)
+                       ok++;
+               else if (res == -2) {
+                       wpa_printf(MSG_DEBUG,  "WPS: WEP credential skipped");
+               }
+               else
+                       return -1;
+       }
+
+       if (ok == 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: No valid Credential attribute "
+                          "received");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int wps_process_ap_settings_e(struct wps_data *wps,
+                                    struct wps_parse_attr *attr,
+                                    struct wpabuf *attrs, int wps2)
+{
+       struct wps_credential *cred;
+       int ret = 0;
+
+       cred = (struct wps_credential *)os_zalloc(sizeof(struct wps_credential));
+       if (cred == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+       if (!wps->wps->ap) {
+               ret = 0;
+               goto _out;
+       }
+
+       if (wps_process_ap_settings(attr, cred) < 0) {
+               ret = -1;
+               goto _out;
+       }
+
+       wpa_printf(MSG_INFO, "WPS: Received new AP configuration from "
+                  "Registrar");
+
+       if (os_memcmp(cred->mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
+           0) {
+               wpa_printf(MSG_DEBUG,  "WPS: MAC Address in the AP Settings ("
+                          MACSTR ") does not match with own address (" MACSTR
+                          ")", MAC2STR(cred->mac_addr),
+                          MAC2STR(wps->wps->dev.mac_addr));
+               /*
+                * In theory, this could be consider fatal error, but there are
+                * number of deployed implementations using other address here
+                * due to unclarity in the specification. For interoperability
+                * reasons, allow this to be processed since we do not really
+                * use the MAC Address information for anything.
+                */
+#ifdef CONFIG_WPS_STRICT
+               if (wps2) {
+                       wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
+                                  "MAC Address in AP Settings");
+                       ret = -1;
+                       goto _out;
+               }
+#endif /* CONFIG_WPS_STRICT */
+       }
+
+#ifdef CONFIG_WPS2
+       if (!(cred->encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES)))
+       {
+               if (cred->encr_type & WPS_ENCR_WEP) {
+                       wpa_printf(MSG_INFO, "WPS: Reject new AP settings "
+                                  "due to WEP configuration");
+                       wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
+                       ret = -1;
+                       goto _out;
+               }
+
+               wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
+                          "invalid encr_type 0x%x", cred->encr_type);
+               ret = -1;
+               goto _out;
+       }
+#endif /* CONFIG_WPS2 */
+
+#ifdef CONFIG_WPS_STRICT
+       if (wps2) {
+               if ((cred->encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) ==
+                   WPS_ENCR_TKIP ||
+                   (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
+                   WPS_AUTH_WPAPSK) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC 2.0 "
+                                  "AP Settings: WPA-Personal/TKIP only");
+                       wps->error_indication =
+                               WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED;
+                       ret = -1;
+                       goto _out;
+               }
+       }
+#endif /* CONFIG_WPS_STRICT */
+
+#ifdef CONFIG_WPS2
+       if ((cred->encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP)
+       {
+               wpa_printf(MSG_DEBUG,  "WPS: Upgrade encr_type TKIP -> "
+                          "TKIP+AES");
+               cred->encr_type |= WPS_ENCR_AES;
+       }
+
+       if ((cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
+           WPS_AUTH_WPAPSK) {
+               wpa_printf(MSG_DEBUG,  "WPS: Upgrade auth_type WPAPSK -> "
+                          "WPAPSK+WPA2PSK");
+               cred->auth_type |= WPS_AUTH_WPA2PSK;
+       }
+#endif /* CONFIG_WPS2 */
+
+       if (wps->wps->cred_cb) {
+               cred->cred_attr = wpabuf_head(attrs);
+               cred->cred_attr_len = wpabuf_len(attrs);
+               wps->wps->cred_cb(wps->wps->cb_ctx, cred);
+       }
+
+_out:
+       if (cred)
+               os_free(cred);
+
+       return ret;
+}
+
+
+static enum wps_process_res wps_process_m2(struct wps_data *wps,
+                                          const struct wpabuf *msg,
+                                          struct wps_parse_attr *attr)
+{
+       wpa_printf(MSG_DEBUG,  "WPS: Received M2");
+
+       if (wps->state != RECV_M2) {
+               wpa_printf(MSG_DEBUG,  "WPS: Unexpected state (%d) for "
+                          "receiving M2", wps->state);
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+           wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+           wps_process_uuid_r(wps, attr->uuid_r)) {
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       /*
+        * Stop here on an AP as an Enrollee if AP Setup is locked unless the
+        * special locked mode is used to allow protocol run up to M7 in order
+        * to support external Registrars that only learn the current AP
+        * configuration without changing it.
+        */
+       if (wps->wps->ap &&
+           ((wps->wps->ap_setup_locked && wps->wps->ap_setup_locked != 2) ||
+            wps->dev_password == NULL)) {
+               wpa_printf(MSG_DEBUG,  "WPS: AP Setup is locked - refuse "
+                          "registration of a new Registrar");
+               wps->config_error = WPS_CFG_SETUP_LOCKED;
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       if (wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
+           wps_process_authenticator(wps, attr->authenticator, msg) ||
+           wps_process_device_attrs(&wps->peer_dev, attr)) {
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       wps->state = SEND_M3;
+       return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m2d(struct wps_data *wps,
+                                           struct wps_parse_attr *attr)
+{
+       wpa_printf(MSG_DEBUG,  "WPS: Received M2D");
+
+       if (wps->state != RECV_M2) {
+               wpa_printf(MSG_DEBUG,  "WPS: Unexpected state (%d) for "
+                          "receiving M2D", wps->state);
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer",
+                         attr->manufacturer, attr->manufacturer_len);
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name",
+                         attr->model_name, attr->model_name_len);
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number",
+                         attr->model_number, attr->model_number_len);
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number",
+                         attr->serial_number, attr->serial_number_len);
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name",
+                         attr->dev_name, attr->dev_name_len);
+
+       if (wps->wps->event_cb) {
+               union wps_event_data data;
+               struct wps_event_m2d *m2d = &data.m2d;
+               os_memset(&data, 0, sizeof(data));
+               if (attr->config_methods)
+                       m2d->config_methods =
+                               WPA_GET_BE16(attr->config_methods);
+               m2d->manufacturer = attr->manufacturer;
+               m2d->manufacturer_len = attr->manufacturer_len;
+               m2d->model_name = attr->model_name;
+               m2d->model_name_len = attr->model_name_len;
+               m2d->model_number = attr->model_number;
+               m2d->model_number_len = attr->model_number_len;
+               m2d->serial_number = attr->serial_number;
+               m2d->serial_number_len = attr->serial_number_len;
+               m2d->dev_name = attr->dev_name;
+               m2d->dev_name_len = attr->dev_name_len;
+               m2d->primary_dev_type = attr->primary_dev_type;
+               if (attr->config_error)
+                       m2d->config_error =
+                               WPA_GET_BE16(attr->config_error);
+               if (attr->dev_password_id)
+                       m2d->dev_password_id =
+                               WPA_GET_BE16(attr->dev_password_id);
+               wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_M2D, &data);
+       }
+
+       wps->state = RECEIVED_M2D;
+       return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m4(struct wps_data *wps,
+                                          const struct wpabuf *msg,
+                                          struct wps_parse_attr *attr)
+{
+       struct wpabuf *decrypted;
+       struct wps_parse_attr *eattr;
+       enum wps_process_res res;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Received M4");
+
+       eattr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+        if (eattr == NULL) {
+               wps->state = SEND_WSC_NACK;
+               res = WPS_CONTINUE;
+               goto _out;
+       }
+               
+       if (wps->state != RECV_M4) {
+               wpa_printf(MSG_DEBUG,  "WPS: Unexpected state (%d) for "
+                          "receiving M4", wps->state);
+               wps->state = SEND_WSC_NACK;
+               res = WPS_CONTINUE;
+               goto _out;
+       }
+
+       if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+           wps_process_authenticator(wps, attr->authenticator, msg) ||
+           wps_process_r_hash1(wps, attr->r_hash1) ||
+           wps_process_r_hash2(wps, attr->r_hash2)) {
+               wps->state = SEND_WSC_NACK;
+               res = WPS_CONTINUE;
+               goto _out;
+       }
+
+       decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+                                             attr->encr_settings_len);
+       if (decrypted == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: Failed to decrypted Encrypted "
+                          "Settings attribute");
+               wps->state = SEND_WSC_NACK;
+               res = WPS_CONTINUE;
+               goto _out;
+       }
+
+       if (wps_validate_m4_encr(decrypted, attr->version2 != NULL) < 0) {
+               wpabuf_free(decrypted);
+               wps->state = SEND_WSC_NACK;
+               res = WPS_CONTINUE;
+               goto _out;
+       }
+
+       wpa_printf(MSG_DEBUG,  "WPS: Processing decrypted Encrypted Settings "
+                  "attribute");
+       if (wps_parse_msg(decrypted, eattr) < 0 ||
+           wps_process_key_wrap_auth(wps, decrypted, eattr->key_wrap_auth) ||
+           wps_process_r_snonce1(wps, eattr->r_snonce1)) {
+               wpabuf_free(decrypted);
+               wps->state = SEND_WSC_NACK;
+               res = WPS_CONTINUE;
+               goto _out;
+       }
+       wpabuf_free(decrypted);
+
+       wps->state = SEND_M5;
+       res = WPS_CONTINUE;
+_out:
+       if (eattr)
+               os_free(eattr);
+       return res;
+}
+
+
+static enum wps_process_res wps_process_m6(struct wps_data *wps,
+                                          const struct wpabuf *msg,
+                                          struct wps_parse_attr *attr)
+{
+       struct wpabuf *decrypted;
+       struct wps_parse_attr *eattr;
+       enum wps_process_res res;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Received M6");
+
+       eattr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+        if (eattr == NULL) {
+               wps->state = SEND_WSC_NACK;
+               res = WPS_CONTINUE;
+               goto _out;
+       }
+
+       if (wps->state != RECV_M6) {
+               wpa_printf(MSG_DEBUG,  "WPS: Unexpected state (%d) for "
+                          "receiving M6", wps->state);
+               wps->state = SEND_WSC_NACK;
+               res = WPS_CONTINUE;
+               goto _out;
+       }
+
+       if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+           wps_process_authenticator(wps, attr->authenticator, msg)) {
+               wps->state = SEND_WSC_NACK;
+               res = WPS_CONTINUE;
+               goto _out;
+       }
+
+       decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+                                             attr->encr_settings_len);
+       if (decrypted == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: Failed to decrypted Encrypted "
+                          "Settings attribute");
+               wps->state = SEND_WSC_NACK;
+               res = WPS_CONTINUE;
+               goto _out;
+       }
+
+       if (wps_validate_m6_encr(decrypted, attr->version2 != NULL) < 0) {
+               wpabuf_free(decrypted);
+               wps->state = SEND_WSC_NACK;
+               res = WPS_CONTINUE;
+               goto _out;
+       }
+
+       wpa_printf(MSG_DEBUG,  "WPS: Processing decrypted Encrypted Settings "
+                  "attribute");
+       if (wps_parse_msg(decrypted, eattr) < 0 ||
+           wps_process_key_wrap_auth(wps, decrypted, eattr->key_wrap_auth) ||
+           wps_process_r_snonce2(wps, eattr->r_snonce2)) {
+               wpabuf_free(decrypted);
+               wps->state = SEND_WSC_NACK;
+               res = WPS_CONTINUE;
+               goto _out;
+       }
+       wpabuf_free(decrypted);
+
+       if (wps->wps->ap)
+               wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_AP_PIN_SUCCESS,
+                                  NULL);
+
+       wps->state = SEND_M7;
+       res = WPS_CONTINUE;
+_out:
+       if (eattr)
+               os_free(eattr);
+       return res;
+}
+
+
+static enum wps_process_res wps_process_m8(struct wps_data *wps,
+                                          const struct wpabuf *msg,
+                                          struct wps_parse_attr *attr)
+{
+       struct wpabuf *decrypted;
+       struct wps_parse_attr *eattr;
+       enum wps_process_res res;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Received M8");
+
+       eattr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+        if (eattr == NULL) {
+               wps->state = SEND_WSC_NACK;
+               res = WPS_CONTINUE;
+               goto _out;
+       }
+
+       if (wps->state != RECV_M8) {
+               wpa_printf(MSG_DEBUG,  "WPS: Unexpected state (%d) for "
+                          "receiving M8", wps->state);
+               wps->state = SEND_WSC_NACK;
+               res = WPS_CONTINUE;
+               goto _out;
+       }
+
+       if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+           wps_process_authenticator(wps, attr->authenticator, msg)) {
+               wps->state = SEND_WSC_NACK;
+               res = WPS_CONTINUE;
+               goto _out;
+       }
+
+       if (wps->wps->ap && wps->wps->ap_setup_locked) {
+               /*
+                * Stop here if special ap_setup_locked == 2 mode allowed the
+                * protocol to continue beyond M2. This allows ER to learn the
+                * current AP settings without changing them.
+                */
+               wpa_printf(MSG_DEBUG,  "WPS: AP Setup is locked - refuse "
+                          "registration of a new Registrar");
+               wps->config_error = WPS_CFG_SETUP_LOCKED;
+               wps->state = SEND_WSC_NACK;
+               res = WPS_CONTINUE;
+               goto _out;
+       }
+
+       decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+                                             attr->encr_settings_len);
+       if (decrypted == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: Failed to decrypted Encrypted "
+                          "Settings attribute");
+               wps->state = SEND_WSC_NACK;
+               res = WPS_CONTINUE;
+               goto _out;
+       }
+
+       if (wps_validate_m8_encr(decrypted, wps->wps->ap,
+                                attr->version2 != NULL) < 0) {
+               wpabuf_free(decrypted);
+               wps->state = SEND_WSC_NACK;
+               res = WPS_CONTINUE;
+               goto _out;
+       }
+
+       wpa_printf(MSG_DEBUG,  "WPS: Processing decrypted Encrypted Settings "
+                  "attribute");
+       if (wps_parse_msg(decrypted, eattr) < 0 ||
+           wps_process_key_wrap_auth(wps, decrypted, eattr->key_wrap_auth) ||
+           wps_process_creds(wps, eattr->cred, eattr->cred_len,
+                             eattr->num_cred, attr->version2 != NULL) ||
+           wps_process_ap_settings_e(wps, eattr, decrypted,
+                                     attr->version2 != NULL)) {
+               wpabuf_free(decrypted);
+               wps->state = SEND_WSC_NACK;
+               res = WPS_CONTINUE;
+               goto _out;
+       }
+       wpabuf_free(decrypted);
+
+       wps->state = WPS_MSG_DONE;
+       res = WPS_CONTINUE;
+
+_out:
+       if (eattr)
+               os_free(eattr);
+       return res;
+}
+
+extern struct wps_sm *gWpsSm;
+
+static enum wps_process_res wps_process_wsc_start(struct wps_data *wps,
+                                               const struct wpabuf *msg)
+{
+       struct wps_sm *sm = gWpsSm;
+       enum wps_process_res ret = WPS_CONTINUE;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Received WSC_START");
+       ets_timer_disarm(&sm->wps_eapol_start_timer);
+        wps->state = SEND_M1; 
+       return ret;
+}
+
+#define WPS_IGNORE_STATE(wps_st)  do {\
+    if (wps->state <= RECV_M8 && ((wps_st) == wps->state - 1 || (wps_st) == wps->state - 2)) { \
+        return WPS_IGNORE;\
+    }\
+} while (0)
+
+static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
+                                               const struct wpabuf *msg)
+{
+       struct wps_parse_attr *attr;
+       enum wps_process_res ret = WPS_CONTINUE;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Received WSC_MSG");
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+        if (attr == NULL) {
+               ret = WPS_FAILURE;
+               goto _out;
+       }
+
+       if (wps_parse_msg(msg, attr) < 0) {
+               ret = WPS_FAILURE;
+               goto _out;
+       }
+
+       if (attr->enrollee_nonce == NULL ||
+           os_memcmp(wps->nonce_e, attr->enrollee_nonce, WPS_NONCE_LEN) != 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: Mismatch in enrollee nonce");
+               ret = WPS_FAILURE;
+               goto _out;
+       }
+
+       if (attr->msg_type == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Message Type attribute");
+               wps->state = SEND_WSC_NACK;
+               ret = WPS_CONTINUE;
+               goto _out;
+       }
+
+       switch (*attr->msg_type) {
+       case WPS_M2:
+                WPS_IGNORE_STATE(RECV_M2);
+               if (wps_validate_m2(msg) < 0) {
+                       ret = WPS_FAILURE;
+                       goto _out;
+               }
+               ret = wps_process_m2(wps, msg, attr);
+               break;
+       case WPS_M2D:
+               if (wps_validate_m2d(msg) < 0) {
+                       ret = WPS_FAILURE;
+                       goto _out;
+               }
+               ret = wps_process_m2d(wps, attr);
+               break;
+       case WPS_M4:
+                WPS_IGNORE_STATE(RECV_M4);
+               if (wps_validate_m4(msg) < 0) {
+                       ret = WPS_FAILURE;
+                       goto _out;
+               }
+               ret = wps_process_m4(wps, msg, attr);
+               if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+                       wps_fail_event(wps->wps, WPS_M4, wps->config_error,
+                                      wps->error_indication);
+               break;
+       case WPS_M6:
+                WPS_IGNORE_STATE(RECV_M6);
+               if (wps_validate_m6(msg) < 0) {
+                       ret = WPS_FAILURE;
+                       goto _out;
+               }
+               ret = wps_process_m6(wps, msg, attr);
+               if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+                       wps_fail_event(wps->wps, WPS_M6, wps->config_error,
+                                      wps->error_indication);
+               break;
+       case WPS_M8:
+                WPS_IGNORE_STATE(RECV_M8);
+               if (wps_validate_m8(msg) < 0) {
+                       ret = WPS_FAILURE;
+                       goto _out;
+               }
+               ret = wps_process_m8(wps, msg, attr);
+               if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+                       wps_fail_event(wps->wps, WPS_M8, wps->config_error,
+                                      wps->error_indication);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG,  "WPS: Unsupported Message Type %d",
+                          *attr->msg_type);
+               ret = WPS_FAILURE;
+               goto _out;
+       }
+
+       /*
+        * Save a copy of the last message for Authenticator derivation if we
+        * are continuing. However, skip M2D since it is not authenticated and
+        * neither is the ACK/NACK response frame. This allows the possibly
+        * following M2 to be processed correctly by using the previously sent
+        * M1 in Authenticator derivation.
+        */
+       if (ret == WPS_CONTINUE && *attr->msg_type != WPS_M2D) {
+               /* Save a copy of the last message for Authenticator derivation
+                */
+               wpabuf_free(wps->last_msg);
+               wps->last_msg = wpabuf_dup(msg);
+       }
+
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
+                                               const struct wpabuf *msg)
+{
+       struct wps_parse_attr *attr;
+       enum wps_process_res res;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Received WSC_ACK");
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+        if (attr == NULL) {
+               res = WPS_FAILURE;
+               goto _out;
+       }
+
+       if (wps_parse_msg(msg, attr) < 0) {
+               res = WPS_FAILURE;
+               goto _out;
+       }
+
+       if (attr->msg_type == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Message Type attribute");
+               res = WPS_FAILURE;
+               goto _out;
+       }
+
+       if (*attr->msg_type != WPS_WSC_ACK) {
+               wpa_printf(MSG_DEBUG,  "WPS: Invalid Message Type %d",
+                          *attr->msg_type);
+               res = WPS_FAILURE;
+               goto _out;
+       }
+
+       if (attr->registrar_nonce == NULL ||
+           os_memcmp(wps->nonce_r, attr->registrar_nonce, WPS_NONCE_LEN) != 0)
+       {
+               wpa_printf(MSG_DEBUG,  "WPS: Mismatch in registrar nonce");
+               res = WPS_FAILURE;
+               goto _out;
+       }
+
+       if (attr->enrollee_nonce == NULL ||
+           os_memcmp(wps->nonce_e, attr->enrollee_nonce, WPS_NONCE_LEN) != 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: Mismatch in enrollee nonce");
+               res = WPS_FAILURE;
+               goto _out;
+       }
+
+       if (wps->state == RECV_ACK && wps->wps->ap) {
+               wpa_printf(MSG_DEBUG,  "WPS: External Registrar registration "
+                          "completed successfully");
+               wps_success_event(wps->wps);
+               wps->state = WPS_FINISHED;
+               res = WPS_DONE;
+               goto _out;
+       }
+
+       res = WPS_FAILURE;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return res;
+}
+
+
+static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
+                                                const struct wpabuf *msg)
+{
+       struct wps_parse_attr *attr;
+       enum wps_process_res res;
+       u16 config_error;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Received WSC_NACK");
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+        if (attr == NULL) {
+               res = WPS_FAILURE;
+               goto _out;
+       }
+
+       if (wps_parse_msg(msg, attr) < 0) {
+               res = WPS_FAILURE;
+               goto _out;
+       }
+
+       if (attr->msg_type == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Message Type attribute");
+               res = WPS_FAILURE;
+               goto _out;
+       }
+
+       if (*attr->msg_type != WPS_WSC_NACK) {
+               wpa_printf(MSG_DEBUG,  "WPS: Invalid Message Type %d",
+                          *attr->msg_type);
+               res = WPS_FAILURE;
+               goto _out;
+       }
+
+       if (attr->registrar_nonce == NULL ||
+           os_memcmp(wps->nonce_r, attr->registrar_nonce, WPS_NONCE_LEN) != 0)
+       {
+               wpa_printf(MSG_DEBUG,  "WPS: Mismatch in registrar nonce");
+               wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce",
+                           attr->registrar_nonce, WPS_NONCE_LEN);
+               wpa_hexdump(MSG_DEBUG, "WPS: Expected Registrar Nonce",
+                           wps->nonce_r, WPS_NONCE_LEN);
+               res = WPS_FAILURE;
+               goto _out;
+       }
+
+       if (attr->enrollee_nonce == NULL ||
+           os_memcmp(wps->nonce_e, attr->enrollee_nonce, WPS_NONCE_LEN) != 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: Mismatch in enrollee nonce");
+               wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce",
+                           attr->enrollee_nonce, WPS_NONCE_LEN);
+               wpa_hexdump(MSG_DEBUG, "WPS: Expected Enrollee Nonce",
+                           wps->nonce_e, WPS_NONCE_LEN);
+               res = WPS_FAILURE;
+               goto _out;
+       }
+
+       if (attr->config_error == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Configuration Error attribute "
+                          "in WSC_NACK");
+               res = WPS_FAILURE;
+               goto _out;
+       }
+
+       config_error = WPA_GET_BE16(attr->config_error);
+       wpa_printf(MSG_DEBUG,  "WPS: Registrar terminated negotiation with "
+                  "Configuration Error %d", config_error);
+
+       switch (wps->state) {
+       case RECV_M4:
+               wps_fail_event(wps->wps, WPS_M3, config_error,
+                              wps->error_indication);
+               break;
+       case RECV_M6:
+               wps_fail_event(wps->wps, WPS_M5, config_error,
+                              wps->error_indication);
+               break;
+       case RECV_M8:
+               wps_fail_event(wps->wps, WPS_M7, config_error,
+                              wps->error_indication);
+               break;
+       default:
+               break;
+       }
+
+       /* Followed by NACK if Enrollee is Supplicant or EAP-Failure if
+        * Enrollee is Authenticator */
+       wps->state = SEND_WSC_NACK;
+
+       res = WPS_FAILURE;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return res;
+}
+
+
+enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
+                                             enum wsc_op_code op_code,
+                                             const struct wpabuf *msg)
+{
+
+       wpa_printf(MSG_DEBUG,  "WPS: Processing received message (len=%lu "
+                  "op_code=%d)",
+                  (unsigned long) wpabuf_len(msg), op_code);
+
+       if (op_code == WSC_UPnP) {
+               /* Determine the OpCode based on message type attribute */
+               struct wps_parse_attr attr;
+               if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) {
+                       if (*attr.msg_type == WPS_WSC_ACK)
+                               op_code = WSC_ACK;
+                       else if (*attr.msg_type == WPS_WSC_NACK)
+                               op_code = WSC_NACK;
+               }
+       }
+
+       switch (op_code) {
+        case WSC_Start:
+                return wps_process_wsc_start(wps, msg);
+       case WSC_MSG:
+       case WSC_UPnP:
+               return wps_process_wsc_msg(wps, msg);
+       case WSC_ACK:
+               if (wps_validate_wsc_ack(msg) < 0)
+                       return WPS_FAILURE;
+               return wps_process_wsc_ack(wps, msg);
+       case WSC_NACK:
+               if (wps_validate_wsc_nack(msg) < 0)
+                       return WPS_FAILURE;
+               return wps_process_wsc_nack(wps, msg);
+       default:
+               wpa_printf(MSG_DEBUG,  "WPS: Unsupported op_code %d", op_code);
+               return WPS_FAILURE;
+       }
+}
diff --git a/components/wpa_supplicant/src/wps/wps_registrar.c b/components/wpa_supplicant/src/wps/wps_registrar.c
new file mode 100644 (file)
index 0000000..a38a75d
--- /dev/null
@@ -0,0 +1,3635 @@
+/*
+ * Wi-Fi Protected Setup - Registrar
+ * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#include "wpa/includes.h"
+#include "wpa/list.h"
+#include "wpa/common.h"
+#include "crypto/base64.h"
+//#include "utils/eloop.h"
+#include "wps/utils/uuid.h"
+#include "wpa/list.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "wpa/ieee802_11_defs.h"
+#include "wps/wps_i.h"
+#include "wps/wps_dev_attr.h"
+//#include "wps/wps_upnp.h"
+//#include "wps/wps_upnp_i.h"
+
+#ifndef CONFIG_WPS_STRICT
+#define WPS_WORKAROUNDS
+#endif /* CONFIG_WPS_STRICT */
+
+#ifdef CONFIG_WPS_NFC
+
+struct wps_nfc_pw_token {
+       struct dl_list list;
+       u8 pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+       u16 pw_id;
+       u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN];
+       size_t dev_pw_len;
+};
+
+
+static void wps_remove_nfc_pw_token(struct wps_nfc_pw_token *token)
+{
+       dl_list_del(&token->list);
+       os_free(token);
+}
+
+
+static void wps_free_nfc_pw_tokens(struct dl_list *tokens, u16 pw_id)
+{
+       struct wps_nfc_pw_token *token, *prev;
+       dl_list_for_each_safe(token, prev, tokens, struct wps_nfc_pw_token,
+                             list) {
+               if (pw_id == 0 || pw_id == token->pw_id)
+                       wps_remove_nfc_pw_token(token);
+       }
+}
+
+
+static struct wps_nfc_pw_token * wps_get_nfc_pw_token(struct dl_list *tokens,
+                                                     u16 pw_id)
+{
+       struct wps_nfc_pw_token *token;
+       dl_list_for_each(token, tokens, struct wps_nfc_pw_token, list) {
+               if (pw_id == token->pw_id)
+                       return token;
+       }
+       return NULL;
+}
+
+#else /* CONFIG_WPS_NFC */
+
+#define wps_free_nfc_pw_tokens(t, p) do { } while (0)
+
+#endif /* CONFIG_WPS_NFC */
+
+#ifdef CONFIG_WPS_PIN
+
+struct wps_uuid_pin {
+       struct dl_list list;
+       u8 uuid[WPS_UUID_LEN];
+       int wildcard_uuid;
+       u8 *pin;
+       size_t pin_len;
+#define PIN_LOCKED BIT(0)
+#define PIN_EXPIRES BIT(1)
+       int flags;
+       struct os_time expiration;
+       u8 enrollee_addr[ETH_ALEN];
+};
+
+
+static void wps_free_pin(struct wps_uuid_pin *pin)
+{
+       os_free(pin->pin);
+       os_free(pin);
+}
+
+
+static void wps_remove_pin(struct wps_uuid_pin *pin)
+{
+       dl_list_del(&pin->list);
+       wps_free_pin(pin);
+}
+
+
+static void wps_free_pins(struct dl_list *pins)
+{
+       struct wps_uuid_pin *pin, *prev;
+       dl_list_for_each_safe(pin, prev, pins, struct wps_uuid_pin, list);
+       wps_remove_pin(pin);
+}
+
+#endif
+
+struct wps_pbc_session {
+       struct wps_pbc_session *next;
+       u8 addr[ETH_ALEN];
+       u8 uuid_e[WPS_UUID_LEN];
+       struct os_time timestamp;
+};
+
+
+static void wps_free_pbc_sessions(struct wps_pbc_session *pbc)
+{
+       struct wps_pbc_session *prev;
+
+       while (pbc) {
+               prev = pbc;
+               pbc = pbc->next;
+               os_free(prev);
+       }
+}
+
+
+struct wps_registrar_device {
+       struct wps_registrar_device *next;
+       struct wps_device_data dev;
+       u8 uuid[WPS_UUID_LEN];
+};
+
+
+struct wps_registrar {
+       struct wps_context *wps;
+
+       int pbc;
+       int selected_registrar;
+
+       int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk,
+                         size_t psk_len);
+       int (*set_ie_cb)(void *ctx, struct wpabuf *beacon_ie,
+                        struct wpabuf *probe_resp_ie);
+       void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
+                             const struct wps_device_data *dev);
+       void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
+                              const u8 *uuid_e, const u8 *dev_pw,
+                              size_t dev_pw_len);
+       void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id,
+                              u16 sel_reg_config_methods);
+       void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e,
+                                const u8 *pri_dev_type, u16 config_methods,
+                                u16 dev_password_id, u8 request_type,
+                                const char *dev_name);
+       void *cb_ctx;
+
+       struct dl_list pins;
+       struct dl_list nfc_pw_tokens;
+       struct wps_pbc_session *pbc_sessions;
+
+       int skip_cred_build;
+       struct wpabuf *extra_cred;
+       int disable_auto_conf;
+       int sel_reg_union;
+       int sel_reg_dev_password_id_override;
+       int sel_reg_config_methods_override;
+       int static_wep_only;
+       int dualband;
+
+       struct wps_registrar_device *devices;
+
+       int force_pbc_overlap;
+
+       u8 authorized_macs[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
+       u8 authorized_macs_union[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
+
+       u8 p2p_dev_addr[ETH_ALEN];
+
+       u8 pbc_ignore_uuid[WPS_UUID_LEN];
+       struct os_time pbc_ignore_start;
+};
+
+
+static int wps_set_ie(struct wps_registrar *reg);
+//static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx);
+//static void wps_registrar_set_selected_timeout(void *eloop_ctx,
+//                                            void *timeout_ctx);
+static void wps_registrar_pbc_timeout(void *eloop_ctx);
+//static void wps_registrar_set_selected_timeout(void *eloop_ctx);
+
+#ifdef CONFIG_WPS_PIN
+
+static void wps_registrar_remove_pin(struct wps_registrar *reg,
+                                    struct wps_uuid_pin *pin);
+#endif
+
+static void wps_registrar_add_authorized_mac(struct wps_registrar *reg,
+                                            const u8 *addr)
+{
+       int i;
+       wpa_printf(MSG_DEBUG,  "WPS: Add authorized MAC " MACSTR,
+                  MAC2STR(addr));
+       for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++)
+               if (os_memcmp(reg->authorized_macs[i], addr, ETH_ALEN) == 0) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Authorized MAC was "
+                                  "already in the list");
+                       return; /* already in list */
+               }
+       for (i = WPS_MAX_AUTHORIZED_MACS - 1; i > 0; i--)
+               os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i - 1],
+                         ETH_ALEN);
+       os_memcpy(reg->authorized_macs[0], addr, ETH_ALEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs",
+                   (u8 *) reg->authorized_macs, sizeof(reg->authorized_macs));
+}
+
+
+static void wps_registrar_remove_authorized_mac(struct wps_registrar *reg,
+                                               const u8 *addr)
+{
+       int i;
+       wpa_printf(MSG_DEBUG,  "WPS: Remove authorized MAC " MACSTR,
+                  MAC2STR(addr));
+       for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++) {
+               if (os_memcmp(reg->authorized_macs, addr, ETH_ALEN) == 0)
+                       break;
+       }
+       if (i == WPS_MAX_AUTHORIZED_MACS) {
+               wpa_printf(MSG_DEBUG,  "WPS: Authorized MAC was not in the "
+                          "list");
+               return; /* not in the list */
+       }
+       for (; i + 1 < WPS_MAX_AUTHORIZED_MACS; i++)
+               os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i + 1],
+                         ETH_ALEN);
+       os_memset(reg->authorized_macs[WPS_MAX_AUTHORIZED_MACS - 1], 0,
+                 ETH_ALEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs",
+                   (u8 *) reg->authorized_macs, sizeof(reg->authorized_macs));
+}
+
+
+static void wps_free_devices(struct wps_registrar_device *dev)
+{
+       struct wps_registrar_device *prev;
+
+       while (dev) {
+               prev = dev;
+               dev = dev->next;
+               wps_device_data_free(&prev->dev);
+               os_free(prev);
+       }
+}
+
+
+static struct wps_registrar_device * wps_device_get(struct wps_registrar *reg,
+                                                   const u8 *addr)
+{
+       struct wps_registrar_device *dev;
+
+       for (dev = reg->devices; dev; dev = dev->next) {
+               if (os_memcmp(dev->dev.mac_addr, addr, ETH_ALEN) == 0)
+                       return dev;
+       }
+       return NULL;
+}
+
+
+static void wps_device_clone_data(struct wps_device_data *dst,
+                                 struct wps_device_data *src)
+{
+       os_memcpy(dst->mac_addr, src->mac_addr, ETH_ALEN);
+       os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN);
+
+#define WPS_STRDUP(n) \
+       os_free(dst->n); \
+       dst->n = src->n ? os_strdup(src->n) : NULL
+
+       WPS_STRDUP(device_name);
+       WPS_STRDUP(manufacturer);
+       WPS_STRDUP(model_name);
+       WPS_STRDUP(model_number);
+       WPS_STRDUP(serial_number);
+#undef WPS_STRDUP
+}
+
+
+int wps_device_store(struct wps_registrar *reg,
+                    struct wps_device_data *dev, const u8 *uuid)
+{
+       struct wps_registrar_device *d;
+
+       d = wps_device_get(reg, dev->mac_addr);
+       if (d == NULL) {
+               d = (struct wps_registrar_device *)os_zalloc(sizeof(*d));
+               if (d == NULL)
+                       return -1;
+               d->next = reg->devices;
+               reg->devices = d;
+       }
+
+       wps_device_clone_data(&d->dev, dev);
+       os_memcpy(d->uuid, uuid, WPS_UUID_LEN);
+
+       return 0;
+}
+
+
+static void wps_registrar_add_pbc_session(struct wps_registrar *reg,
+                                         const u8 *addr, const u8 *uuid_e)
+{
+       struct wps_pbc_session *pbc, *prev = NULL;
+       struct os_time now;
+
+       os_get_time(&now);
+
+       pbc = reg->pbc_sessions;
+       while (pbc) {
+               if (os_memcmp(pbc->addr, addr, ETH_ALEN) == 0 &&
+                   os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) {
+                       if (prev)
+                               prev->next = pbc->next;
+                       else
+                               reg->pbc_sessions = pbc->next;
+                       break;
+               }
+               prev = pbc;
+               pbc = pbc->next;
+       }
+
+       if (!pbc) {
+               pbc = (struct wps_pbc_session *)os_zalloc(sizeof(*pbc));
+               if (pbc == NULL)
+                       return;
+               os_memcpy(pbc->addr, addr, ETH_ALEN);
+               if (uuid_e)
+                       os_memcpy(pbc->uuid_e, uuid_e, WPS_UUID_LEN);
+       }
+
+       pbc->next = reg->pbc_sessions;
+       reg->pbc_sessions = pbc;
+       pbc->timestamp = now;
+
+       /* remove entries that have timed out */
+       prev = pbc;
+       pbc = pbc->next;
+
+       while (pbc) {
+               if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) {
+                       prev->next = NULL;
+                       wps_free_pbc_sessions(pbc);
+                       break;
+               }
+               prev = pbc;
+               pbc = pbc->next;
+       }
+}
+
+
+static void wps_registrar_remove_pbc_session(struct wps_registrar *reg,
+                                            const u8 *uuid_e,
+                                            const u8 *p2p_dev_addr)
+{
+       struct wps_pbc_session *pbc, *prev = NULL, *tmp;
+
+       pbc = reg->pbc_sessions;
+       while (pbc) {
+               if (os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0 ||
+                   (p2p_dev_addr && !is_zero_ether_addr(reg->p2p_dev_addr) &&
+                    os_memcmp(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
+                    0)) {
+                       if (prev)
+                               prev->next = pbc->next;
+                       else
+                               reg->pbc_sessions = pbc->next;
+                       tmp = pbc;
+                       pbc = pbc->next;
+                       wpa_printf(MSG_DEBUG,  "WPS: Removing PBC session for "
+                                  "addr=" MACSTR, MAC2STR(tmp->addr));
+                       wpa_hexdump(MSG_DEBUG, "WPS: Removed UUID-E",
+                                   tmp->uuid_e, WPS_UUID_LEN);
+                       os_free(tmp);
+                       continue;
+               }
+               prev = pbc;
+               pbc = pbc->next;
+       }
+}
+
+
+int wps_registrar_pbc_overlap(struct wps_registrar *reg,
+                             const u8 *addr, const u8 *uuid_e)
+{
+       int count = 0;
+       struct wps_pbc_session *pbc;
+       struct wps_pbc_session *first = NULL;
+       struct os_time now;
+
+       os_get_time(&now);
+
+       wpa_printf(MSG_DEBUG,  "WPS: Checking active PBC sessions for overlap");
+
+       if (uuid_e) {
+               wpa_printf(MSG_DEBUG,  "WPS: Add one for the requested UUID");
+               wpa_hexdump(MSG_DEBUG, "WPS: Requested UUID",
+                           uuid_e, WPS_UUID_LEN);
+               count++;
+       }
+
+       for (pbc = reg->pbc_sessions; pbc; pbc = pbc->next) {
+               wpa_printf(MSG_DEBUG,  "WPS: Consider PBC session with " MACSTR,
+                          MAC2STR(pbc->addr));
+               wpa_hexdump(MSG_DEBUG, "WPS: UUID-E",
+                           pbc->uuid_e, WPS_UUID_LEN);
+               if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) {
+                       wpa_printf(MSG_DEBUG,  "WPS: PBC walk time has "
+                                  "expired");
+                       break;
+               }
+               if (first &&
+                   os_memcmp(pbc->uuid_e, first->uuid_e, WPS_UUID_LEN) == 0) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Same Enrollee");
+                       continue; /* same Enrollee */
+               }
+               if (uuid_e == NULL ||
+                   os_memcmp(uuid_e, pbc->uuid_e, WPS_UUID_LEN)) {
+                       wpa_printf(MSG_DEBUG,  "WPS: New Enrollee");
+                       count++;
+               }
+               if (first == NULL)
+                       first = pbc;
+       }
+
+       wpa_printf(MSG_DEBUG,  "WPS: %u active PBC session(s) found", count);
+
+       return count > 1 ? 1 : 0;
+}
+
+
+static int wps_build_wps_state(struct wps_context *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * Wi-Fi Protected Setup State (%d)",
+                  wps->wps_state);
+       wpabuf_put_be16(msg, ATTR_WPS_STATE);
+       wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, wps->wps_state);
+       return 0;
+}
+
+
+#ifdef CONFIG_WPS_UPNP
+static void wps_registrar_free_pending_m2(struct wps_context *wps)
+{
+       struct upnp_pending_message *p, *p2, *prev = NULL;
+       p = wps->upnp_msgs;
+       while (p) {
+               if (p->type == WPS_M2 || p->type == WPS_M2D) {
+                       if (prev == NULL)
+                               wps->upnp_msgs = p->next;
+                       else
+                               prev->next = p->next;
+                       wpa_printf(MSG_DEBUG,  "WPS UPnP: Drop pending M2/M2D");
+                       p2 = p;
+                       p = p->next;
+                       wpabuf_free(p2->msg);
+                       os_free(p2);
+                       continue;
+               }
+               prev = p;
+               p = p->next;
+       }
+}
+#endif /* CONFIG_WPS_UPNP */
+
+
+static int wps_build_ap_setup_locked(struct wps_context *wps,
+                                    struct wpabuf *msg)
+{
+       if (wps->ap_setup_locked && wps->ap_setup_locked != 2) {
+               wpa_printf(MSG_DEBUG,  "WPS:  * AP Setup Locked");
+               wpabuf_put_be16(msg, ATTR_AP_SETUP_LOCKED);
+               wpabuf_put_be16(msg, 1);
+               wpabuf_put_u8(msg, 1);
+       }
+       return 0;
+}
+
+
+static int wps_build_selected_registrar(struct wps_registrar *reg,
+                                       struct wpabuf *msg)
+{
+       if (!reg->sel_reg_union)
+               return 0;
+       wpa_printf(MSG_DEBUG,  "WPS:  * Selected Registrar");
+       wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR);
+       wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, 1);
+       return 0;
+}
+
+
+static int wps_build_sel_reg_dev_password_id(struct wps_registrar *reg,
+                                            struct wpabuf *msg)
+{
+       u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT;
+       if (!reg->sel_reg_union)
+               return 0;
+       if (reg->sel_reg_dev_password_id_override >= 0)
+               id = reg->sel_reg_dev_password_id_override;
+       wpa_printf(MSG_DEBUG,  "WPS:  * Device Password ID (%d)", id);
+       wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, id);
+       return 0;
+}
+
+
+static int wps_build_sel_pbc_reg_uuid_e(struct wps_registrar *reg,
+                                       struct wpabuf *msg)
+{
+       u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT;
+       if (!reg->sel_reg_union)
+               return 0;
+       if (reg->sel_reg_dev_password_id_override >= 0)
+               id = reg->sel_reg_dev_password_id_override;
+       if (id != DEV_PW_PUSHBUTTON || !reg->dualband)
+               return 0;
+       return wps_build_uuid_e(msg, reg->wps->uuid);
+}
+
+
+static void wps_set_pushbutton(u16 *methods, u16 conf_methods)
+{
+       *methods |= WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+       if ((conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON) ==
+           WPS_CONFIG_VIRT_PUSHBUTTON)
+               *methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+       if ((conf_methods & WPS_CONFIG_PHY_PUSHBUTTON) ==
+           WPS_CONFIG_PHY_PUSHBUTTON)
+               *methods |= WPS_CONFIG_PHY_PUSHBUTTON;
+       if ((*methods & WPS_CONFIG_VIRT_PUSHBUTTON) !=
+           WPS_CONFIG_VIRT_PUSHBUTTON &&
+           (*methods & WPS_CONFIG_PHY_PUSHBUTTON) !=
+           WPS_CONFIG_PHY_PUSHBUTTON) {
+               /*
+                * Required to include virtual/physical flag, but we were not
+                * configured with push button type, so have to default to one
+                * of them.
+                */
+               *methods |= WPS_CONFIG_PHY_PUSHBUTTON;
+       }
+#endif /* CONFIG_WPS2 */
+}
+
+
+static int wps_build_sel_reg_config_methods(struct wps_registrar *reg,
+                                           struct wpabuf *msg)
+{
+       u16 methods;
+       if (!reg->sel_reg_union)
+               return 0;
+       methods = reg->wps->config_methods;
+       methods &= ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+       methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+                    WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
+       if (reg->pbc)
+               wps_set_pushbutton(&methods, reg->wps->config_methods);
+       if (reg->sel_reg_config_methods_override >= 0)
+               methods = reg->sel_reg_config_methods_override;
+       wpa_printf(MSG_DEBUG,  "WPS:  * Selected Registrar Config Methods (%x)",
+                  methods);
+       wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, methods);
+       return 0;
+}
+
+
+static int wps_build_probe_config_methods(struct wps_registrar *reg,
+                                         struct wpabuf *msg)
+{
+       u16 methods;
+       /*
+        * These are the methods that the AP supports as an Enrollee for adding
+        * external Registrars.
+        */
+       methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+       methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+                    WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
+       wpa_printf(MSG_DEBUG,  "WPS:  * Config Methods (%x)", methods);
+       wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, methods);
+       return 0;
+}
+
+
+static int wps_build_config_methods_r(struct wps_registrar *reg,
+                                     struct wpabuf *msg)
+{
+       return wps_build_config_methods(msg, reg->wps->config_methods);
+}
+
+
+const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count)
+{
+       *count = 0;
+
+#ifdef CONFIG_WPS2
+       while (*count < WPS_MAX_AUTHORIZED_MACS) {
+               if (is_zero_ether_addr(reg->authorized_macs_union[*count]))
+                       break;
+               (*count)++;
+       }
+#endif /* CONFIG_WPS2 */
+
+       return (const u8 *) reg->authorized_macs_union;
+}
+
+
+/**
+ * wps_registrar_init - Initialize WPS Registrar data
+ * @wps: Pointer to longterm WPS context
+ * @cfg: Registrar configuration
+ * Returns: Pointer to allocated Registrar data or %NULL on failure
+ *
+ * This function is used to initialize WPS Registrar functionality. It can be
+ * used for a single Registrar run (e.g., when run in a supplicant) or multiple
+ * runs (e.g., when run as an internal Registrar in an AP). Caller is
+ * responsible for freeing the returned data with wps_registrar_deinit() when
+ * Registrar functionality is not needed anymore.
+ */
+struct wps_registrar * wps_registrar_init(struct wps_context *wps,
+                  const struct wps_registrar_config *cfg)
+{
+       struct wps_registrar *reg = (struct wps_registrar *)os_zalloc(sizeof(*reg));
+       if (reg == NULL)
+               return NULL;
+#ifdef CONFIG_WPS_PIN
+       dl_list_init(&reg->pins);
+#endif
+#ifdef CONFIG_WPS_NFC
+       dl_list_init(&reg->nfc_pw_tokens);
+#endif
+       reg->wps = wps;
+       reg->new_psk_cb = cfg->new_psk_cb;
+       reg->set_ie_cb = cfg->set_ie_cb;
+#ifdef CONFIG_WPS_PIN
+       reg->pin_needed_cb = cfg->pin_needed_cb;
+#endif
+       reg->reg_success_cb = cfg->reg_success_cb;
+       reg->set_sel_reg_cb = cfg->set_sel_reg_cb;
+       reg->enrollee_seen_cb = cfg->enrollee_seen_cb;
+       reg->cb_ctx = cfg->cb_ctx;
+       reg->skip_cred_build = cfg->skip_cred_build;
+       if (cfg->extra_cred) {
+               reg->extra_cred = wpabuf_alloc_copy(cfg->extra_cred,
+                                                   cfg->extra_cred_len);
+               if (reg->extra_cred == NULL) {
+                       os_free(reg);
+                       return NULL;
+               }
+       }
+       reg->disable_auto_conf = cfg->disable_auto_conf;
+       reg->sel_reg_dev_password_id_override = -1;
+       reg->sel_reg_config_methods_override = -1;
+       reg->static_wep_only = cfg->static_wep_only;
+       reg->dualband = cfg->dualband;
+
+       if (wps_set_ie(reg)) {
+               wps_registrar_deinit(reg);
+               return NULL;
+       }
+
+       return reg;
+}
+
+
+/**
+ * wps_registrar_deinit - Deinitialize WPS Registrar data
+ * @reg: Registrar data from wps_registrar_init()
+ */
+void wps_registrar_deinit(struct wps_registrar *reg)
+{
+       if (reg == NULL)
+               return;
+       //eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+       //eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+
+       // TODO: snake to check, no sys_untimeout now, by wujg
+//     sys_untimeout(wps_registrar_pbc_timeout, reg);
+//     sys_untimeout(wps_registrar_set_selected_timeout, reg);
+
+#ifdef CONFIG_WPS_PIN
+       wps_free_pins(&reg->pins);
+#endif
+#ifdef CONFIG_WPS_NFC
+       wps_free_nfc_pw_tokens(&reg->nfc_pw_tokens, 0);
+#endif
+       wps_free_pbc_sessions(reg->pbc_sessions);
+       wpabuf_free(reg->extra_cred);
+       wps_free_devices(reg->devices);
+       os_free(reg);
+}
+
+#ifdef CONFIG_WPS_PIN
+
+static void wps_registrar_invalidate_unused(struct wps_registrar *reg)
+{
+       struct wps_uuid_pin *pin;
+
+       dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
+               if (pin->wildcard_uuid == 1 && !(pin->flags & PIN_LOCKED)) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Invalidate previously "
+                                  "configured wildcard PIN");
+                       wps_registrar_remove_pin(reg, pin);
+                       break;
+               }
+       }
+}
+
+
+/**
+ * wps_registrar_add_pin - Configure a new PIN for Registrar
+ * @reg: Registrar data from wps_registrar_init()
+ * @addr: Enrollee MAC address or %NULL if not known
+ * @uuid: UUID-E or %NULL for wildcard (any UUID)
+ * @pin: PIN (Device Password)
+ * @pin_len: Length of pin in octets
+ * @timeout: Time (in seconds) when the PIN will be invalidated; 0 = no timeout
+ * Returns: 0 on success, -1 on failure
+ */
+int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr,
+                         const u8 *uuid, const u8 *pin, size_t pin_len,
+                         int timeout)
+{
+       struct wps_uuid_pin *p;
+
+       p = (struct wps_uuid_pin *)os_zalloc(sizeof(*p));
+       if (p == NULL)
+               return -1;
+       if (addr)
+               os_memcpy(p->enrollee_addr, addr, ETH_ALEN);
+       if (uuid == NULL)
+               p->wildcard_uuid = 1;
+       else
+               os_memcpy(p->uuid, uuid, WPS_UUID_LEN);
+       p->pin = (u8 *)os_malloc(pin_len);
+       if (p->pin == NULL) {
+               os_free(p);
+               return -1;
+       }
+       os_memcpy(p->pin, pin, pin_len);
+       p->pin_len = pin_len;
+
+       if (timeout) {
+               p->flags |= PIN_EXPIRES;
+               os_get_time(&p->expiration);
+               p->expiration.sec += timeout;
+       }
+
+       if (p->wildcard_uuid)
+               wps_registrar_invalidate_unused(reg);
+
+       dl_list_add(&reg->pins, &p->list);
+
+       wpa_printf(MSG_DEBUG,  "WPS: A new PIN configured (timeout=%d)",
+                  timeout);
+       wpa_hexdump(MSG_DEBUG, "WPS: UUID", uuid, WPS_UUID_LEN);
+       wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: PIN", pin, pin_len);
+       reg->selected_registrar = 1;
+       reg->pbc = 0;
+       if (addr)
+               wps_registrar_add_authorized_mac(reg, addr);
+       else
+               wps_registrar_add_authorized_mac(
+                       reg, (u8 *) "\xff\xff\xff\xff\xff\xff");
+       wps_registrar_selected_registrar_changed(reg);
+       //eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+       //eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
+                              //wps_registrar_set_selected_timeout,
+                              //reg, NULL);
+
+       return 0;
+}
+
+
+static void wps_registrar_remove_pin(struct wps_registrar *reg,
+                                    struct wps_uuid_pin *pin)
+{
+       u8 *addr;
+       u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+       if (is_zero_ether_addr(pin->enrollee_addr))
+               addr = bcast;
+       else
+               addr = pin->enrollee_addr;
+       wps_registrar_remove_authorized_mac(reg, addr);
+       wps_remove_pin(pin);
+       wps_registrar_selected_registrar_changed(reg);
+}
+
+
+static void wps_registrar_expire_pins(struct wps_registrar *reg)
+{
+       struct wps_uuid_pin *pin, *prev;
+       struct os_time now;
+
+       os_get_time(&now);
+       dl_list_for_each_safe(pin, prev, &reg->pins, struct wps_uuid_pin, list)
+       {
+               if ((pin->flags & PIN_EXPIRES) &&
+                   os_time_before(&pin->expiration, &now)) {
+                       wpa_hexdump(MSG_DEBUG, "WPS: Expired PIN for UUID",
+                                   pin->uuid, WPS_UUID_LEN);
+                       wps_registrar_remove_pin(reg, pin);
+               }
+       }
+}
+
+
+/**
+ * wps_registrar_invalidate_wildcard_pin - Invalidate a wildcard PIN
+ * @reg: Registrar data from wps_registrar_init()
+ * @dev_pw: PIN to search for or %NULL to match any
+ * @dev_pw_len: Length of dev_pw in octets
+ * Returns: 0 on success, -1 if not wildcard PIN is enabled
+ */
+static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg,
+                                                const u8 *dev_pw,
+                                                size_t dev_pw_len)
+{
+       struct wps_uuid_pin *pin, *prev;
+
+       dl_list_for_each_safe(pin, prev, &reg->pins, struct wps_uuid_pin, list)
+       {
+               if (dev_pw && pin->pin &&
+                   (dev_pw_len != pin->pin_len ||
+                    os_memcmp(dev_pw, pin->pin, dev_pw_len) != 0))
+                       continue; /* different PIN */
+               if (pin->wildcard_uuid) {
+                       wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID",
+                                   pin->uuid, WPS_UUID_LEN);
+                       wps_registrar_remove_pin(reg, pin);
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+
+/**
+ * wps_registrar_invalidate_pin - Invalidate a PIN for a specific UUID-E
+ * @reg: Registrar data from wps_registrar_init()
+ * @uuid: UUID-E
+ * Returns: 0 on success, -1 on failure (e.g., PIN not found)
+ */
+int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid)
+{
+       struct wps_uuid_pin *pin, *prev;
+
+       dl_list_for_each_safe(pin, prev, &reg->pins, struct wps_uuid_pin, list)
+       {
+               if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
+                       wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID",
+                                   pin->uuid, WPS_UUID_LEN);
+                       wps_registrar_remove_pin(reg, pin);
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+
+static const u8 * wps_registrar_get_pin(struct wps_registrar *reg,
+                                       const u8 *uuid, size_t *pin_len)
+{
+       struct wps_uuid_pin *pin, *found = NULL;
+
+       wps_registrar_expire_pins(reg);
+
+       dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
+               if (!pin->wildcard_uuid &&
+                   os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
+                       found = pin;
+                       break;
+               }
+       }
+
+       if (!found) {
+               /* Check for wildcard UUIDs since none of the UUID-specific
+                * PINs matched */
+               dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
+                       if (pin->wildcard_uuid == 1 ||
+                           pin->wildcard_uuid == 2) {
+                               wpa_printf(MSG_DEBUG,  "WPS: Found a wildcard "
+                                          "PIN. Assigned it for this UUID-E");
+                               pin->wildcard_uuid++;
+                               os_memcpy(pin->uuid, uuid, WPS_UUID_LEN);
+                               found = pin;
+                               break;
+                       }
+               }
+       }
+
+       if (!found)
+               return NULL;
+
+       /*
+        * Lock the PIN to avoid attacks based on concurrent re-use of the PIN
+        * that could otherwise avoid PIN invalidations.
+        */
+       if (found->flags & PIN_LOCKED) {
+               wpa_printf(MSG_DEBUG,  "WPS: Selected PIN locked - do not "
+                          "allow concurrent re-use");
+               return NULL;
+       }
+       *pin_len = found->pin_len;
+       found->flags |= PIN_LOCKED;
+       return found->pin;
+}
+
+
+/**
+ * wps_registrar_unlock_pin - Unlock a PIN for a specific UUID-E
+ * @reg: Registrar data from wps_registrar_init()
+ * @uuid: UUID-E
+ * Returns: 0 on success, -1 on failure
+ *
+ * PINs are locked to enforce only one concurrent use. This function unlocks a
+ * PIN to allow it to be used again. If the specified PIN was configured using
+ * a wildcard UUID, it will be removed instead of allowing multiple uses.
+ */
+int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid)
+{
+       struct wps_uuid_pin *pin;
+
+       dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
+               if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
+                       if (pin->wildcard_uuid == 3) {
+                               wpa_printf(MSG_DEBUG,  "WPS: Invalidating used "
+                                          "wildcard PIN");
+                               return wps_registrar_invalidate_pin(reg, uuid);
+                       }
+                       pin->flags &= ~PIN_LOCKED;
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+#endif
+
+static void wps_registrar_stop_pbc(struct wps_registrar *reg)
+{
+       reg->selected_registrar = 0;
+       reg->pbc = 0;
+       os_memset(reg->p2p_dev_addr, 0, ETH_ALEN);
+       wps_registrar_remove_authorized_mac(reg,
+                                           (u8 *) "\xff\xff\xff\xff\xff\xff");
+       wps_registrar_selected_registrar_changed(reg);
+}
+
+
+//static void ICACHE_FLASH_ATTR wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx)
+static void wps_registrar_pbc_timeout(void *eloop_ctx)
+{
+       struct wps_registrar *reg = eloop_ctx;
+
+       wpa_printf(MSG_DEBUG,  "WPS: PBC timed out - disable PBC mode");
+       wps_pbc_timeout_event(reg->wps);
+       wps_registrar_stop_pbc(reg);
+}
+
+
+/**
+ * wps_registrar_button_pushed - Notify Registrar that AP button was pushed
+ * @reg: Registrar data from wps_registrar_init()
+ * @p2p_dev_addr: Limit allowed PBC devices to the specified P2P device, %NULL
+ *     indicates no such filtering
+ * Returns: 0 on success, -1 on failure, -2 on session overlap
+ *
+ * This function is called on an AP when a push button is pushed to activate
+ * PBC mode. The PBC mode will be stopped after walk time (2 minutes) timeout
+ * or when a PBC registration is completed. If more than one Enrollee in active
+ * PBC mode has been detected during the monitor time (previous 2 minutes), the
+ * PBC mode is not activated and -2 is returned to indicate session overlap.
+ * This is skipped if a specific Enrollee is selected.
+ */
+int wps_registrar_button_pushed(struct wps_registrar *reg,
+                               const u8 *p2p_dev_addr)
+{
+       if (p2p_dev_addr == NULL &&
+           wps_registrar_pbc_overlap(reg, NULL, NULL)) {
+               wpa_printf(MSG_DEBUG,  "WPS: PBC overlap - do not start PBC "
+                          "mode");
+               wps_pbc_overlap_event(reg->wps);
+               return -2;
+       }
+       wpa_printf(MSG_DEBUG,  "WPS: Button pushed - PBC mode started");
+       reg->force_pbc_overlap = 0;
+       reg->selected_registrar = 1;
+       reg->pbc = 1;
+       if (p2p_dev_addr)
+               os_memcpy(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
+       else
+               os_memset(reg->p2p_dev_addr, 0, ETH_ALEN);
+       wps_registrar_add_authorized_mac(reg,
+                                        (u8 *) "\xff\xff\xff\xff\xff\xff");
+       wps_registrar_selected_registrar_changed(reg);
+
+       //eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+       //eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+
+       // TODO: snake to check, no sys_untimeout now, by wujg
+//     sys_untimeout(wps_registrar_set_selected_timeout, reg);
+//     sys_untimeout(wps_registrar_pbc_timeout, reg);
+
+       //eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout,
+       //                     reg, NULL);
+//     sys_timeout(WPS_PBC_WALK_TIME*1000, wps_registrar_pbc_timeout, reg);
+
+       return 0;
+}
+
+
+static void wps_registrar_pbc_completed(struct wps_registrar *reg)
+{
+       wpa_printf(MSG_DEBUG,  "WPS: PBC completed - stopping PBC mode");
+
+       // TODO: snake to check, no sys_untimeout now, by wujg
+       //eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+//     sys_untimeout(wps_registrar_pbc_timeout, reg);
+
+       wps_registrar_stop_pbc(reg);
+}
+
+#ifdef CONFIG_WPS_PIN
+
+static void wps_registrar_pin_completed(struct wps_registrar *reg)
+{
+       wpa_printf(MSG_DEBUG,  "WPS: PIN completed using internal Registrar");
+       //eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+       reg->selected_registrar = 0;
+       wps_registrar_selected_registrar_changed(reg);
+}
+#endif
+
+void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e,
+                           const u8 *dev_pw, size_t dev_pw_len)
+{
+       if (registrar->pbc) {
+               wps_registrar_remove_pbc_session(registrar,
+                                                uuid_e, NULL);
+               wps_registrar_pbc_completed(registrar);
+               os_get_time(&registrar->pbc_ignore_start);
+               os_memcpy(registrar->pbc_ignore_uuid, uuid_e, WPS_UUID_LEN);
+       } else {
+#ifdef CONFIG_WPS_PIN
+               wps_registrar_pin_completed(registrar);
+#endif
+       }
+#ifdef CONFIG_WPS_PIN
+       if (dev_pw &&
+           wps_registrar_invalidate_wildcard_pin(registrar, dev_pw,
+                                                 dev_pw_len) == 0) {
+               wpa_hexdump_key(MSG_DEBUG, "WPS: Invalidated wildcard PIN",
+                               dev_pw, dev_pw_len);
+       }
+#endif
+}
+
+
+int wps_registrar_wps_cancel(struct wps_registrar *reg)
+{
+       if (reg->pbc) {
+               wpa_printf(MSG_DEBUG,  "WPS: PBC is set - cancelling it");
+               //wps_registrar_pbc_timeout(reg, NULL);
+               //eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+               wps_registrar_pbc_timeout(reg);
+
+               // TODO: snake to check, no sys_untimeout now, by wujg
+//             sys_untimeout(wps_registrar_pbc_timeout, reg);
+
+               return 1;
+       } else if (reg->selected_registrar) {
+#ifdef CONFIG_WPS_PIN
+
+               /* PIN Method */
+               wpa_printf(MSG_DEBUG,  "WPS: PIN is set - cancelling it");
+               wps_registrar_pin_completed(reg);
+               wps_registrar_invalidate_wildcard_pin(reg, NULL, 0);
+               return 1;
+#endif
+       }
+       return 0;
+}
+
+
+/**
+ * wps_registrar_probe_req_rx - Notify Registrar of Probe Request
+ * @reg: Registrar data from wps_registrar_init()
+ * @addr: MAC address of the Probe Request sender
+ * @wps_data: WPS IE contents
+ *
+ * This function is called on an AP when a Probe Request with WPS IE is
+ * received. This is used to track PBC mode use and to detect possible overlap
+ * situation with other WPS APs.
+ */
+void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
+                               const struct wpabuf *wps_data,
+                               int p2p_wildcard)
+{
+       struct wps_parse_attr attr;
+       int skip_add = 0;
+
+       wpa_hexdump_buf(MSG_MSGDUMP,
+                       "WPS: Probe Request with WPS data received",
+                       wps_data);
+
+       if (wps_parse_msg(wps_data, &attr) < 0)
+               return;
+
+       if (attr.config_methods == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Config Methods attribute in "
+                          "Probe Request");
+               return;
+       }
+
+       if (attr.dev_password_id == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Device Password Id attribute "
+                          "in Probe Request");
+               return;
+       }
+
+       if (reg->enrollee_seen_cb && attr.uuid_e &&
+           attr.primary_dev_type && attr.request_type && !p2p_wildcard) {
+               char *dev_name = NULL;
+               if (attr.dev_name) {
+                       dev_name = (char *)os_zalloc(attr.dev_name_len + 1);
+                       if (dev_name) {
+                               os_memcpy(dev_name, attr.dev_name,
+                                         attr.dev_name_len);
+                       }
+               }
+               reg->enrollee_seen_cb(reg->cb_ctx, addr, attr.uuid_e,
+                                     attr.primary_dev_type,
+                                     WPA_GET_BE16(attr.config_methods),
+                                     WPA_GET_BE16(attr.dev_password_id),
+                                     *attr.request_type, dev_name);
+               os_free(dev_name);
+       }
+
+       if (WPA_GET_BE16(attr.dev_password_id) != DEV_PW_PUSHBUTTON)
+               return; /* Not PBC */
+
+       wpa_printf(MSG_DEBUG,  "WPS: Probe Request for PBC received from "
+                  MACSTR, MAC2STR(addr));
+       if (attr.uuid_e == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: Invalid Probe Request WPS IE: No "
+                          "UUID-E included");
+               return;
+       }
+       wpa_hexdump(MSG_DEBUG, "WPS: UUID-E from Probe Request", attr.uuid_e,
+                   WPS_UUID_LEN);
+
+#ifdef WPS_WORKAROUNDS
+       if (reg->pbc_ignore_start.sec &&
+           os_memcmp(attr.uuid_e, reg->pbc_ignore_uuid, WPS_UUID_LEN) == 0) {
+               struct os_time now, dur;
+               os_get_time(&now);
+               os_time_sub(&now, &reg->pbc_ignore_start, &dur);
+               if (dur.sec >= 0 && dur.sec < 5) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Ignore PBC activation "
+                                  "based on Probe Request from the Enrollee "
+                                  "that just completed PBC provisioning");
+                       skip_add = 1;
+               } else
+                       reg->pbc_ignore_start.sec = 0;
+       }
+#endif /* WPS_WORKAROUNDS */
+
+       if (!skip_add)
+               wps_registrar_add_pbc_session(reg, addr, attr.uuid_e);
+       if (wps_registrar_pbc_overlap(reg, addr, attr.uuid_e)) {
+               wpa_printf(MSG_DEBUG,  "WPS: PBC session overlap detected");
+               reg->force_pbc_overlap = 1;
+               wps_pbc_overlap_event(reg->wps);
+       }
+}
+
+
+static int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr,
+                         const u8 *psk, size_t psk_len)
+{
+       if (reg->new_psk_cb == NULL)
+               return 0;
+
+       return reg->new_psk_cb(reg->cb_ctx, mac_addr, psk, psk_len);
+}
+
+#ifdef CONFIG_WPS_PIN
+
+static void wps_cb_pin_needed(struct wps_registrar *reg, const u8 *uuid_e,
+                             const struct wps_device_data *dev)
+{
+       if (reg->pin_needed_cb == NULL)
+               return;
+
+       reg->pin_needed_cb(reg->cb_ctx, uuid_e, dev);
+}
+#endif
+
+static void wps_cb_reg_success(struct wps_registrar *reg, const u8 *mac_addr,
+                              const u8 *uuid_e, const u8 *dev_pw,
+                              size_t dev_pw_len)
+{
+       if (reg->reg_success_cb == NULL)
+               return;
+
+       reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e, dev_pw, dev_pw_len);
+}
+
+
+static int wps_cb_set_ie(struct wps_registrar *reg, struct wpabuf *beacon_ie,
+                        struct wpabuf *probe_resp_ie)
+{
+       return reg->set_ie_cb(reg->cb_ctx, beacon_ie, probe_resp_ie);
+}
+
+
+static void wps_cb_set_sel_reg(struct wps_registrar *reg)
+{
+       u16 methods = 0;
+       if (reg->set_sel_reg_cb == NULL)
+               return;
+
+       if (reg->selected_registrar) {
+               methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+               methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+                            WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
+               if (reg->pbc)
+                       wps_set_pushbutton(&methods, reg->wps->config_methods);
+       }
+
+       wpa_printf(MSG_DEBUG,  "WPS: wps_cb_set_sel_reg: sel_reg=%d "
+                  "config_methods=0x%x pbc=%d methods=0x%x",
+                  reg->selected_registrar, reg->wps->config_methods,
+                  reg->pbc, methods);
+
+       reg->set_sel_reg_cb(reg->cb_ctx, reg->selected_registrar,
+                           reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT,
+                           methods);
+}
+
+
+static int wps_set_ie(struct wps_registrar *reg)
+{
+       struct wpabuf *beacon;
+       struct wpabuf *probe;
+       const u8 *auth_macs;
+       size_t count;
+       size_t vendor_len = 0;
+       int i;
+
+       if (reg->set_ie_cb == NULL)
+               return 0;
+
+       for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+               if (reg->wps->dev.vendor_ext[i]) {
+                       vendor_len += 2 + 2;
+                       vendor_len += wpabuf_len(reg->wps->dev.vendor_ext[i]);
+               }
+       }
+
+       beacon = wpabuf_alloc(400 + vendor_len);
+       if (beacon == NULL)
+               return -1;
+       probe = wpabuf_alloc(500 + vendor_len);
+       if (probe == NULL) {
+               wpabuf_free(beacon);
+               return -1;
+       }
+
+       auth_macs = wps_authorized_macs(reg, &count);
+
+       wpa_printf(MSG_DEBUG,  "WPS: Build Beacon IEs");
+
+       if (wps_build_version(beacon) ||
+           wps_build_wps_state(reg->wps, beacon) ||
+           wps_build_ap_setup_locked(reg->wps, beacon) ||
+           wps_build_selected_registrar(reg, beacon) ||
+           wps_build_sel_reg_dev_password_id(reg, beacon) ||
+           wps_build_sel_reg_config_methods(reg, beacon) ||
+           wps_build_sel_pbc_reg_uuid_e(reg, beacon) ||
+           (reg->dualband && wps_build_rf_bands(&reg->wps->dev, beacon)) ||
+           wps_build_wfa_ext(beacon, 0, auth_macs, count) ||
+           wps_build_vendor_ext(&reg->wps->dev, beacon)) {
+               wpabuf_free(beacon);
+               wpabuf_free(probe);
+               return -1;
+       }
+
+#ifdef CONFIG_P2P
+       if (wps_build_dev_name(&reg->wps->dev, beacon) ||
+           wps_build_primary_dev_type(&reg->wps->dev, beacon)) {
+               wpabuf_free(beacon);
+               wpabuf_free(probe);
+               return -1;
+       }
+#endif /* CONFIG_P2P */
+
+       wpa_printf(MSG_DEBUG,  "WPS: Build Probe Response IEs");
+
+       if (wps_build_version(probe) ||
+           wps_build_wps_state(reg->wps, probe) ||
+           wps_build_ap_setup_locked(reg->wps, probe) ||
+           wps_build_selected_registrar(reg, probe) ||
+           wps_build_sel_reg_dev_password_id(reg, probe) ||
+           wps_build_sel_reg_config_methods(reg, probe) ||
+           wps_build_resp_type(probe, reg->wps->ap ? WPS_RESP_AP :
+                               WPS_RESP_REGISTRAR) ||
+           wps_build_uuid_e(probe, reg->wps->uuid) ||
+           wps_build_device_attrs(&reg->wps->dev, probe) ||
+           wps_build_probe_config_methods(reg, probe) ||
+           (reg->dualband && wps_build_rf_bands(&reg->wps->dev, probe)) ||
+           wps_build_wfa_ext(probe, 0, auth_macs, count) ||
+           wps_build_vendor_ext(&reg->wps->dev, probe)) {
+               wpabuf_free(beacon);
+               wpabuf_free(probe);
+               return -1;
+       }
+
+       beacon = wps_ie_encapsulate(beacon);
+       probe = wps_ie_encapsulate(probe);
+
+       if (!beacon || !probe) {
+               wpabuf_free(beacon);
+               wpabuf_free(probe);
+               return -1;
+       }
+
+       if (reg->static_wep_only) {
+               /*
+                * Windows XP and Vista clients can get confused about
+                * EAP-Identity/Request when they probe the network with
+                * EAPOL-Start. In such a case, they may assume the network is
+                * using IEEE 802.1X and prompt user for a certificate while
+                * the correct (non-WPS) behavior would be to ask for the
+                * static WEP key. As a workaround, use Microsoft Provisioning
+                * IE to advertise that legacy 802.1X is not supported.
+                */
+               const u8 ms_wps[7] = {
+                       WLAN_EID_VENDOR_SPECIFIC, 5,
+                       /* Microsoft Provisioning IE (00:50:f2:5) */
+                       0x00, 0x50, 0xf2, 5,
+                       0x00 /* no legacy 802.1X or MS WPS */
+               };
+               wpa_printf(MSG_DEBUG,  "WPS: Add Microsoft Provisioning IE "
+                          "into Beacon/Probe Response frames");
+               wpabuf_put_data(beacon, ms_wps, sizeof(ms_wps));
+               wpabuf_put_data(probe, ms_wps, sizeof(ms_wps));
+       }
+
+       return wps_cb_set_ie(reg, beacon, probe);
+}
+
+
+static int wps_get_dev_password(struct wps_data *wps)
+{
+       const u8 *pin;
+       size_t pin_len = 0;
+
+       os_free(wps->dev_password);
+       wps->dev_password = NULL;
+
+       if (wps->pbc) {
+               wpa_printf(MSG_DEBUG,  "WPS: Use default PIN for PBC");
+               pin = (const u8 *) "00000000";
+               pin_len = 8;
+#ifdef CONFIG_WPS_NFC
+       } else if (wps->nfc_pw_token) {
+               wpa_printf(MSG_DEBUG,  "WPS: Use OOB Device Password from NFC "
+                          "Password Token");
+               pin = wps->nfc_pw_token->dev_pw;
+               pin_len = wps->nfc_pw_token->dev_pw_len;
+#endif /* CONFIG_WPS_NFC */
+       } else {
+#ifdef CONFIG_WPS_PIN
+
+               pin = wps_registrar_get_pin(wps->wps->registrar, wps->uuid_e,
+                                           &pin_len);
+       }
+       if (pin == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Device Password available for "
+                          "the Enrollee");
+               wps_cb_pin_needed(wps->wps->registrar, wps->uuid_e,
+                                 &wps->peer_dev);
+               return -1;
+#endif
+       }
+
+       wps->dev_password = (u8 *)os_malloc(pin_len);
+       if (wps->dev_password == NULL)
+               return -1;
+       os_memcpy(wps->dev_password, pin, pin_len);
+       wps->dev_password_len = pin_len;
+
+       return 0;
+}
+
+
+static int wps_build_uuid_r(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * UUID-R");
+       wpabuf_put_be16(msg, ATTR_UUID_R);
+       wpabuf_put_be16(msg, WPS_UUID_LEN);
+       wpabuf_put_data(msg, wps->uuid_r, WPS_UUID_LEN);
+       return 0;
+}
+
+
+static int wps_build_r_hash(struct wps_data *wps, struct wpabuf *msg)
+{
+       u8 *hash;
+       const u8 *addr[4];
+       size_t len[4];
+
+       if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
+               return -1;
+       wpa_hexdump(MSG_DEBUG, "WPS: R-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: R-S2",
+                   wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN);
+
+       if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: DH public keys not available for "
+                          "R-Hash derivation");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG,  "WPS:  * R-Hash1");
+       wpabuf_put_be16(msg, ATTR_R_HASH1);
+       wpabuf_put_be16(msg, SHA256_MAC_LEN);
+       hash = wpabuf_put(msg, SHA256_MAC_LEN);
+       /* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
+       addr[0] = wps->snonce;
+       len[0] = WPS_SECRET_NONCE_LEN;
+       addr[1] = wps->psk1;
+       len[1] = WPS_PSK_LEN;
+       addr[2] = wpabuf_head(wps->dh_pubkey_e);
+       len[2] = wpabuf_len(wps->dh_pubkey_e);
+       addr[3] = wpabuf_head(wps->dh_pubkey_r);
+       len[3] = wpabuf_len(wps->dh_pubkey_r);
+       if (wps_crypto_funcs.hmac_sha256_vector) {
+               wps_crypto_funcs.hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, (int *)len, hash);
+       } else {
+               wpa_printf(MSG_ERROR, "In function %s, fail to register hmac_sha256_vector function!\r\n", __FUNCTION__);
+               return -1;
+       }
+       wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", hash, SHA256_MAC_LEN);
+
+       wpa_printf(MSG_DEBUG,  "WPS:  * R-Hash2");
+       wpabuf_put_be16(msg, ATTR_R_HASH2);
+       wpabuf_put_be16(msg, SHA256_MAC_LEN);
+       hash = wpabuf_put(msg, SHA256_MAC_LEN);
+       /* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
+       addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN;
+       addr[1] = wps->psk2;
+       if (wps_crypto_funcs.hmac_sha256_vector) {
+               wps_crypto_funcs.hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, (int *)len, hash);
+       } else {
+               wpa_printf(MSG_ERROR, "In function %s, fail to register hmac_sha256_vector function!\r\n", __FUNCTION__);
+               return -1;
+       }
+       wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", hash, SHA256_MAC_LEN);
+
+       return 0;
+}
+
+
+static int wps_build_r_snonce1(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * R-SNonce1");
+       wpabuf_put_be16(msg, ATTR_R_SNONCE1);
+       wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+       wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN);
+       return 0;
+}
+
+
+static int wps_build_r_snonce2(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * R-SNonce2");
+       wpabuf_put_be16(msg, ATTR_R_SNONCE2);
+       wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+       wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN,
+                       WPS_SECRET_NONCE_LEN);
+       return 0;
+}
+
+
+static int wps_build_cred_network_idx(struct wpabuf *msg,
+                                     const struct wps_credential *cred)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * Network Index (1)");
+       wpabuf_put_be16(msg, ATTR_NETWORK_INDEX);
+       wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, 1);
+       return 0;
+}
+
+
+static int wps_build_cred_ssid(struct wpabuf *msg,
+                              const struct wps_credential *cred)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * SSID");
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID for Credential",
+                         cred->ssid, cred->ssid_len);
+       wpabuf_put_be16(msg, ATTR_SSID);
+       wpabuf_put_be16(msg, cred->ssid_len);
+       wpabuf_put_data(msg, cred->ssid, cred->ssid_len);
+       return 0;
+}
+
+
+static int wps_build_cred_auth_type(struct wpabuf *msg,
+                                   const struct wps_credential *cred)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * Authentication Type (0x%x)",
+                  cred->auth_type);
+       wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, cred->auth_type);
+       return 0;
+}
+
+
+static int wps_build_cred_encr_type(struct wpabuf *msg,
+                                   const struct wps_credential *cred)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * Encryption Type (0x%x)",
+                  cred->encr_type);
+       wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, cred->encr_type);
+       return 0;
+}
+
+
+static int wps_build_cred_network_key(struct wpabuf *msg,
+                                     const struct wps_credential *cred)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * Network Key (len=%d)",
+                  (int) cred->key_len);
+       wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key",
+                       cred->key, cred->key_len);
+       wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
+       wpabuf_put_be16(msg, cred->key_len);
+       wpabuf_put_data(msg, cred->key, cred->key_len);
+       return 0;
+}
+
+
+static int wps_build_cred_mac_addr(struct wpabuf *msg,
+                                  const struct wps_credential *cred)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * MAC Address (" MACSTR ")",
+                  MAC2STR(cred->mac_addr));
+       wpabuf_put_be16(msg, ATTR_MAC_ADDR);
+       wpabuf_put_be16(msg, ETH_ALEN);
+       wpabuf_put_data(msg, cred->mac_addr, ETH_ALEN);
+       return 0;
+}
+
+
+static int wps_build_credential(struct wpabuf *msg,
+                               const struct wps_credential *cred)
+{
+       if (wps_build_cred_network_idx(msg, cred) ||
+           wps_build_cred_ssid(msg, cred) ||
+           wps_build_cred_auth_type(msg, cred) ||
+           wps_build_cred_encr_type(msg, cred) ||
+           wps_build_cred_network_key(msg, cred) ||
+           wps_build_cred_mac_addr(msg, cred))
+               return -1;
+       return 0;
+}
+
+
+int wps_build_credential_wrap(struct wpabuf *msg,
+                             const struct wps_credential *cred)
+{
+       struct wpabuf *wbuf;
+       wbuf = wpabuf_alloc(200);
+       if (wbuf == NULL)
+               return -1;
+       if (wps_build_credential(wbuf, cred)) {
+               wpabuf_free(wbuf);
+               return -1;
+       }
+       wpabuf_put_be16(msg, ATTR_CRED);
+       wpabuf_put_be16(msg, wpabuf_len(wbuf));
+       wpabuf_put_buf(msg, wbuf);
+       wpabuf_free(wbuf);
+       return 0;
+}
+
+
+int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
+{
+       struct wpabuf *cred;
+
+       if (wps->wps->registrar->skip_cred_build)
+               goto skip_cred_build;
+
+       wpa_printf(MSG_DEBUG,  "WPS:  * Credential");
+       if (wps->use_cred) {
+               os_memcpy(&wps->cred, wps->use_cred, sizeof(wps->cred));
+               goto use_provided;
+       }
+       os_memset(&wps->cred, 0, sizeof(wps->cred));
+
+       os_memcpy(wps->cred.ssid, wps->wps->ssid, wps->wps->ssid_len);
+       wps->cred.ssid_len = wps->wps->ssid_len;
+
+       /* Select the best authentication and encryption type */
+       if (wps->auth_type & WPS_AUTH_WPA2PSK)
+               wps->auth_type = WPS_AUTH_WPA2PSK;
+       else if (wps->auth_type & WPS_AUTH_WPAPSK)
+               wps->auth_type = WPS_AUTH_WPAPSK;
+       else if (wps->auth_type & WPS_WIFI_AUTH_OPEN)
+               wps->auth_type = WPS_WIFI_AUTH_OPEN;
+       else if (wps->auth_type & WPS_AUTH_SHARED)
+               wps->auth_type = WPS_AUTH_SHARED;
+       else {
+               wpa_printf(MSG_DEBUG,  "WPS: Unsupported auth_type 0x%x",
+                          wps->auth_type);
+               return -1;
+       }
+       wps->cred.auth_type = wps->auth_type;
+
+       if (wps->auth_type == WPS_AUTH_WPA2PSK ||
+           wps->auth_type == WPS_AUTH_WPAPSK) {
+               if (wps->encr_type & WPS_ENCR_AES)
+                       wps->encr_type = WPS_ENCR_AES;
+               else if (wps->encr_type & WPS_ENCR_TKIP)
+                       wps->encr_type = WPS_ENCR_TKIP;
+               else {
+                       wpa_printf(MSG_DEBUG,  "WPS: No suitable encryption "
+                                  "type for WPA/WPA2");
+                       return -1;
+               }
+       } else {
+               if (wps->encr_type & WPS_ENCR_WEP)
+                       wps->encr_type = WPS_ENCR_WEP;
+               else if (wps->encr_type & WPS_ENCR_NONE)
+                       wps->encr_type = WPS_ENCR_NONE;
+               else {
+                       wpa_printf(MSG_DEBUG,  "WPS: No suitable encryption "
+                                  "type for non-WPA/WPA2 mode");
+                       return -1;
+               }
+       }
+       wps->cred.encr_type = wps->encr_type;
+       /*
+        * Set MAC address in the Credential to be the Enrollee's MAC address
+        */
+       os_memcpy(wps->cred.mac_addr, wps->mac_addr_e, ETH_ALEN);
+
+       if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->wps->ap &&
+           !wps->wps->registrar->disable_auto_conf) {
+               u8 r[16];
+               /* Generate a random passphrase */
+               if (random_get_bytes(r, sizeof(r)) < 0)
+                       return -1;
+               os_free(wps->new_psk);
+               //wps->new_psk = base64_encode(r, sizeof(r), &wps->new_psk_len);
+               if (wps->new_psk == NULL)
+                       return -1;
+               wps->new_psk_len--; /* remove newline */
+               while (wps->new_psk_len &&
+                      wps->new_psk[wps->new_psk_len - 1] == '=')
+                       wps->new_psk_len--;
+               wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Generated passphrase",
+                                     wps->new_psk, wps->new_psk_len);
+               os_memcpy(wps->cred.key, wps->new_psk, wps->new_psk_len);
+               wps->cred.key_len = wps->new_psk_len;
+       } else if (wps->use_psk_key && wps->wps->psk_set) {
+               char hex[65];
+               wpa_printf(MSG_DEBUG,  "WPS: Use PSK format for Network Key");
+               //wpa_snprintf_hex(hex, sizeof(hex), wps->wps->psk, 32);
+               os_memcpy(wps->cred.key, hex, 32 * 2);
+               wps->cred.key_len = 32 * 2;
+       } else if (wps->wps->network_key) {
+               os_memcpy(wps->cred.key, wps->wps->network_key,
+                         wps->wps->network_key_len);
+               wps->cred.key_len = wps->wps->network_key_len;
+       } else if (wps->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
+               char hex[65];
+               /* Generate a random per-device PSK */
+               os_free(wps->new_psk);
+               wps->new_psk_len = 32;
+               wps->new_psk = (u8 *)os_malloc(wps->new_psk_len);
+               if (wps->new_psk == NULL)
+                       return -1;
+               if (random_get_bytes(wps->new_psk, wps->new_psk_len) < 0) {
+                       os_free(wps->new_psk);
+                       wps->new_psk = NULL;
+                       return -1;
+               }
+               wpa_hexdump_key(MSG_DEBUG, "WPS: Generated per-device PSK",
+                               wps->new_psk, wps->new_psk_len);
+               //wpa_snprintf_hex(hex, sizeof(hex), wps->new_psk,
+               //               wps->new_psk_len);
+               os_memcpy(wps->cred.key, hex, wps->new_psk_len * 2);
+               wps->cred.key_len = wps->new_psk_len * 2;
+       }
+
+use_provided:
+#ifdef CONFIG_WPS_TESTING
+       if (wps_testing_dummy_cred)
+               cred = wpabuf_alloc(200);
+       else
+               cred = NULL;
+       if (cred) {
+               struct wps_credential dummy;
+               wpa_printf(MSG_DEBUG,  "WPS: Add dummy credential");
+               os_memset(&dummy, 0, sizeof(dummy));
+               os_memcpy(dummy.ssid, "dummy", 5);
+               dummy.ssid_len = 5;
+               dummy.auth_type = WPS_AUTH_WPA2PSK;
+               dummy.encr_type = WPS_ENCR_AES;
+               os_memcpy(dummy.key, "dummy psk", 9);
+               dummy.key_len = 9;
+               os_memcpy(dummy.mac_addr, wps->mac_addr_e, ETH_ALEN);
+               wps_build_credential(cred, &dummy);
+               wpa_hexdump_buf(MSG_DEBUG, "WPS: Dummy Credential", cred);
+
+               wpabuf_put_be16(msg, ATTR_CRED);
+               wpabuf_put_be16(msg, wpabuf_len(cred));
+               wpabuf_put_buf(msg, cred);
+
+               wpabuf_free(cred);
+       }
+#endif /* CONFIG_WPS_TESTING */
+
+       cred = wpabuf_alloc(200);
+       if (cred == NULL)
+               return -1;
+
+       if (wps_build_credential(cred, &wps->cred)) {
+               wpabuf_free(cred);
+               return -1;
+       }
+
+       wpabuf_put_be16(msg, ATTR_CRED);
+       wpabuf_put_be16(msg, wpabuf_len(cred));
+       wpabuf_put_buf(msg, cred);
+       wpabuf_free(cred);
+
+skip_cred_build:
+       if (wps->wps->registrar->extra_cred) {
+               wpa_printf(MSG_DEBUG,  "WPS:  * Credential (pre-configured)");
+               wpabuf_put_buf(msg, wps->wps->registrar->extra_cred);
+       }
+
+       return 0;
+}
+
+
+static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG,  "WPS:  * AP Settings");
+
+       if (wps_build_credential(msg, &wps->cred))
+               return -1;
+
+       return 0;
+}
+
+
+static struct wpabuf * wps_build_ap_cred(struct wps_data *wps)
+{
+       struct wpabuf *msg, *plain;
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL)
+               return NULL;
+
+       plain = wpabuf_alloc(200);
+       if (plain == NULL) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       if (wps_build_ap_settings(wps, plain)) {
+               wpabuf_free(plain);
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       wpabuf_put_be16(msg, ATTR_CRED);
+       wpabuf_put_be16(msg, wpabuf_len(plain));
+       wpabuf_put_buf(msg, plain);
+       wpabuf_free(plain);
+
+       return msg;
+}
+
+
+static struct wpabuf * wps_build_m2(struct wps_data *wps)
+{
+       struct wpabuf *msg;
+
+       if (random_get_bytes(wps->nonce_r, WPS_NONCE_LEN) < 0)
+               return NULL;
+       wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
+                   wps->nonce_r, WPS_NONCE_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
+
+       wpa_printf(MSG_DEBUG,  "WPS: Building Message M2");
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL)
+               return NULL;
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_M2) ||
+           wps_build_enrollee_nonce(wps, msg) ||
+           wps_build_registrar_nonce(wps, msg) ||
+           wps_build_uuid_r(wps, msg) ||
+           wps_build_public_key(wps, msg, WPS_CALC_KEY_NORMAL) ||
+           wps_derive_keys(wps) ||
+           wps_build_auth_type_flags(wps, msg) ||
+           wps_build_encr_type_flags(wps, msg) ||
+           wps_build_conn_type_flags(wps, msg) ||
+           wps_build_config_methods_r(wps->wps->registrar, msg) ||
+           wps_build_device_attrs(&wps->wps->dev, msg) ||
+           wps_build_rf_bands(&wps->wps->dev, msg) ||
+           wps_build_assoc_state(wps, msg) ||
+           wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
+           wps_build_dev_password_id(msg, wps->dev_pw_id) ||
+           wps_build_os_version(&wps->wps->dev, msg) ||
+           wps_build_wfa_ext(msg, 0, NULL, 0) ||
+           wps_build_authenticator(wps, msg)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       wps->int_reg = 1;
+       wps->state = RECV_M3;
+       return msg;
+}
+
+
+static struct wpabuf * wps_build_m2d(struct wps_data *wps)
+{
+       struct wpabuf *msg;
+       u16 err = wps->config_error;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Building Message M2D");
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL)
+               return NULL;
+
+       if (wps->wps->ap && wps->wps->ap_setup_locked &&
+           err == WPS_CFG_NO_ERROR)
+               err = WPS_CFG_SETUP_LOCKED;
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_M2D) ||
+           wps_build_enrollee_nonce(wps, msg) ||
+           wps_build_registrar_nonce(wps, msg) ||
+           wps_build_uuid_r(wps, msg) ||
+           wps_build_auth_type_flags(wps, msg) ||
+           wps_build_encr_type_flags(wps, msg) ||
+           wps_build_conn_type_flags(wps, msg) ||
+           wps_build_config_methods_r(wps->wps->registrar, msg) ||
+           wps_build_device_attrs(&wps->wps->dev, msg) ||
+           wps_build_rf_bands(&wps->wps->dev, msg) ||
+           wps_build_assoc_state(wps, msg) ||
+           wps_build_config_error(msg, err) ||
+           wps_build_os_version(&wps->wps->dev, msg) ||
+           wps_build_wfa_ext(msg, 0, NULL, 0)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       wps->state = RECV_M2D_ACK;
+       return msg;
+}
+
+
+static struct wpabuf * wps_build_m4(struct wps_data *wps)
+{
+       struct wpabuf *msg, *plain;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Building Message M4");
+
+       wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
+
+       plain = wpabuf_alloc(200);
+       if (plain == NULL)
+               return NULL;
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL) {
+               wpabuf_free(plain);
+               return NULL;
+       }
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_M4) ||
+           wps_build_enrollee_nonce(wps, msg) ||
+           wps_build_r_hash(wps, msg) ||
+           wps_build_r_snonce1(wps, plain) ||
+           wps_build_key_wrap_auth(wps, plain) ||
+           wps_build_encr_settings(wps, msg, plain) ||
+           wps_build_wfa_ext(msg, 0, NULL, 0) ||
+           wps_build_authenticator(wps, msg)) {
+               wpabuf_free(plain);
+               wpabuf_free(msg);
+               return NULL;
+       }
+       wpabuf_free(plain);
+
+       wps->state = RECV_M5;
+       return msg;
+}
+
+
+static struct wpabuf * wps_build_m6(struct wps_data *wps)
+{
+       struct wpabuf *msg, *plain;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Building Message M6");
+
+       plain = wpabuf_alloc(200);
+       if (plain == NULL)
+               return NULL;
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL) {
+               wpabuf_free(plain);
+               return NULL;
+       }
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_M6) ||
+           wps_build_enrollee_nonce(wps, msg) ||
+           wps_build_r_snonce2(wps, plain) ||
+           wps_build_key_wrap_auth(wps, plain) ||
+           wps_build_encr_settings(wps, msg, plain) ||
+           wps_build_wfa_ext(msg, 0, NULL, 0) ||
+           wps_build_authenticator(wps, msg)) {
+               wpabuf_free(plain);
+               wpabuf_free(msg);
+               return NULL;
+       }
+       wpabuf_free(plain);
+
+       wps->wps_pin_revealed = 1;
+       wps->state = RECV_M7;
+       return msg;
+}
+
+
+static struct wpabuf * wps_build_m8(struct wps_data *wps)
+{
+       struct wpabuf *msg, *plain;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Building Message M8");
+
+       plain = wpabuf_alloc(500);
+       if (plain == NULL)
+               return NULL;
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL) {
+               wpabuf_free(plain);
+               return NULL;
+       }
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_M8) ||
+           wps_build_enrollee_nonce(wps, msg) ||
+           ((wps->wps->ap || wps->er) && wps_build_cred(wps, plain)) ||
+           (!wps->wps->ap && !wps->er && wps_build_ap_settings(wps, plain)) ||
+           wps_build_key_wrap_auth(wps, plain) ||
+           wps_build_encr_settings(wps, msg, plain) ||
+           wps_build_wfa_ext(msg, 0, NULL, 0) ||
+           wps_build_authenticator(wps, msg)) {
+               wpabuf_free(plain);
+               wpabuf_free(msg);
+               return NULL;
+       }
+       wpabuf_free(plain);
+
+       wps->state = RECV_DONE;
+       return msg;
+}
+
+
+struct wpabuf * wps_registrar_get_msg(struct wps_data *wps,
+                                     enum wsc_op_code *op_code)
+{
+       struct wpabuf *msg;
+
+#ifdef CONFIG_WPS_UPNP
+       if (!wps->int_reg && wps->wps->wps_upnp) {
+               struct upnp_pending_message *p, *prev = NULL;
+               if (wps->ext_reg > 1)
+                       wps_registrar_free_pending_m2(wps->wps);
+               p = wps->wps->upnp_msgs;
+               /* TODO: check pending message MAC address */
+               while (p && p->next) {
+                       prev = p;
+                       p = p->next;
+               }
+               if (p) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Use pending message from "
+                                  "UPnP");
+                       if (prev)
+                               prev->next = NULL;
+                       else
+                               wps->wps->upnp_msgs = NULL;
+                       msg = p->msg;
+                       switch (p->type) {
+                       case WPS_WSC_ACK:
+                               *op_code = WSC_ACK;
+                               break;
+                       case WPS_WSC_NACK:
+                               *op_code = WSC_NACK;
+                               break;
+                       default:
+                               *op_code = WSC_MSG;
+                               break;
+                       }
+                       os_free(p);
+                       if (wps->ext_reg == 0)
+                               wps->ext_reg = 1;
+                       return msg;
+               }
+       }
+       if (wps->ext_reg) {
+               wpa_printf(MSG_DEBUG,  "WPS: Using external Registrar, but no "
+                          "pending message available");
+               return NULL;
+       }
+#endif /* CONFIG_WPS_UPNP */
+
+       switch (wps->state) {
+       case SEND_M2:
+               if (wps_get_dev_password(wps) < 0)
+                       msg = wps_build_m2d(wps);
+               else
+                       msg = wps_build_m2(wps);
+               *op_code = WSC_MSG;
+               break;
+       case SEND_M2D:
+               msg = wps_build_m2d(wps);
+               *op_code = WSC_MSG;
+               break;
+       case SEND_M4:
+               msg = wps_build_m4(wps);
+               *op_code = WSC_MSG;
+               break;
+       case SEND_M6:
+               msg = wps_build_m6(wps);
+               *op_code = WSC_MSG;
+               break;
+       case SEND_M8:
+               msg = wps_build_m8(wps);
+               *op_code = WSC_MSG;
+               break;
+       case RECV_DONE:
+               msg = wps_build_wsc_ack(wps);
+               *op_code = WSC_ACK;
+               break;
+       case SEND_WSC_NACK:
+               msg = wps_build_wsc_nack(wps);
+               *op_code = WSC_NACK;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG,  "WPS: Unsupported state %d for building "
+                          "a message", wps->state);
+               msg = NULL;
+               break;
+       }
+
+       if (*op_code == WSC_MSG && msg) {
+               /* Save a copy of the last message for Authenticator derivation
+                */
+               wpabuf_free(wps->last_msg);
+               wps->last_msg = wpabuf_dup(msg);
+       }
+
+       return msg;
+}
+
+
+static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
+{
+       if (e_nonce == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Enrollee Nonce received");
+               return -1;
+       }
+
+       os_memcpy(wps->nonce_e, e_nonce, WPS_NONCE_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
+                   wps->nonce_e, WPS_NONCE_LEN);
+
+       return 0;
+}
+
+
+static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
+{
+       if (r_nonce == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Registrar Nonce received");
+               return -1;
+       }
+
+       if (os_memcmp(wps->nonce_r, r_nonce, WPS_NONCE_LEN) != 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: Invalid Registrar Nonce received");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int wps_process_uuid_e(struct wps_data *wps, const u8 *uuid_e)
+{
+       if (uuid_e == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No UUID-E received");
+               return -1;
+       }
+
+       os_memcpy(wps->uuid_e, uuid_e, WPS_UUID_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", wps->uuid_e, WPS_UUID_LEN);
+
+       return 0;
+}
+
+
+static int wps_process_dev_password_id(struct wps_data *wps, const u8 *pw_id)
+{
+       if (pw_id == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Device Password ID received");
+               return -1;
+       }
+
+       wps->dev_pw_id = WPA_GET_BE16(pw_id);
+       wpa_printf(MSG_DEBUG,  "WPS: Device Password ID %d", wps->dev_pw_id);
+
+       return 0;
+}
+
+
+static int wps_process_e_hash1(struct wps_data *wps, const u8 *e_hash1)
+{
+       if (e_hash1 == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No E-Hash1 received");
+               return -1;
+       }
+
+       os_memcpy(wps->peer_hash1, e_hash1, WPS_HASH_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", wps->peer_hash1, WPS_HASH_LEN);
+
+       return 0;
+}
+
+
+static int wps_process_e_hash2(struct wps_data *wps, const u8 *e_hash2)
+{
+       if (e_hash2 == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No E-Hash2 received");
+               return -1;
+       }
+
+       os_memcpy(wps->peer_hash2, e_hash2, WPS_HASH_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", wps->peer_hash2, WPS_HASH_LEN);
+
+       return 0;
+}
+
+
+static int wps_process_e_snonce1(struct wps_data *wps, const u8 *e_snonce1)
+{
+       u8 hash[SHA256_MAC_LEN];
+       const u8 *addr[4];
+       size_t len[4];
+
+       if (e_snonce1 == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No E-SNonce1 received");
+               return -1;
+       }
+
+       wpa_hexdump_key(MSG_DEBUG, "WPS: E-SNonce1", e_snonce1,
+                       WPS_SECRET_NONCE_LEN);
+
+       /* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */
+       addr[0] = e_snonce1;
+       len[0] = WPS_SECRET_NONCE_LEN;
+       addr[1] = wps->psk1;
+       len[1] = WPS_PSK_LEN;
+       addr[2] = wpabuf_head(wps->dh_pubkey_e);
+       len[2] = wpabuf_len(wps->dh_pubkey_e);
+       addr[3] = wpabuf_head(wps->dh_pubkey_r);
+       len[3] = wpabuf_len(wps->dh_pubkey_r);
+       if (wps_crypto_funcs.hmac_sha256_vector) {
+               wps_crypto_funcs.hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, (int *)len, hash);
+       } else {
+               wpa_printf(MSG_ERROR, "In function %s, fail to register hmac_sha256_vector function!\r\n", __FUNCTION__);
+               return -1;
+       }
+       if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: E-Hash1 derived from E-S1 does "
+                          "not match with the pre-committed value");
+               wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
+               wps_pwd_auth_fail_event(wps->wps, 0, 1);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG,  "WPS: Enrollee proved knowledge of the first "
+                  "half of the device password");
+
+       return 0;
+}
+
+
+static int wps_process_e_snonce2(struct wps_data *wps, const u8 *e_snonce2)
+{
+       u8 hash[SHA256_MAC_LEN];
+       const u8 *addr[4];
+       size_t len[4];
+
+       if (e_snonce2 == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No E-SNonce2 received");
+               return -1;
+       }
+
+       wpa_hexdump_key(MSG_DEBUG, "WPS: E-SNonce2", e_snonce2,
+                       WPS_SECRET_NONCE_LEN);
+
+       /* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */
+       addr[0] = e_snonce2;
+       len[0] = WPS_SECRET_NONCE_LEN;
+       addr[1] = wps->psk2;
+       len[1] = WPS_PSK_LEN;
+       addr[2] = wpabuf_head(wps->dh_pubkey_e);
+       len[2] = wpabuf_len(wps->dh_pubkey_e);
+       addr[3] = wpabuf_head(wps->dh_pubkey_r);
+       len[3] = wpabuf_len(wps->dh_pubkey_r);
+
+       if (wps_crypto_funcs.hmac_sha256_vector) {
+               wps_crypto_funcs.hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, (int *)len, hash);
+       } else {
+               wpa_printf(MSG_ERROR, "In function %s, fail to register hmac_sha256_vector function!\r\n", __FUNCTION__);
+               return -1;
+       }
+       if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: E-Hash2 derived from E-S2 does "
+                          "not match with the pre-committed value");
+#ifdef CONFIG_WPS_PIN
+               wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e);
+#endif
+               wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
+               wps_pwd_auth_fail_event(wps->wps, 0, 2);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG,  "WPS: Enrollee proved knowledge of the second "
+                  "half of the device password");
+       wps->wps_pin_revealed = 0;
+#ifdef CONFIG_WPS_PIN
+       wps_registrar_unlock_pin(wps->wps->registrar, wps->uuid_e);
+
+       /*
+        * In case wildcard PIN is used and WPS handshake succeeds in the first
+        * attempt, wps_registrar_unlock_pin() would not free the PIN, so make
+        * sure the PIN gets invalidated here.
+        */
+       wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e);
+#endif
+       return 0;
+}
+
+
+static int wps_process_mac_addr(struct wps_data *wps, const u8 *mac_addr)
+{
+       if (mac_addr == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No MAC Address received");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG,  "WPS: Enrollee MAC Address " MACSTR,
+                  MAC2STR(mac_addr));
+       os_memcpy(wps->mac_addr_e, mac_addr, ETH_ALEN);
+       os_memcpy(wps->peer_dev.mac_addr, mac_addr, ETH_ALEN);
+
+       return 0;
+}
+
+
+static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
+                             size_t pk_len)
+{
+       if (pk == NULL || pk_len == 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Public Key received");
+               return -1;
+       }
+
+       wpabuf_free(wps->dh_pubkey_e);
+       wps->dh_pubkey_e = wpabuf_alloc_copy(pk, pk_len);
+       if (wps->dh_pubkey_e == NULL)
+               return -1;
+
+       return 0;
+}
+
+
+static int wps_process_auth_type_flags(struct wps_data *wps, const u8 *auth)
+{
+       u16 auth_types;
+
+       if (auth == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Authentication Type flags "
+                          "received");
+               return -1;
+       }
+
+       auth_types = WPA_GET_BE16(auth);
+
+       wpa_printf(MSG_DEBUG,  "WPS: Enrollee Authentication Type flags 0x%x",
+                  auth_types);
+       wps->auth_type = wps->wps->auth_types & auth_types;
+       if (wps->auth_type == 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: No match in supported "
+                          "authentication types (own 0x%x Enrollee 0x%x)",
+                          wps->wps->auth_types, auth_types);
+#ifdef WPS_WORKAROUNDS
+               /*
+                * Some deployed implementations seem to advertise incorrect
+                * information in this attribute. For example, Linksys WRT350N
+                * seems to have a byteorder bug that breaks this negotiation.
+                * In order to interoperate with existing implementations,
+                * assume that the Enrollee supports everything we do.
+                */
+               wpa_printf(MSG_DEBUG,  "WPS: Workaround - assume Enrollee "
+                          "does not advertise supported authentication types "
+                          "correctly");
+               wps->auth_type = wps->wps->auth_types;
+#else /* WPS_WORKAROUNDS */
+               return -1;
+#endif /* WPS_WORKAROUNDS */
+       }
+
+       return 0;
+}
+
+
+static int wps_process_encr_type_flags(struct wps_data *wps, const u8 *encr)
+{
+       u16 encr_types;
+
+       if (encr == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Encryption Type flags "
+                          "received");
+               return -1;
+       }
+
+       encr_types = WPA_GET_BE16(encr);
+
+       wpa_printf(MSG_DEBUG,  "WPS: Enrollee Encryption Type flags 0x%x",
+                  encr_types);
+       wps->encr_type = wps->wps->encr_types & encr_types;
+       if (wps->encr_type == 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: No match in supported "
+                          "encryption types (own 0x%x Enrollee 0x%x)",
+                          wps->wps->encr_types, encr_types);
+#ifdef WPS_WORKAROUNDS
+               /*
+                * Some deployed implementations seem to advertise incorrect
+                * information in this attribute. For example, Linksys WRT350N
+                * seems to have a byteorder bug that breaks this negotiation.
+                * In order to interoperate with existing implementations,
+                * assume that the Enrollee supports everything we do.
+                */
+               wpa_printf(MSG_DEBUG,  "WPS: Workaround - assume Enrollee "
+                          "does not advertise supported encryption types "
+                          "correctly");
+               wps->encr_type = wps->wps->encr_types;
+#else /* WPS_WORKAROUNDS */
+               return -1;
+#endif /* WPS_WORKAROUNDS */
+       }
+
+       return 0;
+}
+
+
+static int wps_process_conn_type_flags(struct wps_data *wps, const u8 *conn)
+{
+       if (conn == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Connection Type flags "
+                          "received");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG,  "WPS: Enrollee Connection Type flags 0x%x",
+                  *conn);
+
+       return 0;
+}
+
+
+static int wps_process_config_methods(struct wps_data *wps, const u8 *methods)
+{
+       u16 m;
+
+       if (methods == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Config Methods received");
+               return -1;
+       }
+
+       m = WPA_GET_BE16(methods);
+
+       wpa_printf(MSG_DEBUG,  "WPS: Enrollee Config Methods 0x%x"
+                  "%s%s%s%s%s%s%s%s%s", m,
+                  m & WPS_CONFIG_USBA ? " [USBA]" : "",
+                  m & WPS_CONFIG_ETHERNET ? " [Ethernet]" : "",
+                  m & WPS_CONFIG_LABEL ? " [Label]" : "",
+                  m & WPS_CONFIG_DISPLAY ? " [Display]" : "",
+                  m & WPS_CONFIG_EXT_NFC_TOKEN ? " [Ext NFC Token]" : "",
+                  m & WPS_CONFIG_INT_NFC_TOKEN ? " [Int NFC Token]" : "",
+                  m & WPS_CONFIG_NFC_INTERFACE ? " [NFC]" : "",
+                  m & WPS_CONFIG_PUSHBUTTON ? " [PBC]" : "",
+                  m & WPS_CONFIG_KEYPAD ? " [Keypad]" : "");
+
+       if (!(m & WPS_CONFIG_DISPLAY) && !wps->use_psk_key) {
+               /*
+                * The Enrollee does not have a display so it is unlikely to be
+                * able to show the passphrase to a user and as such, could
+                * benefit from receiving PSK to reduce key derivation time.
+                */
+               wpa_printf(MSG_DEBUG,  "WPS: Prefer PSK format key due to "
+                          "Enrollee not supporting display");
+               wps->use_psk_key = 1;
+       }
+
+       return 0;
+}
+
+
+static int wps_process_wps_state(struct wps_data *wps, const u8 *state)
+{
+       if (state == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Wi-Fi Protected Setup State "
+                          "received");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG,  "WPS: Enrollee Wi-Fi Protected Setup State %d",
+                  *state);
+
+       return 0;
+}
+
+
+static int wps_process_assoc_state(struct wps_data *wps, const u8 *assoc)
+{
+       u16 a;
+
+       if (assoc == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Association State received");
+               return -1;
+       }
+
+       a = WPA_GET_BE16(assoc);
+       wpa_printf(MSG_DEBUG,  "WPS: Enrollee Association State %d", a);
+
+       return 0;
+}
+
+
+static int wps_process_config_error(struct wps_data *wps, const u8 *err)
+{
+       u16 e;
+
+       if (err == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Configuration Error received");
+               return -1;
+       }
+
+       e = WPA_GET_BE16(err);
+       wpa_printf(MSG_DEBUG,  "WPS: Enrollee Configuration Error %d", e);
+
+       return 0;
+}
+
+
+static int wps_registrar_p2p_dev_addr_match(struct wps_data *wps)
+{
+#ifdef CONFIG_P2P
+       struct wps_registrar *reg = wps->wps->registrar;
+
+       if (is_zero_ether_addr(reg->p2p_dev_addr))
+               return 1; /* no filtering in use */
+
+       if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) != 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: No match on P2P Device Address "
+                          "filtering for PBC: expected " MACSTR " was "
+                          MACSTR " - indicate PBC session overlap",
+                          MAC2STR(reg->p2p_dev_addr),
+                          MAC2STR(wps->p2p_dev_addr));
+               return 0;
+       }
+#endif /* CONFIG_P2P */
+       return 1;
+}
+
+
+static int wps_registrar_skip_overlap(struct wps_data *wps)
+{
+#ifdef CONFIG_P2P
+       struct wps_registrar *reg = wps->wps->registrar;
+
+       if (is_zero_ether_addr(reg->p2p_dev_addr))
+               return 0; /* no specific Enrollee selected */
+
+       if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) == 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: Skip PBC overlap due to selected "
+                          "Enrollee match");
+               return 1;
+       }
+#endif /* CONFIG_P2P */
+       return 0;
+}
+
+
+static enum wps_process_res wps_process_m1(struct wps_data *wps,
+                                          struct wps_parse_attr *attr)
+{
+       wpa_printf(MSG_DEBUG,  "WPS: Received M1");
+
+       if (wps->state != RECV_M1) {
+               wpa_printf(MSG_DEBUG,  "WPS: Unexpected state (%d) for "
+                          "receiving M1", wps->state);
+               return WPS_FAILURE;
+       }
+
+       if (wps_process_uuid_e(wps, attr->uuid_e) ||
+           wps_process_mac_addr(wps, attr->mac_addr) ||
+           wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+           wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
+           wps_process_auth_type_flags(wps, attr->auth_type_flags) ||
+           wps_process_encr_type_flags(wps, attr->encr_type_flags) ||
+           wps_process_conn_type_flags(wps, attr->conn_type_flags) ||
+           wps_process_config_methods(wps, attr->config_methods) ||
+           wps_process_wps_state(wps, attr->wps_state) ||
+           wps_process_device_attrs(&wps->peer_dev, attr) ||
+           wps_process_rf_bands(&wps->peer_dev, attr->rf_bands) ||
+           wps_process_assoc_state(wps, attr->assoc_state) ||
+           wps_process_dev_password_id(wps, attr->dev_password_id) ||
+           wps_process_config_error(wps, attr->config_error) ||
+           wps_process_os_version(&wps->peer_dev, attr->os_version))
+               return WPS_FAILURE;
+
+       if (wps->dev_pw_id < 0x10 &&
+           wps->dev_pw_id != DEV_PW_DEFAULT &&
+           wps->dev_pw_id != DEV_PW_USER_SPECIFIED &&
+           wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED &&
+           wps->dev_pw_id != DEV_PW_REGISTRAR_SPECIFIED &&
+           (wps->dev_pw_id != DEV_PW_PUSHBUTTON ||
+            !wps->wps->registrar->pbc)) {
+               wpa_printf(MSG_DEBUG,  "WPS: Unsupported Device Password ID %d",
+                          wps->dev_pw_id);
+               wps->state = SEND_M2D;
+               return WPS_CONTINUE;
+       }
+
+#ifdef CONFIG_WPS_NFC
+       if (wps->dev_pw_id >= 0x10) {
+               struct wps_nfc_pw_token *token;
+               const u8 *addr[1];
+               u8 hash[WPS_HASH_LEN];
+
+               token = wps_get_nfc_pw_token(
+                       &wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id);
+               if (token) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Found matching NFC "
+                                  "Password Token");
+                       dl_list_del(&token->list);
+                       wps->nfc_pw_token = token;
+
+                       addr[0] = attr->public_key;
+                       if (wps_crypto_funcs.sha256_vector) {
+                               wps_crypto_funcs.sha256_vector(1, addr, &attr->public_key_len, hash);
+                       } else {
+                               wpa_printf(MSG_ERROR, "In function %s, fail to register sha256_vector function!\r\n", __FUNCTION__);
+                               return WPS_FAILURE;
+                       }
+                       if (os_memcmp(hash, wps->nfc_pw_token->pubkey_hash,
+                                     WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+                               wpa_printf(MSG_ERROR,  "WPS: Public Key hash "
+                                          "mismatch");
+                               return WPS_FAILURE;
+                       }
+               }
+       }
+#endif /* CONFIG_WPS_NFC */
+
+       if (wps->dev_pw_id == DEV_PW_PUSHBUTTON) {
+               if ((wps->wps->registrar->force_pbc_overlap ||
+                    wps_registrar_pbc_overlap(wps->wps->registrar,
+                                              wps->mac_addr_e, wps->uuid_e) ||
+                    !wps_registrar_p2p_dev_addr_match(wps)) &&
+                   !wps_registrar_skip_overlap(wps)) {
+                       wpa_printf(MSG_DEBUG,  "WPS: PBC overlap - deny PBC "
+                                  "negotiation");
+                       wps->state = SEND_M2D;
+                       wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+                       wps_pbc_overlap_event(wps->wps);
+                       wps_fail_event(wps->wps, WPS_M1,
+                                      WPS_CFG_MULTIPLE_PBC_DETECTED,
+                                      WPS_EI_NO_ERROR);
+                       wps->wps->registrar->force_pbc_overlap = 1;
+                       return WPS_CONTINUE;
+               }
+               wps_registrar_add_pbc_session(wps->wps->registrar,
+                                             wps->mac_addr_e, wps->uuid_e);
+               wps->pbc = 1;
+       }
+
+#ifdef WPS_WORKAROUNDS
+       /*
+        * It looks like Mac OS X 10.6.3 and 10.6.4 do not like Network Key in
+        * passphrase format. To avoid interop issues, force PSK format to be
+        * used.
+        */
+       if (!wps->use_psk_key &&
+           wps->peer_dev.manufacturer &&
+           os_strncmp(wps->peer_dev.manufacturer, "Apple ", 6) == 0 &&
+           wps->peer_dev.model_name &&
+           os_strcmp(wps->peer_dev.model_name, "AirPort") == 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: Workaround - Force Network Key in "
+                          "PSK format");
+               wps->use_psk_key = 1;
+       }
+#endif /* WPS_WORKAROUNDS */
+
+       wps->state = SEND_M2;
+       return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m3(struct wps_data *wps,
+                                          const struct wpabuf *msg,
+                                          struct wps_parse_attr *attr)
+{
+       wpa_printf(MSG_DEBUG,  "WPS: Received M3");
+
+       if (wps->state != RECV_M3) {
+               wpa_printf(MSG_DEBUG,  "WPS: Unexpected state (%d) for "
+                          "receiving M3", wps->state);
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
+           !wps_registrar_skip_overlap(wps)) {
+               wpa_printf(MSG_DEBUG,  "WPS: Reject negotiation due to PBC "
+                          "session overlap");
+               wps->state = SEND_WSC_NACK;
+               wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+               return WPS_CONTINUE;
+       }
+
+       if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+           wps_process_authenticator(wps, attr->authenticator, msg) ||
+           wps_process_e_hash1(wps, attr->e_hash1) ||
+           wps_process_e_hash2(wps, attr->e_hash2)) {
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       wps->state = SEND_M4;
+       return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m5(struct wps_data *wps,
+                                          const struct wpabuf *msg,
+                                          struct wps_parse_attr *attr)
+{
+       struct wpabuf *decrypted;
+       struct wps_parse_attr eattr;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Received M5");
+
+       if (wps->state != RECV_M5) {
+               wpa_printf(MSG_DEBUG,  "WPS: Unexpected state (%d) for "
+                          "receiving M5", wps->state);
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
+           !wps_registrar_skip_overlap(wps)) {
+               wpa_printf(MSG_DEBUG,  "WPS: Reject negotiation due to PBC "
+                          "session overlap");
+               wps->state = SEND_WSC_NACK;
+               wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+               return WPS_CONTINUE;
+       }
+
+       if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+           wps_process_authenticator(wps, attr->authenticator, msg)) {
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+                                             attr->encr_settings_len);
+       if (decrypted == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: Failed to decrypted Encrypted "
+                          "Settings attribute");
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       if (wps_validate_m5_encr(decrypted, attr->version2 != NULL) < 0) {
+               wpabuf_free(decrypted);
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       wpa_printf(MSG_DEBUG,  "WPS: Processing decrypted Encrypted Settings "
+                  "attribute");
+       if (wps_parse_msg(decrypted, &eattr) < 0 ||
+           wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+           wps_process_e_snonce1(wps, eattr.e_snonce1)) {
+               wpabuf_free(decrypted);
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+       wpabuf_free(decrypted);
+
+       wps->state = SEND_M6;
+       return WPS_CONTINUE;
+}
+
+
+static void wps_sta_cred_cb(struct wps_data *wps)
+{
+       /*
+        * Update credential to only include a single authentication and
+        * encryption type in case the AP configuration includes more than one
+        * option.
+        */
+       if (wps->cred.auth_type & WPS_AUTH_WPA2PSK)
+               wps->cred.auth_type = WPS_AUTH_WPA2PSK;
+       else if (wps->cred.auth_type & WPS_AUTH_WPAPSK)
+               wps->cred.auth_type = WPS_AUTH_WPAPSK;
+       if (wps->cred.encr_type & WPS_ENCR_AES)
+               wps->cred.encr_type = WPS_ENCR_AES;
+       else if (wps->cred.encr_type & WPS_ENCR_TKIP)
+               wps->cred.encr_type = WPS_ENCR_TKIP;
+       wpa_printf(MSG_DEBUG,  "WPS: Update local configuration based on the "
+                  "AP configuration");
+       if (wps->wps->cred_cb)
+               wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred);
+}
+
+
+static void wps_cred_update(struct wps_credential *dst,
+                           struct wps_credential *src)
+{
+       os_memcpy(dst->ssid, src->ssid, sizeof(dst->ssid));
+       dst->ssid_len = src->ssid_len;
+       dst->auth_type = src->auth_type;
+       dst->encr_type = src->encr_type;
+       dst->key_idx = src->key_idx;
+       os_memcpy(dst->key, src->key, sizeof(dst->key));
+       dst->key_len = src->key_len;
+}
+
+
+static int wps_process_ap_settings_r(struct wps_data *wps,
+                                    struct wps_parse_attr *attr)
+{
+       struct wpabuf *msg;
+
+       if (wps->wps->ap || wps->er)
+               return 0;
+
+       /* AP Settings Attributes in M7 when Enrollee is an AP */
+       if (wps_process_ap_settings(attr, &wps->cred) < 0)
+               return -1;
+
+       wpa_printf(MSG_INFO, "WPS: Received old AP configuration from AP");
+
+       if (wps->new_ap_settings) {
+               wpa_printf(MSG_INFO, "WPS: Update AP configuration based on "
+                          "new settings");
+               wps_cred_update(&wps->cred, wps->new_ap_settings);
+               return 0;
+       } else {
+#ifdef CONFIG_WPS_PIN
+               /*
+                * Use the AP PIN only to receive the current AP settings, not
+                * to reconfigure the AP.
+                */
+
+               /*
+                * Clear selected registrar here since we do not get to
+                * WSC_Done in this protocol run.
+                */
+               wps_registrar_pin_completed(wps->wps->registrar);
+#endif
+               msg = wps_build_ap_cred(wps);
+               if (msg == NULL)
+                       return -1;
+               wps->cred.cred_attr = wpabuf_head(msg);
+               wps->cred.cred_attr_len = wpabuf_len(msg);
+
+               if (wps->ap_settings_cb) {
+                       wps->ap_settings_cb(wps->ap_settings_cb_ctx,
+                                           &wps->cred);
+                       wpabuf_free(msg);
+                       return 1;
+               }
+               wps_sta_cred_cb(wps);
+
+               wps->cred.cred_attr = NULL;
+               wps->cred.cred_attr_len = 0;
+               wpabuf_free(msg);
+
+               return 1;
+       }
+}
+
+
+static enum wps_process_res wps_process_m7(struct wps_data *wps,
+                                          const struct wpabuf *msg,
+                                          struct wps_parse_attr *attr)
+{
+       struct wpabuf *decrypted;
+       struct wps_parse_attr eattr;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Received M7");
+
+       if (wps->state != RECV_M7) {
+               wpa_printf(MSG_DEBUG,  "WPS: Unexpected state (%d) for "
+                          "receiving M7", wps->state);
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
+           !wps_registrar_skip_overlap(wps)) {
+               wpa_printf(MSG_DEBUG,  "WPS: Reject negotiation due to PBC "
+                          "session overlap");
+               wps->state = SEND_WSC_NACK;
+               wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+               return WPS_CONTINUE;
+       }
+
+       if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+           wps_process_authenticator(wps, attr->authenticator, msg)) {
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+                                             attr->encr_settings_len);
+       if (decrypted == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: Failed to decrypt Encrypted "
+                          "Settings attribute");
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       if (wps_validate_m7_encr(decrypted, wps->wps->ap || wps->er,
+                                attr->version2 != NULL) < 0) {
+               wpabuf_free(decrypted);
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       wpa_printf(MSG_DEBUG,  "WPS: Processing decrypted Encrypted Settings "
+                  "attribute");
+       if (wps_parse_msg(decrypted, &eattr) < 0 ||
+           wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+           wps_process_e_snonce2(wps, eattr.e_snonce2) ||
+           wps_process_ap_settings_r(wps, &eattr)) {
+               wpabuf_free(decrypted);
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       wpabuf_free(decrypted);
+
+       wps->state = SEND_M8;
+       return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
+                                               const struct wpabuf *msg)
+{
+       struct wps_parse_attr attr;
+       enum wps_process_res ret = WPS_CONTINUE;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Received WSC_MSG");
+
+       if (wps_parse_msg(msg, &attr) < 0)
+               return WPS_FAILURE;
+
+       if (attr.msg_type == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Message Type attribute");
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       if (*attr.msg_type != WPS_M1 &&
+           (attr.registrar_nonce == NULL ||
+            os_memcmp(wps->nonce_r, attr.registrar_nonce,
+                      WPS_NONCE_LEN) != 0)) {
+               wpa_printf(MSG_DEBUG,  "WPS: Mismatch in registrar nonce");
+               return WPS_FAILURE;
+       }
+
+       switch (*attr.msg_type) {
+       case WPS_M1:
+               if (wps_validate_m1(msg) < 0)
+                       return WPS_FAILURE;
+#ifdef CONFIG_WPS_UPNP
+               if (wps->wps->wps_upnp && attr.mac_addr) {
+                       /* Remove old pending messages when starting new run */
+                       wps_free_pending_msgs(wps->wps->upnp_msgs);
+                       wps->wps->upnp_msgs = NULL;
+
+                       upnp_wps_device_send_wlan_event(
+                               wps->wps->wps_upnp, attr.mac_addr,
+                               UPNP_WPS_WLANEVENT_TYPE_EAP, msg);
+               }
+#endif /* CONFIG_WPS_UPNP */
+               ret = wps_process_m1(wps, &attr);
+               break;
+       case WPS_M3:
+               if (wps_validate_m3(msg) < 0)
+                       return WPS_FAILURE;
+               ret = wps_process_m3(wps, msg, &attr);
+               if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+                       wps_fail_event(wps->wps, WPS_M3, wps->config_error,
+                                      wps->error_indication);
+               break;
+       case WPS_M5:
+               if (wps_validate_m5(msg) < 0)
+                       return WPS_FAILURE;
+               ret = wps_process_m5(wps, msg, &attr);
+               if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+                       wps_fail_event(wps->wps, WPS_M5, wps->config_error,
+                                      wps->error_indication);
+               break;
+       case WPS_M7:
+               if (wps_validate_m7(msg) < 0)
+                       return WPS_FAILURE;
+               ret = wps_process_m7(wps, msg, &attr);
+               if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+                       wps_fail_event(wps->wps, WPS_M7, wps->config_error,
+                                      wps->error_indication);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG,  "WPS: Unsupported Message Type %d",
+                          *attr.msg_type);
+               return WPS_FAILURE;
+       }
+
+       if (ret == WPS_CONTINUE) {
+               /* Save a copy of the last message for Authenticator derivation
+                */
+               wpabuf_free(wps->last_msg);
+               wps->last_msg = wpabuf_dup(msg);
+       }
+
+       return ret;
+}
+
+
+static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
+                                               const struct wpabuf *msg)
+{
+       struct wps_parse_attr attr;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Received WSC_ACK");
+
+       if (wps_parse_msg(msg, &attr) < 0)
+               return WPS_FAILURE;
+
+       if (attr.msg_type == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Message Type attribute");
+               return WPS_FAILURE;
+       }
+
+       if (*attr.msg_type != WPS_WSC_ACK) {
+               wpa_printf(MSG_DEBUG,  "WPS: Invalid Message Type %d",
+                          *attr.msg_type);
+               return WPS_FAILURE;
+       }
+
+#ifdef CONFIG_WPS_UPNP
+       if (wps->wps->wps_upnp && wps->ext_reg && wps->state == RECV_M2D_ACK &&
+           upnp_wps_subscribers(wps->wps->wps_upnp)) {
+               if (wps->wps->upnp_msgs)
+                       return WPS_CONTINUE;
+               wpa_printf(MSG_DEBUG,  "WPS: Wait for response from an "
+                          "external Registrar");
+               return WPS_PENDING;
+       }
+#endif /* CONFIG_WPS_UPNP */
+
+       if (attr.registrar_nonce == NULL ||
+           os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
+       {
+               wpa_printf(MSG_DEBUG,  "WPS: Mismatch in registrar nonce");
+               return WPS_FAILURE;
+       }
+
+       if (attr.enrollee_nonce == NULL ||
+           os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: Mismatch in enrollee nonce");
+               return WPS_FAILURE;
+       }
+
+       if (wps->state == RECV_M2D_ACK) {
+#ifdef CONFIG_WPS_UPNP
+               if (wps->wps->wps_upnp &&
+                   upnp_wps_subscribers(wps->wps->wps_upnp)) {
+                       if (wps->wps->upnp_msgs)
+                               return WPS_CONTINUE;
+                       if (wps->ext_reg == 0)
+                               wps->ext_reg = 1;
+                       wpa_printf(MSG_DEBUG,  "WPS: Wait for response from an "
+                                  "external Registrar");
+                       return WPS_PENDING;
+               }
+#endif /* CONFIG_WPS_UPNP */
+
+               wpa_printf(MSG_DEBUG,  "WPS: No more registrars available - "
+                          "terminate negotiation");
+       }
+
+       return WPS_FAILURE;
+}
+
+
+static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
+                                                const struct wpabuf *msg)
+{
+       struct wps_parse_attr attr;
+       int old_state;
+       u16 config_error;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Received WSC_NACK");
+
+       old_state = wps->state;
+       wps->state = SEND_WSC_NACK;
+
+       if (wps_parse_msg(msg, &attr) < 0)
+               return WPS_FAILURE;
+
+       if (attr.msg_type == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Message Type attribute");
+               return WPS_FAILURE;
+       }
+
+       if (*attr.msg_type != WPS_WSC_NACK) {
+               wpa_printf(MSG_DEBUG,  "WPS: Invalid Message Type %d",
+                          *attr.msg_type);
+               return WPS_FAILURE;
+       }
+
+#ifdef CONFIG_WPS_UPNP
+       if (wps->wps->wps_upnp && wps->ext_reg) {
+               wpa_printf(MSG_DEBUG,  "WPS: Negotiation using external "
+                          "Registrar terminated by the Enrollee");
+               return WPS_FAILURE;
+       }
+#endif /* CONFIG_WPS_UPNP */
+
+       if (attr.registrar_nonce == NULL ||
+           os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
+       {
+               wpa_printf(MSG_DEBUG,  "WPS: Mismatch in registrar nonce");
+               return WPS_FAILURE;
+       }
+
+       if (attr.enrollee_nonce == NULL ||
+           os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: Mismatch in enrollee nonce");
+               return WPS_FAILURE;
+       }
+
+       if (attr.config_error == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Configuration Error attribute "
+                          "in WSC_NACK");
+               return WPS_FAILURE;
+       }
+
+       config_error = WPA_GET_BE16(attr.config_error);
+       wpa_printf(MSG_DEBUG,  "WPS: Enrollee terminated negotiation with "
+                  "Configuration Error %d", config_error);
+
+       switch (old_state) {
+       case RECV_M3:
+               wps_fail_event(wps->wps, WPS_M2, config_error,
+                              wps->error_indication);
+               break;
+       case RECV_M5:
+               wps_fail_event(wps->wps, WPS_M4, config_error,
+                              wps->error_indication);
+               break;
+       case RECV_M7:
+               wps_fail_event(wps->wps, WPS_M6, config_error,
+                              wps->error_indication);
+               break;
+       case RECV_DONE:
+               wps_fail_event(wps->wps, WPS_M8, config_error,
+                              wps->error_indication);
+               break;
+       default:
+               break;
+       }
+
+       return WPS_FAILURE;
+}
+
+
+static enum wps_process_res wps_process_wsc_done(struct wps_data *wps,
+                                                const struct wpabuf *msg)
+{
+       struct wps_parse_attr attr;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Received WSC_Done");
+
+       //if (wps->state != RECV_DONE &&
+       //    (!wps->wps->wps_upnp || !wps->ext_reg))
+       if (wps->state != RECV_DONE && (!wps->ext_reg)){
+               wpa_printf(MSG_DEBUG,  "WPS: Unexpected state (%d) for "
+                          "receiving WSC_Done", wps->state);
+               return WPS_FAILURE;
+       }
+
+       if (wps_parse_msg(msg, &attr) < 0)
+               return WPS_FAILURE;
+
+       if (attr.msg_type == NULL) {
+               wpa_printf(MSG_DEBUG,  "WPS: No Message Type attribute");
+               return WPS_FAILURE;
+       }
+
+       if (*attr.msg_type != WPS_WSC_DONE) {
+               wpa_printf(MSG_DEBUG,  "WPS: Invalid Message Type %d",
+                          *attr.msg_type);
+               return WPS_FAILURE;
+       }
+
+#ifdef CONFIG_WPS_UPNP
+       if (wps->wps->wps_upnp && wps->ext_reg) {
+               wpa_printf(MSG_DEBUG,  "WPS: Negotiation using external "
+                          "Registrar completed successfully");
+               wps_device_store(wps->wps->registrar, &wps->peer_dev,
+                                wps->uuid_e);
+               return WPS_DONE;
+       }
+#endif /* CONFIG_WPS_UPNP */
+
+       if (attr.registrar_nonce == NULL ||
+           os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
+       {
+               wpa_printf(MSG_DEBUG,  "WPS: Mismatch in registrar nonce");
+               return WPS_FAILURE;
+       }
+
+       if (attr.enrollee_nonce == NULL ||
+           os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
+               wpa_printf(MSG_DEBUG,  "WPS: Mismatch in enrollee nonce");
+               return WPS_FAILURE;
+       }
+
+       wpa_printf(MSG_DEBUG,  "WPS: Negotiation completed successfully");
+       wps_device_store(wps->wps->registrar, &wps->peer_dev,
+                        wps->uuid_e);
+
+       if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->new_psk &&
+           wps->wps->ap && !wps->wps->registrar->disable_auto_conf) {
+               struct wps_credential cred;
+
+               wpa_printf(MSG_DEBUG,  "WPS: Moving to Configured state based "
+                          "on first Enrollee connection");
+
+               os_memset(&cred, 0, sizeof(cred));
+               os_memcpy(cred.ssid, wps->wps->ssid, wps->wps->ssid_len);
+               cred.ssid_len = wps->wps->ssid_len;
+               cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK;
+               cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES;
+               os_memcpy(cred.key, wps->new_psk, wps->new_psk_len);
+               cred.key_len = wps->new_psk_len;
+
+               wps->wps->wps_state = WPS_STATE_CONFIGURED;
+               wpa_hexdump_ascii_key(MSG_DEBUG,
+                                     "WPS: Generated random passphrase",
+                                     wps->new_psk, wps->new_psk_len);
+               if (wps->wps->cred_cb)
+                       wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
+
+               os_free(wps->new_psk);
+               wps->new_psk = NULL;
+       }
+
+       if (!wps->wps->ap && !wps->er)
+               wps_sta_cred_cb(wps);
+
+       if (wps->new_psk) {
+               if (wps_cb_new_psk(wps->wps->registrar, wps->mac_addr_e,
+                                  wps->new_psk, wps->new_psk_len)) {
+                       wpa_printf(MSG_DEBUG,  "WPS: Failed to configure the "
+                                  "new PSK");
+               }
+               os_free(wps->new_psk);
+               wps->new_psk = NULL;
+       }
+
+       wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e,
+                          wps->dev_password, wps->dev_password_len);
+
+       if (wps->pbc) {
+               wps_registrar_remove_pbc_session(wps->wps->registrar,
+                                                wps->uuid_e,
+                                                wps->p2p_dev_addr);
+               wps_registrar_pbc_completed(wps->wps->registrar);
+               os_get_time(&wps->wps->registrar->pbc_ignore_start);
+               os_memcpy(wps->wps->registrar->pbc_ignore_uuid, wps->uuid_e,
+                         WPS_UUID_LEN);
+       } else {
+#ifdef CONFIG_WPS_PIN
+               wps_registrar_pin_completed(wps->wps->registrar);
+#endif
+       }
+       /* TODO: maintain AuthorizedMACs somewhere separately for each ER and
+        * merge them into APs own list.. */
+
+       wps_success_event(wps->wps);
+
+       return WPS_DONE;
+}
+
+
+enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
+                                              enum wsc_op_code op_code,
+                                              const struct wpabuf *msg)
+{
+       enum wps_process_res ret;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Processing received message (len=%lu "
+                  "op_code=%d)",
+                  (unsigned long) wpabuf_len(msg), op_code);
+
+#ifdef CONFIG_WPS_UPNP
+       if (wps->wps->wps_upnp && op_code == WSC_MSG && wps->ext_reg == 1) {
+               struct wps_parse_attr attr;
+               if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type &&
+                   *attr.msg_type == WPS_M3)
+                       wps->ext_reg = 2; /* past M2/M2D phase */
+       }
+       if (wps->ext_reg > 1)
+               wps_registrar_free_pending_m2(wps->wps);
+       if (wps->wps->wps_upnp && wps->ext_reg &&
+           wps->wps->upnp_msgs == NULL &&
+           (op_code == WSC_MSG || op_code == WSC_Done || op_code == WSC_NACK))
+       {
+               struct wps_parse_attr attr;
+               int type;
+               if (wps_parse_msg(msg, &attr) < 0 || attr.msg_type == NULL)
+                       type = -1;
+               else
+                       type = *attr.msg_type;
+               wpa_printf(MSG_DEBUG,  "WPS: Sending received message (type %d)"
+                          " to external Registrar for processing", type);
+               upnp_wps_device_send_wlan_event(wps->wps->wps_upnp,
+                                               wps->mac_addr_e,
+                                               UPNP_WPS_WLANEVENT_TYPE_EAP,
+                                               msg);
+               if (op_code == WSC_MSG)
+                       return WPS_PENDING;
+       } else if (wps->wps->wps_upnp && wps->ext_reg && op_code == WSC_MSG) {
+               wpa_printf(MSG_DEBUG,  "WPS: Skip internal processing - using "
+                          "external Registrar");
+               return WPS_CONTINUE;
+       }
+#endif /* CONFIG_WPS_UPNP */
+
+       switch (op_code) {
+       case WSC_MSG:
+               return wps_process_wsc_msg(wps, msg);
+       case WSC_ACK:
+               if (wps_validate_wsc_ack(msg) < 0)
+                       return WPS_FAILURE;
+               return wps_process_wsc_ack(wps, msg);
+       case WSC_NACK:
+               if (wps_validate_wsc_nack(msg) < 0)
+                       return WPS_FAILURE;
+               return wps_process_wsc_nack(wps, msg);
+       case WSC_Done:
+               if (wps_validate_wsc_done(msg) < 0)
+                       return WPS_FAILURE;
+               ret = wps_process_wsc_done(wps, msg);
+               if (ret == WPS_FAILURE) {
+                       wps->state = SEND_WSC_NACK;
+                       wps_fail_event(wps->wps, WPS_WSC_DONE,
+                                      wps->config_error,
+                                      wps->error_indication);
+               }
+               return ret;
+       default:
+               wpa_printf(MSG_DEBUG,  "WPS: Unsupported op_code %d", op_code);
+               return WPS_FAILURE;
+       }
+}
+
+
+int wps_registrar_update_ie(struct wps_registrar *reg)
+{
+       return wps_set_ie(reg);
+}
+
+
+//static void ICACHE_FLASH_ATTR wps_registrar_set_selected_timeout(void *eloop_ctx,
+//                                            void *timeout_ctx)
+#if 0
+static void wps_registrar_set_selected_timeout(void *eloop_ctx)
+{
+       struct wps_registrar *reg = eloop_ctx;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Selected Registrar timeout - "
+                  "unselect internal Registrar");
+       reg->selected_registrar = 0;
+       reg->pbc = 0;
+       wps_registrar_selected_registrar_changed(reg);
+}
+#endif
+
+#ifdef CONFIG_WPS_UPNP
+static void wps_registrar_sel_reg_add(struct wps_registrar *reg,
+                                     struct subscription *s)
+{
+       int i, j;
+       wpa_printf(MSG_DEBUG,  "WPS: External Registrar selected (dev_pw_id=%d "
+                  "config_methods=0x%x)",
+                  s->dev_password_id, s->config_methods);
+       reg->sel_reg_union = 1;
+       if (reg->sel_reg_dev_password_id_override != DEV_PW_PUSHBUTTON)
+               reg->sel_reg_dev_password_id_override = s->dev_password_id;
+       if (reg->sel_reg_config_methods_override == -1)
+               reg->sel_reg_config_methods_override = 0;
+       reg->sel_reg_config_methods_override |= s->config_methods;
+       for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++)
+               if (is_zero_ether_addr(reg->authorized_macs_union[i]))
+                       break;
+       for (j = 0; i < WPS_MAX_AUTHORIZED_MACS && j < WPS_MAX_AUTHORIZED_MACS;
+            j++) {
+               if (is_zero_ether_addr(s->authorized_macs[j]))
+                       break;
+               wpa_printf(MSG_DEBUG,  "WPS: Add authorized MAC into union: "
+                          MACSTR, MAC2STR(s->authorized_macs[j]));
+               os_memcpy(reg->authorized_macs_union[i],
+                         s->authorized_macs[j], ETH_ALEN);
+               i++;
+       }
+       wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs union",
+                   (u8 *) reg->authorized_macs_union,
+                   sizeof(reg->authorized_macs_union));
+}
+#endif /* CONFIG_WPS_UPNP */
+
+
+static void wps_registrar_sel_reg_union(struct wps_registrar *reg)
+{
+#ifdef CONFIG_WPS_UPNP
+       struct subscription *s;
+
+       if (reg->wps->wps_upnp == NULL)
+               return;
+
+       dl_list_for_each(s, &reg->wps->wps_upnp->subscriptions,
+                        struct subscription, list) {
+               struct subscr_addr *sa;
+               sa = dl_list_first(&s->addr_list, struct subscr_addr, list);
+               if (sa) {
+                       wpa_printf(MSG_DEBUG,  "WPS: External Registrar %s:%d",
+                                  inet_ntoa(sa->saddr.sin_addr),
+                                  ntohs(sa->saddr.sin_port));
+               }
+               if (s->selected_registrar)
+                       wps_registrar_sel_reg_add(reg, s);
+               else
+                       wpa_printf(MSG_DEBUG,  "WPS: External Registrar not "
+                                  "selected");
+       }
+#endif /* CONFIG_WPS_UPNP */
+}
+
+
+/**
+ * wps_registrar_selected_registrar_changed - SetSelectedRegistrar change
+ * @reg: Registrar data from wps_registrar_init()
+ *
+ * This function is called when selected registrar state changes, e.g., when an
+ * AP receives a SetSelectedRegistrar UPnP message.
+ */
+void wps_registrar_selected_registrar_changed(struct wps_registrar *reg)
+{
+       wpa_printf(MSG_DEBUG,  "WPS: Selected registrar information changed");
+
+       reg->sel_reg_union = reg->selected_registrar;
+       reg->sel_reg_dev_password_id_override = -1;
+       reg->sel_reg_config_methods_override = -1;
+       os_memcpy(reg->authorized_macs_union, reg->authorized_macs,
+                 WPS_MAX_AUTHORIZED_MACS * ETH_ALEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs union (start with own)",
+                   (u8 *) reg->authorized_macs_union,
+                   sizeof(reg->authorized_macs_union));
+       if (reg->selected_registrar) {
+               u16 methods;
+
+               methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+#ifdef CONFIG_WPS2
+               methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
+                            WPS_CONFIG_PHY_PUSHBUTTON);
+#endif /* CONFIG_WPS2 */
+               if (reg->pbc) {
+                       reg->sel_reg_dev_password_id_override =
+                               DEV_PW_PUSHBUTTON;
+                       wps_set_pushbutton(&methods, reg->wps->config_methods);
+               }
+               wpa_printf(MSG_DEBUG,  "WPS: Internal Registrar selected "
+                          "(pbc=%d)", reg->pbc);
+               reg->sel_reg_config_methods_override = methods;
+       } else {
+        wpa_printf(MSG_DEBUG,  "WPS: Internal Registrar not selected");
+           return;
+       }
+
+       wps_registrar_sel_reg_union(reg);
+
+       wps_set_ie(reg);
+       wps_cb_set_sel_reg(reg);
+}
+
+
+int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr,
+                          char *buf, size_t buflen)
+{
+       struct wps_registrar_device *d;
+       int len = 0, ret;
+       char uuid[40];
+       char devtype[WPS_DEV_TYPE_BUFSIZE];
+
+       d = wps_device_get(reg, addr);
+       if (d == NULL)
+               return 0;
+       if (uuid_bin2str(d->uuid, uuid, sizeof(uuid)))
+               return 0;
+
+       ret = snprintf(buf + len, buflen - len,
+                         "wpsUuid=%s\n"
+                         "wpsPrimaryDeviceType=%s\n"
+                         "wpsDeviceName=%s\n"
+                         "wpsManufacturer=%s\n"
+                         "wpsModelName=%s\n"
+                         "wpsModelNumber=%s\n"
+                         "wpsSerialNumber=%s\n",
+                         uuid,
+                         wps_dev_type_bin2str(d->dev.pri_dev_type, devtype,
+                                              sizeof(devtype)),
+                         d->dev.device_name ? d->dev.device_name : "",
+                         d->dev.manufacturer ? d->dev.manufacturer : "",
+                         d->dev.model_name ? d->dev.model_name : "",
+                         d->dev.model_number ? d->dev.model_number : "",
+                         d->dev.serial_number ? d->dev.serial_number : "");
+       if (ret < 0 || (size_t) ret >= buflen - len)
+               return len;
+       len += ret;
+
+       return len;
+}
+
+
+int wps_registrar_config_ap(struct wps_registrar *reg,
+                           struct wps_credential *cred)
+{
+#ifdef CONFIG_WPS2
+       if (!(cred->encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP |
+                                WPS_ENCR_AES))) {
+               if (cred->encr_type & WPS_ENCR_WEP) {
+                       wpa_printf(MSG_INFO, "WPS: Reject new AP settings "
+                                  "due to WEP configuration");
+                       return -1;
+               }
+
+               wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
+                          "invalid encr_type 0x%x", cred->encr_type);
+               return -1;
+       }
+
+       if ((cred->encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) ==
+           WPS_ENCR_TKIP) {
+               wpa_printf(MSG_DEBUG,  "WPS: Upgrade encr_type TKIP -> "
+                          "TKIP+AES");
+               cred->encr_type |= WPS_ENCR_AES;
+       }
+
+       if ((cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
+           WPS_AUTH_WPAPSK) {
+               wpa_printf(MSG_DEBUG,  "WPS: Upgrade auth_type WPAPSK -> "
+                          "WPAPSK+WPA2PSK");
+               cred->auth_type |= WPS_AUTH_WPA2PSK;
+       }
+#endif /* CONFIG_WPS2 */
+
+       if (reg->wps->cred_cb)
+               return reg->wps->cred_cb(reg->wps->cb_ctx, cred);
+
+       return -1;
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
+                                  const u8 *pubkey_hash, u16 pw_id,
+                                  const u8 *dev_pw, size_t dev_pw_len)
+{
+       struct wps_nfc_pw_token *token;
+
+       if (dev_pw_len > WPS_OOB_DEVICE_PASSWORD_LEN)
+               return -1;
+
+       wps_free_nfc_pw_tokens(&reg->nfc_pw_tokens, pw_id);
+
+       token = os_zalloc(sizeof(*token));
+       if (token == NULL)
+               return -1;
+
+       os_memcpy(token->pubkey_hash, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+       token->pw_id = pw_id;
+       os_memcpy(token->dev_pw, dev_pw, dev_pw_len);
+       token->dev_pw_len = dev_pw_len;
+
+       dl_list_add(&reg->nfc_pw_tokens, &token->list);
+
+       reg->selected_registrar = 1;
+       reg->pbc = 0;
+       wps_registrar_add_authorized_mac(reg,
+                                        (u8 *) "\xff\xff\xff\xff\xff\xff");
+       wps_registrar_selected_registrar_changed(reg);
+       #if 0
+       eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+       eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
+                              wps_registrar_set_selected_timeout,
+                              reg, NULL);
+    #endif
+       return 0;
+}
+
+
+int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
+                                        const u8 *oob_dev_pw,
+                                        size_t oob_dev_pw_len)
+{
+       const u8 *pos, *hash, *dev_pw;
+       u16 id;
+       size_t dev_pw_len;
+
+       if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
+           WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
+           oob_dev_pw_len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
+           WPS_OOB_DEVICE_PASSWORD_LEN)
+               return -1;
+
+       hash = oob_dev_pw;
+       pos = oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN;
+       id = WPA_GET_BE16(pos);
+       dev_pw = pos + 2;
+       dev_pw_len = oob_dev_pw + oob_dev_pw_len - dev_pw;
+
+       wpa_printf(MSG_DEBUG,  "WPS: Add NFC Password Token for Password ID %u",
+                  id);
+
+       wpa_hexdump(MSG_DEBUG, "WPS: Public Key Hash",
+                   hash, WPS_OOB_PUBKEY_HASH_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "WPS: Device Password", dev_pw, dev_pw_len);
+
+       return wps_registrar_add_nfc_pw_token(reg, hash, id, dev_pw,
+                                             dev_pw_len);
+}
+
+
+void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg,
+                                      struct wps_nfc_pw_token *token)
+{
+       wps_registrar_remove_authorized_mac(reg,
+                                           (u8 *) "\xff\xff\xff\xff\xff\xff");
+       wps_registrar_selected_registrar_changed(reg);
+}
+
+#endif /* CONFIG_WPS_NFC */
diff --git a/components/wpa_supplicant/src/wps/wps_validate.c b/components/wpa_supplicant/src/wps/wps_validate.c
new file mode 100644 (file)
index 0000000..7f2ad5e
--- /dev/null
@@ -0,0 +1,2375 @@
+/*
+ * Wi-Fi Protected Setup - Strict protocol validation routines
+ * Copyright (c) 2010, Atheros Communications, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#include "wpa/includes.h"
+
+#include "wpa/common.h"
+#include "wps/wps_i.h"
+#include "wps/wps.h"
+
+
+#ifndef WPS_STRICT_ALL
+#define WPS_STRICT_WPS2
+#endif /* WPS_STRICT_ALL */
+
+
+static int wps_validate_version(const u8 *version, int mandatory)
+{
+       if (version == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Version attribute "
+                                  "missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if (*version != 0x10) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Version attribute "
+                          "value 0x%x", *version);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_version2(const u8 *version2, int mandatory)
+{
+       if (version2 == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Version2 attribute "
+                                  "missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if (*version2 < 0x20) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Version2 attribute "
+                          "value 0x%x", *version2);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_request_type(const u8 *request_type, int mandatory)
+{
+       if (request_type == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Request Type "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if (*request_type > 0x03) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Request Type "
+                          "attribute value 0x%x", *request_type);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_response_type(const u8 *response_type, int mandatory)
+{
+       if (response_type == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Response Type "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if (*response_type > 0x03) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Response Type "
+                          "attribute value 0x%x", *response_type);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int valid_config_methods(u16 val, int wps2)
+{
+       if (wps2) {
+               if ((val & 0x6000) && !(val & WPS_CONFIG_DISPLAY)) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Physical/Virtual "
+                                  "Display flag without old Display flag "
+                                  "set");
+                       return 0;
+               }
+               if (!(val & 0x6000) && (val & WPS_CONFIG_DISPLAY)) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Display flag "
+                                  "without Physical/Virtual Display flag");
+                       return 0;
+               }
+               if ((val & 0x0600) && !(val & WPS_CONFIG_PUSHBUTTON)) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Physical/Virtual "
+                                  "PushButton flag without old PushButton "
+                                  "flag set");
+                       return 0;
+               }
+               if (!(val & 0x0600) && (val & WPS_CONFIG_PUSHBUTTON)) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: PushButton flag "
+                                  "without Physical/Virtual PushButton flag");
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+
+static int wps_validate_config_methods(const u8 *config_methods, int wps2,
+                                      int mandatory)
+{
+       u16 val;
+
+       if (config_methods == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Configuration "
+                                  "Methods attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+
+       val = WPA_GET_BE16(config_methods);
+       if (!valid_config_methods(val, wps2)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration "
+                          "Methods attribute value 0x%04x", val);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_ap_config_methods(const u8 *config_methods, int wps2,
+                                         int mandatory)
+{
+       u16 val;
+
+       if (wps_validate_config_methods(config_methods, wps2, mandatory) < 0)
+               return -1;
+       if (config_methods == NULL)
+               return 0;
+       val = WPA_GET_BE16(config_methods);
+       if (val & WPS_CONFIG_PUSHBUTTON) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration "
+                          "Methods attribute value 0x%04x in AP info "
+                          "(PushButton not allowed for registering new ER)",
+                          val);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_uuid_e(const u8 *uuid_e, int mandatory)
+{
+       if (uuid_e == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: UUID-E "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       return 0;
+}
+
+
+static int wps_validate_uuid_r(const u8 *uuid_r, int mandatory)
+{
+       if (uuid_r == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: UUID-R "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       return 0;
+}
+
+
+static int wps_validate_primary_dev_type(const u8 *primary_dev_type,
+                                        int mandatory)
+{
+       if (primary_dev_type == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Primary Device Type "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       return 0;
+}
+
+
+static int wps_validate_rf_bands(const u8 *rf_bands, int mandatory)
+{
+       if (rf_bands == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: RF Bands "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if (*rf_bands != WPS_RF_24GHZ && *rf_bands != WPS_RF_50GHZ &&
+           *rf_bands != (WPS_RF_24GHZ | WPS_RF_50GHZ)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Rf Bands "
+                          "attribute value 0x%x", *rf_bands);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_assoc_state(const u8 *assoc_state, int mandatory)
+{
+       u16 val;
+       if (assoc_state == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Association State "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       val = WPA_GET_BE16(assoc_state);
+       if (val > 4) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Association State "
+                          "attribute value 0x%04x", val);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_config_error(const u8 *config_error, int mandatory)
+{
+       u16 val;
+
+       if (config_error == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Configuration Error "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       val = WPA_GET_BE16(config_error);
+       if (val > 18) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration Error "
+                          "attribute value 0x%04x", val);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_dev_password_id(const u8 *dev_password_id,
+                                       int mandatory)
+{
+       u16 val;
+
+       if (dev_password_id == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Device Password ID "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       val = WPA_GET_BE16(dev_password_id);
+       if (val >= 0x0006 && val <= 0x000f) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Device Password ID "
+                          "attribute value 0x%04x", val);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_manufacturer(const u8 *manufacturer, size_t len,
+                                    int mandatory)
+{
+       if (manufacturer == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Manufacturer "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if (len > 0 && manufacturer[len - 1] == 0) {
+               wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Manufacturer "
+                          "attribute value", manufacturer, len);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_model_name(const u8 *model_name, size_t len,
+                                  int mandatory)
+{
+       if (model_name == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Model Name "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if (len > 0 && model_name[len - 1] == 0) {
+               wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Model Name "
+                          "attribute value", model_name, len);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_model_number(const u8 *model_number, size_t len,
+                                    int mandatory)
+{
+       if (model_number == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Model Number "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if (len > 0 && model_number[len - 1] == 0) {
+               wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Model Number "
+                          "attribute value", model_number, len);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_serial_number(const u8 *serial_number, size_t len,
+                                     int mandatory)
+{
+       if (serial_number == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Serial Number "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if (len > 0 && serial_number[len - 1] == 0) {
+               wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Serial "
+                                 "Number attribute value",
+                                 serial_number, len);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_dev_name(const u8 *dev_name, size_t len,
+                                int mandatory)
+{
+       if (dev_name == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Device Name "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if (len > 0 && dev_name[len - 1] == 0) {
+               wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Device Name "
+                          "attribute value", dev_name, len);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_request_to_enroll(const u8 *request_to_enroll,
+                                         int mandatory)
+{
+       if (request_to_enroll == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Request to Enroll "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if (*request_to_enroll > 0x01) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Request to Enroll "
+                          "attribute value 0x%x", *request_to_enroll);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_req_dev_type(const u8 *req_dev_type[], size_t num,
+                                    int mandatory)
+{
+       if (num == 0) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Requested Device "
+                                  "Type attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       return 0;
+}
+
+
+static int wps_validate_wps_state(const u8 *wps_state, int mandatory)
+{
+       if (wps_state == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Wi-Fi Protected "
+                                  "Setup State attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if (*wps_state != WPS_STATE_NOT_CONFIGURED &&
+           *wps_state != WPS_STATE_CONFIGURED) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Wi-Fi Protected "
+                          "Setup State attribute value 0x%x", *wps_state);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_ap_setup_locked(const u8 *ap_setup_locked,
+                                       int mandatory)
+{
+       if (ap_setup_locked == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: AP Setup Locked "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if (*ap_setup_locked > 1) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid AP Setup Locked "
+                          "attribute value 0x%x", *ap_setup_locked);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_selected_registrar(const u8 *selected_registrar,
+                                          int mandatory)
+{
+       if (selected_registrar == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Selected Registrar "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if (*selected_registrar > 1) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Selected Registrar "
+                          "attribute value 0x%x", *selected_registrar);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_sel_reg_config_methods(const u8 *config_methods,
+                                              int wps2, int mandatory)
+{
+       u16 val;
+
+       if (config_methods == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Selected Registrar "
+                                  "Configuration Methods attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+
+       val = WPA_GET_BE16(config_methods);
+       if (!valid_config_methods(val, wps2)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Selected Registrar "
+                          "Configuration Methods attribute value 0x%04x",
+                          val);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_authorized_macs(const u8 *authorized_macs, size_t len,
+                                       int mandatory)
+{
+       if (authorized_macs == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Authorized MACs "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if (len > 30 && (len % ETH_ALEN) != 0) {
+               wpa_hexdump(MSG_INFO, "WPS-STRICT: Invalid Authorized "
+                           "MACs attribute value", authorized_macs, len);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_msg_type(const u8 *msg_type, int mandatory)
+{
+       if (msg_type == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Message Type "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if (*msg_type < WPS_Beacon || *msg_type > WPS_WSC_DONE) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Message Type "
+                          "attribute value 0x%x", *msg_type);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_mac_addr(const u8 *mac_addr, int mandatory)
+{
+       if (mac_addr == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: MAC Address "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if (mac_addr[0] & 0x01) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid MAC Address "
+                          "attribute value " MACSTR, MAC2STR(mac_addr));
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_enrollee_nonce(const u8 *enrollee_nonce, int mandatory)
+{
+       if (enrollee_nonce == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Enrollee Nonce "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       return 0;
+}
+
+
+static int wps_validate_registrar_nonce(const u8 *registrar_nonce,
+                                       int mandatory)
+{
+       if (registrar_nonce == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Registrar Nonce "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       return 0;
+}
+
+
+static int wps_validate_public_key(const u8 *public_key, size_t len,
+                                  int mandatory)
+{
+       if (public_key == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Public Key "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if (len != 192) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Public Key "
+                          "attribute length %d", (int) len);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int num_bits_set(u16 val)
+{
+       int c;
+       for (c = 0; val; c++)
+               val &= val - 1;
+       return c;
+}
+
+
+static int wps_validate_auth_type_flags(const u8 *flags, int mandatory)
+{
+       u16 val;
+
+       if (flags == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Authentication Type "
+                                  "Flags attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       val = WPA_GET_BE16(flags);
+       if ((val & ~WPS_AUTH_TYPES) || !(val & WPS_AUTH_WPA2PSK)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Authentication Type "
+                          "Flags attribute value 0x%04x", val);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_auth_type(const u8 *type, int mandatory)
+{
+       u16 val;
+
+       if (type == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Authentication Type "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       val = WPA_GET_BE16(type);
+       if ((val & ~WPS_AUTH_TYPES) || val == 0 ||
+           (num_bits_set(val) > 1 &&
+            val != (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK))) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Authentication Type "
+                          "attribute value 0x%04x", val);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_encr_type_flags(const u8 *flags, int mandatory)
+{
+       u16 val;
+
+       if (flags == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Encryption Type "
+                                  "Flags attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       val = WPA_GET_BE16(flags);
+       if ((val & ~WPS_ENCR_TYPES) || !(val & WPS_ENCR_AES)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encryption Type "
+                          "Flags attribute value 0x%04x", val);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_encr_type(const u8 *type, int mandatory)
+{
+       u16 val;
+
+       if (type == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Encryption Type "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       val = WPA_GET_BE16(type);
+       if ((val & ~WPS_ENCR_TYPES) || val == 0 ||
+           (num_bits_set(val) > 1 && val != (WPS_ENCR_TKIP | WPS_ENCR_AES))) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encryption Type "
+                          "attribute value 0x%04x", val);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_conn_type_flags(const u8 *flags, int mandatory)
+{
+       if (flags == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Connection Type "
+                                  "Flags attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if ((*flags & ~(WPS_CONN_ESS | WPS_CONN_IBSS)) ||
+           !(*flags & WPS_CONN_ESS)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Connection Type "
+                          "Flags attribute value 0x%02x", *flags);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_os_version(const u8 *os_version, int mandatory)
+{
+       if (os_version == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: OS Version "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       return 0;
+}
+
+
+static int wps_validate_authenticator(const u8 *authenticator, int mandatory)
+{
+       if (authenticator == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Authenticator "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       return 0;
+}
+
+
+static int wps_validate_e_hash1(const u8 *hash, int mandatory)
+{
+       if (hash == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: E-Hash1 "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       return 0;
+}
+
+
+static int wps_validate_e_hash2(const u8 *hash, int mandatory)
+{
+       if (hash == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: E-Hash2 "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       return 0;
+}
+
+
+static int wps_validate_r_hash1(const u8 *hash, int mandatory)
+{
+       if (hash == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: R-Hash1 "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       return 0;
+}
+
+
+static int wps_validate_r_hash2(const u8 *hash, int mandatory)
+{
+       if (hash == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: R-Hash2 "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       return 0;
+}
+
+
+static int wps_validate_encr_settings(const u8 *encr_settings, size_t len,
+                                  int mandatory)
+{
+       if (encr_settings == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Encrypted Settings "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if (len < 16) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encrypted Settings "
+                          "attribute length %d", (int) len);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_settings_delay_time(const u8 *delay, int mandatory)
+{
+       if (delay == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Settings Delay Time "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       return 0;
+}
+
+
+static int wps_validate_r_snonce1(const u8 *nonce, int mandatory)
+{
+       if (nonce == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: R-SNonce1 "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       return 0;
+}
+
+
+static int wps_validate_r_snonce2(const u8 *nonce, int mandatory)
+{
+       if (nonce == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: R-SNonce2 "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       return 0;
+}
+
+
+static int wps_validate_e_snonce1(const u8 *nonce, int mandatory)
+{
+       if (nonce == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: E-SNonce1 "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       return 0;
+}
+
+
+static int wps_validate_e_snonce2(const u8 *nonce, int mandatory)
+{
+       if (nonce == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: E-SNonce2 "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       return 0;
+}
+
+
+static int wps_validate_key_wrap_auth(const u8 *auth, int mandatory)
+{
+       if (auth == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Key Wrap "
+                                  "Authenticator attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       return 0;
+}
+
+
+static int wps_validate_ssid(const u8 *ssid, size_t ssid_len, int mandatory)
+{
+       if (ssid == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: SSID "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if (ssid_len == 0 || ssid[ssid_len - 1] == 0) {
+               wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid SSID "
+                                 "attribute value", ssid, ssid_len);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_network_key_index(const u8 *idx, int mandatory)
+{
+       if (idx == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Network Key Index "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       return 0;
+}
+
+
+static int wps_validate_network_idx(const u8 *idx, int mandatory)
+{
+       if (idx == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Network Index "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       return 0;
+}
+
+
+static int wps_validate_network_key(const u8 *key, size_t key_len,
+                                   const u8 *encr_type, int mandatory)
+{
+       if (key == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Network Key "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if (((encr_type == NULL || WPA_GET_BE16(encr_type) != WPS_ENCR_WEP) &&
+            key_len > 8 && key_len < 64 && key[key_len - 1] == 0) ||
+           key_len > 64) {
+               wpa_hexdump_ascii_key(MSG_INFO, "WPS-STRICT: Invalid Network "
+                                     "Key attribute value", key, key_len);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_network_key_shareable(const u8 *val, int mandatory)
+{
+       if (val == NULL) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Network Key "
+                                  "Shareable attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+       if (*val > 1) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Network Key "
+                          "Shareable attribute value 0x%x", *val);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wps_validate_cred(const u8 *cred, size_t len)
+{
+       struct wps_parse_attr *attr;
+       struct wpabuf buf;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+       if (cred == NULL) {
+               ret = -1;
+               goto _out;
+       }
+       wpabuf_set(&buf, cred, len);
+       if (wps_parse_msg(&buf, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse Credential");
+               ret = -1;
+               goto _out;
+       }
+
+       if (wps_validate_network_idx(attr->network_idx, 1) ||
+           wps_validate_ssid(attr->ssid, attr->ssid_len, 1) ||
+           wps_validate_auth_type(attr->auth_type, 1) ||
+           wps_validate_encr_type(attr->encr_type, 1) ||
+           wps_validate_network_key_index(attr->network_key_idx, 0) ||
+           wps_validate_network_key(attr->network_key, attr->network_key_len,
+                                    attr->encr_type, 1) ||
+           wps_validate_mac_addr(attr->mac_addr, 1) ||
+           wps_validate_network_key_shareable(attr->network_key_shareable, 0))
+       {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Credential");
+               ret = -1;
+               goto _out;
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+static int wps_validate_credential(const u8 *cred[], size_t len[], size_t num,
+                                  int mandatory)
+{
+       size_t i;
+
+       if (num == 0) {
+               if (mandatory) {
+                       wpa_printf(MSG_INFO, "WPS-STRICT: Credential "
+                                  "attribute missing");
+                       return -1;
+               }
+               return 0;
+       }
+
+       for (i = 0; i < num; i++) {
+               if (wps_validate_cred(cred[i], len[i]) < 0)
+                       return -1;
+       }
+
+       return 0;
+}
+
+
+int wps_validate_beacon(const struct wpabuf *wps_ie)
+{
+       struct wps_parse_attr *attr;
+       int wps2, sel_reg;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+       if (wps_ie == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in Beacon frame");
+               ret = -1;
+               goto _out;
+       }
+       if (wps_parse_msg(wps_ie, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+                          "Beacon frame");
+               ret = -1;
+               goto _out;
+       }
+
+       wps2 = attr->version2 != NULL;
+       sel_reg = attr->selected_registrar != NULL &&
+               *attr->selected_registrar != 0;
+       if (wps_validate_version(attr->version, 1) ||
+           wps_validate_wps_state(attr->wps_state, 1) ||
+           wps_validate_ap_setup_locked(attr->ap_setup_locked, 0) ||
+           wps_validate_selected_registrar(attr->selected_registrar, 0) ||
+           wps_validate_dev_password_id(attr->dev_password_id, sel_reg) ||
+           wps_validate_sel_reg_config_methods(attr->sel_reg_config_methods,
+                                               wps2, sel_reg) ||
+           wps_validate_uuid_e(attr->uuid_e, 0) ||
+           wps_validate_rf_bands(attr->rf_bands, 0) ||
+           wps_validate_version2(attr->version2, wps2) ||
+           wps_validate_authorized_macs(attr->authorized_macs,
+                                        attr->authorized_macs_len, 0)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Beacon frame");
+               ret = -1;
+               goto _out;
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, int probe,
+                                  const u8 *addr)
+{
+       struct wps_parse_attr *attr;
+       int wps2, sel_reg;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+       if (wps_ie == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+                          "%sProbe Response frame", probe ? "" : "Beacon/");
+               ret = -1;
+               goto _out;
+       }
+       if (wps_parse_msg(wps_ie, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+                          "%sProbe Response frame", probe ? "" : "Beacon/");
+               ret = -1;
+               goto _out;
+       }
+
+       wps2 = attr->version2 != NULL;
+       sel_reg = attr->selected_registrar != NULL &&
+               *attr->selected_registrar != 0;
+       if (wps_validate_version(attr->version, 1) ||
+           wps_validate_wps_state(attr->wps_state, 1) ||
+           wps_validate_ap_setup_locked(attr->ap_setup_locked, 0) ||
+           wps_validate_selected_registrar(attr->selected_registrar, 0) ||
+           wps_validate_dev_password_id(attr->dev_password_id, sel_reg) ||
+           wps_validate_sel_reg_config_methods(attr->sel_reg_config_methods,
+                                               wps2, sel_reg) ||
+           wps_validate_response_type(attr->response_type, probe) ||
+           wps_validate_uuid_e(attr->uuid_e, probe) ||
+           wps_validate_manufacturer(attr->manufacturer, attr->manufacturer_len,
+                                     probe) ||
+           wps_validate_model_name(attr->model_name, attr->model_name_len,
+                                   probe) ||
+           wps_validate_model_number(attr->model_number, attr->model_number_len,
+                                     probe) ||
+           wps_validate_serial_number(attr->serial_number,
+                                      attr->serial_number_len, probe) ||
+           wps_validate_primary_dev_type(attr->primary_dev_type, probe) ||
+           wps_validate_dev_name(attr->dev_name, attr->dev_name_len, probe) ||
+           wps_validate_ap_config_methods(attr->config_methods, wps2, probe) ||
+           wps_validate_rf_bands(attr->rf_bands, 0) ||
+           wps_validate_version2(attr->version2, wps2) ||
+           wps_validate_authorized_macs(attr->authorized_macs,
+                                        attr->authorized_macs_len, 0)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid %sProbe Response "
+                          "frame from " MACSTR, probe ? "" : "Beacon/",
+                          MAC2STR(addr));
+#ifdef WPS_STRICT_WPS2
+               if (wps2) {
+                       ret = -1;
+                       goto _out;
+               }
+#else /* WPS_STRICT_WPS2 */
+               ret = -1;
+               goto _out;
+#endif /* WPS_STRICT_WPS2 */
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+int wps_validate_probe_req(const struct wpabuf *wps_ie, const u8 *addr)
+{
+       struct wps_parse_attr *attr;
+       int wps2;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+       if (wps_ie == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+                          "Probe Request frame");
+               ret = -1;
+               goto _out;
+       }
+       if (wps_parse_msg(wps_ie, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+                          "Probe Request frame");
+               ret = -1;
+               goto _out;
+       }
+
+       wps2 = attr->version2 != NULL;
+       if (wps_validate_version(attr->version, 1) ||
+           wps_validate_request_type(attr->request_type, 1) ||
+           wps_validate_config_methods(attr->config_methods, wps2, 1) ||
+           wps_validate_uuid_e(attr->uuid_e, attr->uuid_r == NULL) ||
+           wps_validate_uuid_r(attr->uuid_r, attr->uuid_e == NULL) ||
+           wps_validate_primary_dev_type(attr->primary_dev_type, 1) ||
+           wps_validate_rf_bands(attr->rf_bands, 1) ||
+           wps_validate_assoc_state(attr->assoc_state, 1) ||
+           wps_validate_config_error(attr->config_error, 1) ||
+           wps_validate_dev_password_id(attr->dev_password_id, 1) ||
+           wps_validate_version2(attr->version2, wps2) ||
+           wps_validate_manufacturer(attr->manufacturer, attr->manufacturer_len,
+                                     wps2) ||
+           wps_validate_model_name(attr->model_name, attr->model_name_len,
+                                   wps2) ||
+           wps_validate_model_number(attr->model_number, attr->model_number_len,
+                                     wps2) ||
+           wps_validate_dev_name(attr->dev_name, attr->dev_name_len, wps2) ||
+           wps_validate_request_to_enroll(attr->request_to_enroll, 0) ||
+           wps_validate_req_dev_type(attr->req_dev_type, attr->num_req_dev_type,
+                                     0)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Probe Request "
+                          "frame from " MACSTR, MAC2STR(addr));
+               ret = -1;
+               goto _out;
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+int wps_validate_assoc_req(const struct wpabuf *wps_ie)
+{
+       struct wps_parse_attr *attr;
+       int wps2;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+       if (wps_ie == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+                          "(Re)Association Request frame");
+               ret = -1;
+               goto _out;
+       }
+       if (wps_parse_msg(wps_ie, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+                          "(Re)Association Request frame");
+               ret = -1;
+               goto _out;
+       }
+
+       wps2 = attr->version2 != NULL;
+       if (wps_validate_version(attr->version, 1) ||
+           wps_validate_request_type(attr->request_type, 1) ||
+           wps_validate_version2(attr->version2, wps2)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid (Re)Association "
+                          "Request frame");
+               ret = -1;
+               goto _out;
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+int wps_validate_assoc_resp(const struct wpabuf *wps_ie)
+{
+       struct wps_parse_attr *attr;
+       int wps2;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+
+       if (wps_ie == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
+                          "(Re)Association Response frame");
+               ret = -1;
+               goto _out;
+       }
+       if (wps_parse_msg(wps_ie, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
+                          "(Re)Association Response frame");
+               ret = -1;
+               goto _out;
+       }
+
+       wps2 = attr->version2 != NULL;
+       if (wps_validate_version(attr->version, 1) ||
+           wps_validate_response_type(attr->response_type, 1) ||
+           wps_validate_version2(attr->version2, wps2)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid (Re)Association "
+                          "Response frame");
+               ret = -1;
+               goto _out;
+       }
+
+       ret = 0;
+_out:
+       if(attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+int wps_validate_m1(const struct wpabuf *tlvs)
+{
+       struct wps_parse_attr *attr;
+       int wps2;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+
+       if (tlvs == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M1");
+               ret = -1;
+               goto _out;
+       }
+       if (wps_parse_msg(tlvs, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+                          "in M1");
+               ret = -1;
+               goto _out;
+       }
+
+       wps2 = attr->version2 != NULL;
+       if (wps_validate_version(attr->version, 1) ||
+           wps_validate_msg_type(attr->msg_type, 1) ||
+           wps_validate_uuid_e(attr->uuid_e, 1) ||
+           wps_validate_mac_addr(attr->mac_addr, 1) ||
+           wps_validate_enrollee_nonce(attr->enrollee_nonce, 1) ||
+           wps_validate_public_key(attr->public_key, attr->public_key_len, 1) ||
+           wps_validate_auth_type_flags(attr->auth_type_flags, 1) ||
+           wps_validate_encr_type_flags(attr->encr_type_flags, 1) ||
+           wps_validate_conn_type_flags(attr->conn_type_flags, 1) ||
+           wps_validate_config_methods(attr->config_methods, wps2, 1) ||
+           wps_validate_wps_state(attr->wps_state, 1) ||
+           wps_validate_manufacturer(attr->manufacturer, attr->manufacturer_len,
+                                     1) ||
+           wps_validate_model_name(attr->model_name, attr->model_name_len, 1) ||
+           wps_validate_model_number(attr->model_number, attr->model_number_len,
+                                     1) ||
+           wps_validate_serial_number(attr->serial_number,
+                                      attr->serial_number_len, 1) ||
+           wps_validate_primary_dev_type(attr->primary_dev_type, 1) ||
+           wps_validate_dev_name(attr->dev_name, attr->dev_name_len, 1) ||
+           wps_validate_rf_bands(attr->rf_bands, 1) ||
+           wps_validate_assoc_state(attr->assoc_state, 1) ||
+           wps_validate_dev_password_id(attr->dev_password_id, 1) ||
+           wps_validate_config_error(attr->config_error, 1) ||
+           wps_validate_os_version(attr->os_version, 1) ||
+           wps_validate_version2(attr->version2, wps2) ||
+           wps_validate_request_to_enroll(attr->request_to_enroll, 0)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M1");
+#ifdef WPS_STRICT_WPS2
+               if (wps2) {
+                       ret = -1;
+                       goto _out;
+               }
+#else /* WPS_STRICT_WPS2 */
+               ret = -1;
+               goto _out;
+#endif /* WPS_STRICT_WPS2 */
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+int wps_validate_m2(const struct wpabuf *tlvs)
+{
+       struct wps_parse_attr *attr;
+       int wps2;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+
+       if (tlvs == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M2");
+               ret = -1;
+               goto _out;
+       }
+       if (wps_parse_msg(tlvs, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+                          "in M2");
+               ret = -1;
+               goto _out;
+       }
+
+       wps2 = attr->version2 != NULL;
+       if (wps_validate_version(attr->version, 1) ||
+           wps_validate_msg_type(attr->msg_type, 1) ||
+           wps_validate_enrollee_nonce(attr->enrollee_nonce, 1) ||
+           wps_validate_registrar_nonce(attr->registrar_nonce, 1) ||
+           wps_validate_uuid_r(attr->uuid_r, 1) ||
+           wps_validate_public_key(attr->public_key, attr->public_key_len, 1) ||
+           wps_validate_auth_type_flags(attr->auth_type_flags, 1) ||
+           wps_validate_encr_type_flags(attr->encr_type_flags, 1) ||
+           wps_validate_conn_type_flags(attr->conn_type_flags, 1) ||
+           wps_validate_config_methods(attr->config_methods, wps2, 1) ||
+           wps_validate_manufacturer(attr->manufacturer, attr->manufacturer_len,
+                                     1) ||
+           wps_validate_model_name(attr->model_name, attr->model_name_len, 1) ||
+           wps_validate_model_number(attr->model_number, attr->model_number_len,
+                                     1) ||
+           wps_validate_serial_number(attr->serial_number,
+                                      attr->serial_number_len, 1) ||
+           wps_validate_primary_dev_type(attr->primary_dev_type, 1) ||
+           wps_validate_dev_name(attr->dev_name, attr->dev_name_len, 1) ||
+           wps_validate_rf_bands(attr->rf_bands, 1) ||
+           wps_validate_assoc_state(attr->assoc_state, 1) ||
+           wps_validate_config_error(attr->config_error, 1) ||
+           wps_validate_dev_password_id(attr->dev_password_id, 1) ||
+           wps_validate_os_version(attr->os_version, 1) ||
+           wps_validate_version2(attr->version2, wps2) ||
+           wps_validate_authenticator(attr->authenticator, 1)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M2");
+#ifdef WPS_STRICT_WPS2
+               if (wps2) {
+                       ret = -1;
+                       goto _out;
+               }
+#else /* WPS_STRICT_WPS2 */
+               ret = -1;
+               goto _out;
+#endif /* WPS_STRICT_WPS2 */
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+int wps_validate_m2d(const struct wpabuf *tlvs)
+{
+       struct wps_parse_attr *attr;
+       int wps2;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+       if (tlvs == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M2D");
+               ret = -1;
+               goto _out;
+       }
+       if (wps_parse_msg(tlvs, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+                          "in M2D");
+               ret = -1;
+               goto _out;
+       }
+
+       wps2 = attr->version2 != NULL;
+       if (wps_validate_version(attr->version, 1) ||
+           wps_validate_msg_type(attr->msg_type, 1) ||
+           wps_validate_enrollee_nonce(attr->enrollee_nonce, 1) ||
+           wps_validate_registrar_nonce(attr->registrar_nonce, 1) ||
+           wps_validate_uuid_r(attr->uuid_r, 1) ||
+           wps_validate_auth_type_flags(attr->auth_type_flags, 1) ||
+           wps_validate_encr_type_flags(attr->encr_type_flags, 1) ||
+           wps_validate_conn_type_flags(attr->conn_type_flags, 1) ||
+           wps_validate_config_methods(attr->config_methods, wps2, 1) ||
+           wps_validate_manufacturer(attr->manufacturer, attr->manufacturer_len,
+                                     1) ||
+           wps_validate_model_name(attr->model_name, attr->model_name_len, 1) ||
+           wps_validate_model_number(attr->model_number, attr->model_number_len,
+                                     1) ||
+           wps_validate_serial_number(attr->serial_number,
+                                      attr->serial_number_len, 1) ||
+           wps_validate_primary_dev_type(attr->primary_dev_type, 1) ||
+           wps_validate_dev_name(attr->dev_name, attr->dev_name_len, 1) ||
+           wps_validate_rf_bands(attr->rf_bands, 1) ||
+           wps_validate_assoc_state(attr->assoc_state, 1) ||
+           wps_validate_config_error(attr->config_error, 1) ||
+           wps_validate_os_version(attr->os_version, 1) ||
+           wps_validate_version2(attr->version2, wps2)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M2D");
+#ifdef WPS_STRICT_WPS2
+               if (wps2) {
+                       ret = -1;
+                       goto _out;
+               }
+#else /* WPS_STRICT_WPS2 */
+               ret = -1;
+               goto _out;
+#endif /* WPS_STRICT_WPS2 */
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+int wps_validate_m3(const struct wpabuf *tlvs)
+{
+       struct wps_parse_attr *attr;
+       int wps2;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+       if (tlvs == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M3");
+               ret = -1;
+               goto _out;
+       }
+       if (wps_parse_msg(tlvs, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+                          "in M3");
+               ret = -1;
+               goto _out;
+       }
+
+       wps2 = attr->version2 != NULL;
+       if (wps_validate_version(attr->version, 1) ||
+           wps_validate_msg_type(attr->msg_type, 1) ||
+           wps_validate_registrar_nonce(attr->registrar_nonce, 1) ||
+           wps_validate_e_hash1(attr->e_hash1, 1) ||
+           wps_validate_e_hash2(attr->e_hash2, 1) ||
+           wps_validate_version2(attr->version2, wps2) ||
+           wps_validate_authenticator(attr->authenticator, 1)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M3");
+#ifdef WPS_STRICT_WPS2
+               if (wps2) {
+                       ret = -1;
+                       goto _out;
+               }
+#else /* WPS_STRICT_WPS2 */
+               ret = -1;
+               goto _out;
+#endif /* WPS_STRICT_WPS2 */
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+int wps_validate_m4(const struct wpabuf *tlvs)
+{
+       struct wps_parse_attr *attr;
+       int wps2;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+       if (tlvs == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M4");
+               ret = -1;
+               goto _out;
+       }
+       if (wps_parse_msg(tlvs, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+                          "in M4");
+               ret = -1;
+               goto _out;
+       }
+
+       wps2 = attr->version2 != NULL;
+       if (wps_validate_version(attr->version, 1) ||
+           wps_validate_msg_type(attr->msg_type, 1) ||
+           wps_validate_enrollee_nonce(attr->enrollee_nonce, 1) ||
+           wps_validate_r_hash1(attr->r_hash1, 1) ||
+           wps_validate_r_hash2(attr->r_hash2, 1) ||
+           wps_validate_encr_settings(attr->encr_settings,
+                                      attr->encr_settings_len, 1) ||
+           wps_validate_version2(attr->version2, wps2) ||
+           wps_validate_authenticator(attr->authenticator, 1)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M4");
+#ifdef WPS_STRICT_WPS2
+               if (wps2) {
+                       ret = -1;
+                       goto _out;
+               }
+#else /* WPS_STRICT_WPS2 */
+               ret = -1;
+               goto _out;
+#endif /* WPS_STRICT_WPS2 */
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2)
+{
+       struct wps_parse_attr *attr;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+
+       if (tlvs == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M4 encrypted "
+                          "settings");
+               ret = -1;
+               goto _out;
+       }
+       if (wps_parse_msg(tlvs, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+                          "in M4 encrypted settings");
+               ret = -1;
+               goto _out;
+       }
+
+       if (wps_validate_r_snonce1(attr->r_snonce1, 1) ||
+           wps_validate_key_wrap_auth(attr->key_wrap_auth, 1)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M4 encrypted "
+                          "settings");
+#ifdef WPS_STRICT_WPS2
+               if (wps2) {
+                       ret = -1;
+                       goto _out;
+               }
+#else /* WPS_STRICT_WPS2 */
+               ret = -1;
+               goto _out;
+#endif /* WPS_STRICT_WPS2 */
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+int wps_validate_m5(const struct wpabuf *tlvs)
+{
+       struct wps_parse_attr *attr;
+       int wps2;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+       if (tlvs == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M5");
+               ret = -1;
+               goto _out;
+       }
+       if (wps_parse_msg(tlvs, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+                          "in M5");
+               ret = -1;
+               goto _out;
+       }
+
+       wps2 = attr->version2 != NULL;
+       if (wps_validate_version(attr->version, 1) ||
+           wps_validate_msg_type(attr->msg_type, 1) ||
+           wps_validate_registrar_nonce(attr->registrar_nonce, 1) ||
+           wps_validate_encr_settings(attr->encr_settings,
+                                      attr->encr_settings_len, 1) ||
+           wps_validate_version2(attr->version2, wps2) ||
+           wps_validate_authenticator(attr->authenticator, 1)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M5");
+#ifdef WPS_STRICT_WPS2
+               if (wps2) {
+                       ret = -1;
+                       goto _out;
+               }
+#else /* WPS_STRICT_WPS2 */
+               ret = -1;
+               goto _out;
+#endif /* WPS_STRICT_WPS2 */
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2)
+{
+       struct wps_parse_attr *attr;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+       if (tlvs == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M5 encrypted "
+                          "settings");
+               ret = -1;
+               goto _out;
+       }
+       if (wps_parse_msg(tlvs, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+                          "in M5 encrypted settings");
+               ret = -1;
+               goto _out;
+       }
+
+       if (wps_validate_e_snonce1(attr->e_snonce1, 1) ||
+           wps_validate_key_wrap_auth(attr->key_wrap_auth, 1)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M5 encrypted "
+                          "settings");
+#ifdef WPS_STRICT_WPS2
+               if (wps2) {
+                       ret = -1;
+                       goto _out;
+               }
+#else /* WPS_STRICT_WPS2 */
+               ret = -1;
+               goto _out;
+#endif /* WPS_STRICT_WPS2 */
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+int wps_validate_m6(const struct wpabuf *tlvs)
+{
+       struct wps_parse_attr *attr;
+       int wps2;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+       if (tlvs == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M6");
+               ret = -1;
+               goto _out;
+       }
+       if (wps_parse_msg(tlvs, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+                          "in M6");
+               ret = -1;
+               goto _out;
+       }
+
+       wps2 = attr->version2 != NULL;
+       if (wps_validate_version(attr->version, 1) ||
+           wps_validate_msg_type(attr->msg_type, 1) ||
+           wps_validate_enrollee_nonce(attr->enrollee_nonce, 1) ||
+           wps_validate_encr_settings(attr->encr_settings,
+                                      attr->encr_settings_len, 1) ||
+           wps_validate_version2(attr->version2, wps2) ||
+           wps_validate_authenticator(attr->authenticator, 1)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M6");
+#ifdef WPS_STRICT_WPS2
+               if (wps2) {
+                       ret = -1;
+                       goto _out;
+               }
+#else /* WPS_STRICT_WPS2 */
+               ret = -1;
+               goto _out;
+#endif /* WPS_STRICT_WPS2 */
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2)
+{
+       struct wps_parse_attr *attr;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+       if (tlvs == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M6 encrypted "
+                          "settings");
+               ret = -1;
+               goto _out;
+       }
+       if (wps_parse_msg(tlvs, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+                          "in M6 encrypted settings");
+               ret = -1;
+               goto _out;
+       }
+
+       if (wps_validate_r_snonce2(attr->r_snonce2, 1) ||
+           wps_validate_key_wrap_auth(attr->key_wrap_auth, 1)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M6 encrypted "
+                          "settings");
+#ifdef WPS_STRICT_WPS2
+               if (wps2) {
+                       ret = -1;
+                       goto _out;
+               }
+#else /* WPS_STRICT_WPS2 */
+               ret = -1;
+               goto _out;
+#endif /* WPS_STRICT_WPS2 */
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+int wps_validate_m7(const struct wpabuf *tlvs)
+{
+       struct wps_parse_attr *attr;
+       int wps2;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+       if (tlvs == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M7");
+               ret = -1;
+               goto _out;
+       }
+       if (wps_parse_msg(tlvs, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+                          "in M7");
+               ret = -1;
+               goto _out;
+       }
+
+       wps2 = attr->version2 != NULL;
+       if (wps_validate_version(attr->version, 1) ||
+           wps_validate_msg_type(attr->msg_type, 1) ||
+           wps_validate_registrar_nonce(attr->registrar_nonce, 1) ||
+           wps_validate_encr_settings(attr->encr_settings,
+                                      attr->encr_settings_len, 1) ||
+           wps_validate_settings_delay_time(attr->settings_delay_time, 0) ||
+           wps_validate_version2(attr->version2, wps2) ||
+           wps_validate_authenticator(attr->authenticator, 1)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M7");
+#ifdef WPS_STRICT_WPS2
+               if (wps2) {
+                       ret = -1;
+                       goto _out;
+               }
+#else /* WPS_STRICT_WPS2 */
+               ret = -1;
+               goto _out;
+#endif /* WPS_STRICT_WPS2 */
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, int wps2)
+{
+       struct wps_parse_attr *attr;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+       
+       if (tlvs == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M7 encrypted "
+                          "settings");
+               ret = -1;
+               goto _out;
+       }
+       if (wps_parse_msg(tlvs, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+                          "in M7 encrypted settings");
+               ret = -1;
+               goto _out;
+       }
+
+       if (wps_validate_e_snonce2(attr->e_snonce2, 1) ||
+           wps_validate_ssid(attr->ssid, attr->ssid_len, !ap) ||
+           wps_validate_mac_addr(attr->mac_addr, !ap) ||
+           wps_validate_auth_type(attr->auth_type, !ap) ||
+           wps_validate_encr_type(attr->encr_type, !ap) ||
+           wps_validate_network_key_index(attr->network_key_idx, 0) ||
+           wps_validate_network_key(attr->network_key, attr->network_key_len,
+                                    attr->encr_type, !ap) ||
+           wps_validate_key_wrap_auth(attr->key_wrap_auth, 1)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M7 encrypted "
+                          "settings");
+#ifdef WPS_STRICT_WPS2
+               if (wps2) {
+                       ret = -1;
+                       goto _out;
+               }
+#else /* WPS_STRICT_WPS2 */
+               ret = -1;
+               goto _out;
+#endif /* WPS_STRICT_WPS2 */
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+int wps_validate_m8(const struct wpabuf *tlvs)
+{
+       struct wps_parse_attr *attr;
+       int wps2;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+       if (tlvs == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M8");
+               ret = -1;
+               goto _out;
+       }
+       if (wps_parse_msg(tlvs, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+                          "in M8");
+               ret = -1;
+               goto _out;
+       }
+
+       wps2 = attr->version2 != NULL;
+       if (wps_validate_version(attr->version, 1) ||
+           wps_validate_msg_type(attr->msg_type, 1) ||
+           wps_validate_enrollee_nonce(attr->enrollee_nonce, 1) ||
+           wps_validate_encr_settings(attr->encr_settings,
+                                      attr->encr_settings_len, 1) ||
+           wps_validate_version2(attr->version2, wps2) ||
+           wps_validate_authenticator(attr->authenticator, 1)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M8");
+#ifdef WPS_STRICT_WPS2
+               if (wps2) {
+                       ret = -1;
+                       goto _out;
+               }
+#else /* WPS_STRICT_WPS2 */
+               ret = -1;
+               goto _out;
+#endif /* WPS_STRICT_WPS2 */
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, int wps2)
+{
+       struct wps_parse_attr *attr;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+       if (tlvs == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M8 encrypted "
+                          "settings");
+               ret = -1;
+               goto _out;
+       }
+       if (wps_parse_msg(tlvs, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+                          "in M8 encrypted settings");
+               ret = -1;
+               goto _out;
+       }
+
+       if (wps_validate_ssid(attr->ssid, attr->ssid_len, ap) ||
+           wps_validate_auth_type(attr->auth_type, ap) ||
+           wps_validate_encr_type(attr->encr_type, ap) ||
+           wps_validate_network_key_index(attr->network_key_idx, 0) ||
+           wps_validate_mac_addr(attr->mac_addr, ap) ||
+           wps_validate_credential(attr->cred, attr->cred_len, attr->num_cred,
+                                   !ap) ||
+           wps_validate_key_wrap_auth(attr->key_wrap_auth, 1)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M8 encrypted "
+                          "settings");
+#ifdef WPS_STRICT_WPS2
+               if (wps2) {
+                       ret = -1;
+                       goto _out;
+               }
+#else /* WPS_STRICT_WPS2 */
+               ret = -1;
+               goto _out;
+#endif /* WPS_STRICT_WPS2 */
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+int wps_validate_wsc_ack(const struct wpabuf *tlvs)
+{
+       struct wps_parse_attr *attr;
+       int wps2;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+       if (tlvs == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_ACK");
+               ret = -1;
+               goto _out;
+       }
+       if (wps_parse_msg(tlvs, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+                          "in WSC_ACK");
+               ret = -1;
+               goto _out;
+       }
+
+       wps2 = attr->version2 != NULL;
+       if (wps_validate_version(attr->version, 1) ||
+           wps_validate_msg_type(attr->msg_type, 1) ||
+           wps_validate_enrollee_nonce(attr->enrollee_nonce, 1) ||
+           wps_validate_registrar_nonce(attr->registrar_nonce, 1) ||
+           wps_validate_version2(attr->version2, wps2)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_ACK");
+#ifdef WPS_STRICT_WPS2
+               if (wps2) {
+                       ret = -1;
+                       goto _out;
+               }
+#else /* WPS_STRICT_WPS2 */
+               ret = -1;
+               goto _out;
+#endif /* WPS_STRICT_WPS2 */
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+int wps_validate_wsc_nack(const struct wpabuf *tlvs)
+{
+       struct wps_parse_attr *attr;
+       int wps2;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+       if (tlvs == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_NACK");
+               ret = -1;
+       }
+       if (wps_parse_msg(tlvs, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+                          "in WSC_NACK");
+               ret = -1;
+               goto _out;
+       }
+
+       wps2 = attr->version2 != NULL;
+       if (wps_validate_version(attr->version, 1) ||
+           wps_validate_msg_type(attr->msg_type, 1) ||
+           wps_validate_enrollee_nonce(attr->enrollee_nonce, 1) ||
+           wps_validate_registrar_nonce(attr->registrar_nonce, 1) ||
+           wps_validate_config_error(attr->config_error, 1) ||
+           wps_validate_version2(attr->version2, wps2)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_NACK");
+#ifdef WPS_STRICT_WPS2
+               if (wps2) {
+                       ret = -1;
+                       goto _out;
+               }
+#else /* WPS_STRICT_WPS2 */
+               goto _out;
+               ret =  -1;
+#endif /* WPS_STRICT_WPS2 */
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+int wps_validate_wsc_done(const struct wpabuf *tlvs)
+{
+       struct wps_parse_attr *attr;
+       int wps2;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+       if (tlvs == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_Done");
+               ret = -1;
+               goto _out;
+       }
+       if (wps_parse_msg(tlvs, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+                          "in WSC_Done");
+               ret = -1;
+               goto _out;
+       }
+
+       wps2 = attr->version2 != NULL;
+       if (wps_validate_version(attr->version, 1) ||
+           wps_validate_msg_type(attr->msg_type, 1) ||
+           wps_validate_enrollee_nonce(attr->enrollee_nonce, 1) ||
+           wps_validate_registrar_nonce(attr->registrar_nonce, 1) ||
+           wps_validate_version2(attr->version2, wps2)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_Done");
+#ifdef WPS_STRICT_WPS2
+               if (wps2) {
+                       ret = -1;
+                       goto _out;
+               }
+#else /* WPS_STRICT_WPS2 */
+               ret = -1;
+               goto _out;
+#endif /* WPS_STRICT_WPS2 */
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}
+
+
+int wps_validate_upnp_set_selected_registrar(const struct wpabuf *tlvs)
+{
+       struct wps_parse_attr *attr;
+       int wps2;
+       int sel_reg;
+       int ret;
+
+       attr = (struct wps_parse_attr *)os_zalloc(sizeof(struct wps_parse_attr));
+       if (attr == NULL) {
+               ret = -99;
+               goto _out;
+       }
+
+       if (tlvs == NULL) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in "
+                          "SetSelectedRegistrar");
+               ret = -1;
+               goto _out;
+       }
+       if (wps_parse_msg(tlvs, attr) < 0) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
+                          "in SetSelectedRegistrar");
+               ret = -1;
+               goto _out;
+       }
+
+       wps2 = attr->version2 != NULL;
+       sel_reg = attr->selected_registrar != NULL &&
+               *attr->selected_registrar != 0;
+       if (wps_validate_version(attr->version, 1) ||
+           wps_validate_dev_password_id(attr->dev_password_id, sel_reg) ||
+           wps_validate_sel_reg_config_methods(attr->sel_reg_config_methods,
+                                               wps2, sel_reg) ||
+           wps_validate_version2(attr->version2, wps2) ||
+           wps_validate_authorized_macs(attr->authorized_macs,
+                                        attr->authorized_macs_len, wps2) ||
+           wps_validate_uuid_r(attr->uuid_r, wps2)) {
+               wpa_printf(MSG_INFO, "WPS-STRICT: Invalid "
+                          "SetSelectedRegistrar");
+#ifdef WPS_STRICT_WPS2
+               if (wps2) {
+                       ret = -1;
+                       goto _out;
+               }
+#else /* WPS_STRICT_WPS2 */
+               ret -1;
+               goto _out;
+#endif /* WPS_STRICT_WPS2 */
+       }
+
+       ret = 0;
+_out:
+       if (attr)
+               os_free(attr);
+
+       return ret;
+}