diff --git a/components/esp8266/CMakeLists.txt b/components/esp8266/CMakeLists.txt index 4eaf46bd..f66fd36b 100644 --- a/components/esp8266/CMakeLists.txt +++ b/components/esp8266/CMakeLists.txt @@ -67,7 +67,7 @@ else() target_link_libraries(${COMPONENT_LIB} PUBLIC "-L ${CMAKE_CURRENT_SOURCE_DIR}/lib" "-lstdc++") if(NOT CONFIG_NO_BLOBS) - set(blobs "gcc" "hal" "core" "net80211" "phy" "rtc" "clk" "pp" "smartconfig" "ssc" "wpa" "espnow" "wps") + set(blobs "gcc" "hal" "core" "net80211" "phy" "rtc" "clk" "pp" "smartconfig" "ssc" "wpa" "espnow" "wps" "wpa2") foreach(blob ${blobs}) add_library(${blob} STATIC IMPORTED) set_property(TARGET ${blob} PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/lib/lib${blob}.a) diff --git a/components/esp8266/Kconfig b/components/esp8266/Kconfig index 5ae4a6b6..a4520e59 100644 --- a/components/esp8266/Kconfig +++ b/components/esp8266/Kconfig @@ -568,6 +568,13 @@ config ESP8266_WIFI_DEBUG_LOG_SUBMODULE_FRAG help When this option is enabled, log for frag module will be enabled. +config ESP8266_WIFI_DEBUG_LOG_SUBMODULE_WPA2 + depends on ESP8266_WIFI_DEBUG_LOG_SUBMODULE + bool "wpa2_enterprise" + default n + help + When this option is enabled, log for wpa2_enterprise module will be enabled. + endmenu menu PHY diff --git a/components/esp8266/component.mk b/components/esp8266/component.mk index 4cb89d13..af8a1835 100644 --- a/components/esp8266/component.mk +++ b/components/esp8266/component.mk @@ -13,10 +13,10 @@ LIBS ?= ifndef CONFIG_NO_BLOBS ifndef CONFIG_ESP8266_WIFI_DEBUG_LOG_ENABLE LIBS += gcc hal core net80211 \ - phy rtc clk pp smartconfig ssc wpa espnow wps + phy rtc clk pp smartconfig ssc wpa espnow wps wpa2 else LIBS += gcc hal core_dbg net80211_dbg \ - phy rtc clk pp_dbg smartconfig ssc wpa_dbg espnow_dbg wps_dbg + phy rtc clk pp_dbg smartconfig ssc wpa_dbg espnow_dbg wps_dbg wpa2_dbg endif endif diff --git a/components/esp8266/include/esp_wifi_crypto_types.h b/components/esp8266/include/esp_wifi_crypto_types.h index 240bf356..2944f355 100644 --- a/components/esp8266/include/esp_wifi_crypto_types.h +++ b/components/esp8266/include/esp_wifi_crypto_types.h @@ -748,6 +748,39 @@ typedef struct{ esp_eap_msg_alloc_t eap_msg_alloc; }wps_crypto_funcs_t; +/** + * @brief The crypto callback function structure used when do WPA enterprise connect. + * The structure can be set as software crypto or the crypto optimized by ESP32 + * hardware. + */ +typedef struct { + uint32_t size; + uint32_t version; + esp_crypto_hash_init_t crypto_hash_init; /**< function used to initialize a crypto_hash structure when use TLSV1 */ + esp_crypto_hash_update_t crypto_hash_update; /**< function used to calculate hash data when use TLSV1 */ + esp_crypto_hash_finish_t crypto_hash_finish; /**< function used to finish the hash calculate when use TLSV1 */ + esp_crypto_cipher_init_t crypto_cipher_init; /**< function used to initialize a crypt_cipher structure when use TLSV1 */ + esp_crypto_cipher_encrypt_t crypto_cipher_encrypt; /**< function used to encrypt cipher when use TLSV1 */ + esp_crypto_cipher_decrypt_t crypto_cipher_decrypt; /**< function used to decrypt cipher when use TLSV1 */ + esp_crypto_cipher_deinit_t crypto_cipher_deinit; /**< function used to free context when use TLSV1 */ + esp_crypto_mod_exp_t crypto_mod_exp; /**< function used to do key exchange when use TLSV1 */ + esp_sha256_vector_t sha256_vector; /**< function used to do X.509v3 certificate parsing and processing */ + esp_tls_init_t tls_init; + esp_tls_deinit_t tls_deinit; + esp_eap_peer_blob_init_t eap_peer_blob_init; + esp_eap_peer_blob_deinit_t eap_peer_blob_deinit; + esp_eap_peer_config_init_t eap_peer_config_init; + esp_eap_peer_config_deinit_t eap_peer_config_deinit; + esp_eap_peer_register_methods_t eap_peer_register_methods; + esp_eap_peer_unregister_methods_t eap_peer_unregister_methods; + esp_eap_deinit_prev_method_t eap_deinit_prev_method; + esp_eap_peer_get_eap_method_t eap_peer_get_eap_method; + esp_eap_sm_abort_t eap_sm_abort; + esp_eap_sm_build_nak_t eap_sm_build_nak; + esp_eap_sm_build_identity_resp_t eap_sm_build_identity_resp; + esp_eap_msg_alloc_t eap_msg_alloc; +} wpa2_crypto_funcs_t; + #ifdef __cplusplus } #endif diff --git a/components/esp8266/include/esp_wpa2.h b/components/esp8266/include/esp_wpa2.h new file mode 100644 index 00000000..245879b8 --- /dev/null +++ b/components/esp8266/include/esp_wpa2.h @@ -0,0 +1,206 @@ +// Hardware crypto support Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ESP_WPA2_H +#define ESP_WPA2_H + +#include +#include "esp_wifi_crypto_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const wpa2_crypto_funcs_t g_wifi_default_wpa2_crypto_funcs; + +typedef struct { + const wpa2_crypto_funcs_t *crypto_funcs; +}esp_wpa2_config_t; + +#define WPA2_CONFIG_INIT_DEFAULT() { \ + .crypto_funcs = &g_wifi_default_wpa2_crypto_funcs \ +} + +/** + * @brief Enable wpa2 enterprise authentication. + * + * @attention 1. wpa2 enterprise authentication can only be used when ESP32 station is enabled. + * @attention 2. wpa2 enterprise authentication can only support TLS, PEAP-MSCHAPv2 and TTLS-MSCHAPv2 method. + * + * @return + * - ESP_OK: succeed. + * - ESP_ERR_NO_MEM: fail(internal memory malloc fail) + */ +esp_err_t esp_wifi_sta_wpa2_ent_enable(const esp_wpa2_config_t *config); + +/** + * @brief Disable wpa2 enterprise authentication. + * + * @attention 1. wpa2 enterprise authentication can only be used when ESP32 station is enabled. + * @attention 2. wpa2 enterprise authentication can only support TLS, PEAP-MSCHAPv2 and TTLS-MSCHAPv2 method. + * + * @return + * - ESP_OK: succeed. + */ +esp_err_t esp_wifi_sta_wpa2_ent_disable(void); + +/** + * @brief Set identity for PEAP/TTLS method. + * + * @attention The API only passes the parameter identity to the global pointer variable in wpa2 enterprise module. + * + * @param identity: point to address where stores the identity; + * @param len: length of identity, limited to 1~127 + * + * @return + * - ESP_OK: succeed + * - ESP_ERR_INVALID_ARG: fail(len <= 0 or len >= 128) + * - ESP_ERR_NO_MEM: fail(internal memory malloc fail) + */ +esp_err_t esp_wifi_sta_wpa2_ent_set_identity(const unsigned char *identity, int len); + +/** + * @brief Clear identity for PEAP/TTLS method. + */ +void esp_wifi_sta_wpa2_ent_clear_identity(void); + +/** + * @brief Set username for PEAP/TTLS method. + * + * @attention The API only passes the parameter username to the global pointer variable in wpa2 enterprise module. + * + * @param username: point to address where stores the username; + * @param len: length of username, limited to 1~127 + * + * @return + * - ESP_OK: succeed + * - ESP_ERR_INVALID_ARG: fail(len <= 0 or len >= 128) + * - ESP_ERR_NO_MEM: fail(internal memory malloc fail) + */ +esp_err_t esp_wifi_sta_wpa2_ent_set_username(const unsigned char *username, int len); + +/** + * @brief Clear username for PEAP/TTLS method. + */ +void esp_wifi_sta_wpa2_ent_clear_username(void); + +/** + * @brief Set password for PEAP/TTLS method.. + * + * @attention The API only passes the parameter password to the global pointer variable in wpa2 enterprise module. + * + * @param password: point to address where stores the password; + * @param len: length of password(len > 0) + * + * @return + * - ESP_OK: succeed + * - ESP_ERR_INVALID_ARG: fail(len <= 0) + * - ESP_ERR_NO_MEM: fail(internal memory malloc fail) + */ +esp_err_t esp_wifi_sta_wpa2_ent_set_password(const unsigned char *password, int len); + +/** + * @brief Clear password for PEAP/TTLS method.. + */ +void esp_wifi_sta_wpa2_ent_clear_password(void); + +/** + * @brief Set new password for MSCHAPv2 method.. + * + * @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 new_password: point to address where stores the password; + * @param len: length of password + * + * @return + * - ESP_OK: succeed + * - ESP_ERR_INVALID_ARG: fail(len <= 0) + * - ESP_ERR_NO_MEM: fail(internal memory malloc fail) + */ + +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.. + */ +void esp_wifi_sta_wpa2_ent_clear_new_password(void); + +/** + * @brief Set CA certificate for PEAP/TTLS method. + * + * @attention 1. The API only passes the parameter ca_cert to the global pointer variable in wpa2 enterprise module. + * @attention 2. The ca_cert should be zero terminated. + * + * @param ca_cert: point to address where stores the CA certificate; + * @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 ca_cert_len); + +/** + * @brief Clear CA certificate for PEAP/TTLS method. + */ +void esp_wifi_sta_wpa2_ent_clear_ca_cert(void); + +/** + * @brief Set client certificate and key. + * + * @attention 1. The API only passes the parameter client_cert, private_key and private_key_passwd to the global pointer variable in wpa2 enterprise module. + * @attention 2. The client_cert, private_key and private_key_passwd should be zero terminated. + * + * @param client_cert: point to address where stores the client certificate; + * @param client_cert_len: length of client certificate; + * @param private_key: point to address where stores the private key; + * @param private_key_len: length of private key, limited to 1~2048; + * @param private_key_password: point to address where stores the private key password; + * @param private_key_password_len: length of private key password; + * + * @return + * - ESP_OK: succeed + */ +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); + +/** + * @brief Clear client certificate and key. + */ +void esp_wifi_sta_wpa2_ent_clear_cert_key(void); + +/** + * @brief Set wpa2 enterprise certs time check(disable or not). + * + * @param true: disable wpa2 enterprise certs time check + * @param false: enable wpa2 enterprise certs time check + * + * @return + * - ESP_OK: succeed + */ +esp_err_t esp_wifi_sta_wpa2_ent_set_disable_time_check(bool disable); + +/** + * @brief Get wpa2 enterprise certs time check(disable or not). + * + * @param disable: store disable value + * + * @return + * - ESP_OK: succeed + */ +esp_err_t esp_wifi_sta_wpa2_ent_get_disable_time_check(bool *disable); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/components/esp8266/include/internal/esp_wifi_internal.h b/components/esp8266/include/internal/esp_wifi_internal.h index 3087f9d5..40c25725 100644 --- a/components/esp8266/include/internal/esp_wifi_internal.h +++ b/components/esp8266/include/internal/esp_wifi_internal.h @@ -56,6 +56,8 @@ typedef enum { #define WIFI_LOG_SUBMODULE_AMPDU (1<<12) #define WIFI_LOG_SUBMODULE_AMSDU (1<<13) #define WIFI_LOG_SUBMODULE_FRAG (1<<14) +#define WIFI_LOG_SUBMODULE_WPA2 (1<<15) + /** * @brief Set WIFI received TCP/IP data cache ram type * diff --git a/components/esp8266/lib/VERSION b/components/esp8266/lib/VERSION index 14cd74f7..5bd603c5 100644 --- a/components/esp8266/lib/VERSION +++ b/components/esp8266/lib/VERSION @@ -1,10 +1,11 @@ gwen: - core: 158ee68 - net80211: 72ecd98 - pp: dedbcf0 - wpa: 743c778 - espnow: 743c778 - wps: 6a32b9b + core: a021d6d + net80211: a021d6d + pp: a021d6d + wpa: a021d6d + espnow: a021d6d + wps: a021d6d + wpa2: a021d6d smartconfig: 2.8.2 phy: 1155.0 diff --git a/components/esp8266/lib/libcore.a b/components/esp8266/lib/libcore.a index 96af2578..a30ddb83 100644 Binary files a/components/esp8266/lib/libcore.a and b/components/esp8266/lib/libcore.a differ diff --git a/components/esp8266/lib/libcore_dbg.a b/components/esp8266/lib/libcore_dbg.a index 3be2a732..4229839c 100644 Binary files a/components/esp8266/lib/libcore_dbg.a and b/components/esp8266/lib/libcore_dbg.a differ diff --git a/components/esp8266/lib/libespnow.a b/components/esp8266/lib/libespnow.a index d12337d3..8d536471 100644 Binary files a/components/esp8266/lib/libespnow.a and b/components/esp8266/lib/libespnow.a differ diff --git a/components/esp8266/lib/libespnow_dbg.a b/components/esp8266/lib/libespnow_dbg.a index 40109a8d..13b08490 100644 Binary files a/components/esp8266/lib/libespnow_dbg.a and b/components/esp8266/lib/libespnow_dbg.a differ diff --git a/components/esp8266/lib/libnet80211.a b/components/esp8266/lib/libnet80211.a index 7ae5d5e5..89a6770a 100644 Binary files a/components/esp8266/lib/libnet80211.a and b/components/esp8266/lib/libnet80211.a differ diff --git a/components/esp8266/lib/libnet80211_dbg.a b/components/esp8266/lib/libnet80211_dbg.a index 1c7fe553..d1a666d0 100644 Binary files a/components/esp8266/lib/libnet80211_dbg.a and b/components/esp8266/lib/libnet80211_dbg.a differ diff --git a/components/esp8266/lib/libpp.a b/components/esp8266/lib/libpp.a index d7c065e5..106d44e3 100644 Binary files a/components/esp8266/lib/libpp.a and b/components/esp8266/lib/libpp.a differ diff --git a/components/esp8266/lib/libpp_dbg.a b/components/esp8266/lib/libpp_dbg.a index ddfe2a21..79ab9e92 100644 Binary files a/components/esp8266/lib/libpp_dbg.a and b/components/esp8266/lib/libpp_dbg.a differ diff --git a/components/esp8266/lib/libwpa.a b/components/esp8266/lib/libwpa.a index 89204880..059e9c56 100644 Binary files a/components/esp8266/lib/libwpa.a and b/components/esp8266/lib/libwpa.a differ diff --git a/components/esp8266/lib/libwpa2.a b/components/esp8266/lib/libwpa2.a new file mode 100644 index 00000000..3e9fdf3b Binary files /dev/null and b/components/esp8266/lib/libwpa2.a differ diff --git a/components/esp8266/lib/libwpa2_dbg.a b/components/esp8266/lib/libwpa2_dbg.a new file mode 100644 index 00000000..5e1cb296 Binary files /dev/null and b/components/esp8266/lib/libwpa2_dbg.a differ diff --git a/components/esp8266/lib/libwpa_dbg.a b/components/esp8266/lib/libwpa_dbg.a index 6faee56a..85f6996c 100644 Binary files a/components/esp8266/lib/libwpa_dbg.a and b/components/esp8266/lib/libwpa_dbg.a differ diff --git a/components/esp8266/lib/libwps.a b/components/esp8266/lib/libwps.a old mode 100755 new mode 100644 index fa62af0c..9b2152d1 Binary files a/components/esp8266/lib/libwps.a and b/components/esp8266/lib/libwps.a differ diff --git a/components/esp8266/lib/libwps_dbg.a b/components/esp8266/lib/libwps_dbg.a old mode 100755 new mode 100644 index e66fc94f..f028ca93 Binary files a/components/esp8266/lib/libwps_dbg.a and b/components/esp8266/lib/libwps_dbg.a differ diff --git a/components/esp8266/source/esp_wifi.c b/components/esp8266/source/esp_wifi.c index 03787876..1cd45f4c 100644 --- a/components/esp8266/source/esp_wifi.c +++ b/components/esp8266/source/esp_wifi.c @@ -86,6 +86,9 @@ static void esp_wifi_set_debug_log() #endif #if CONFIG_ESP8266_WIFI_DEBUG_LOG_SUBMODULE_FRAG wifi_log_submodule |= WIFI_LOG_SUBMODULE_FRAG; +#endif +#if CONFIG_ESP8266_WIFI_DEBUG_LOG_SUBMODULE_WPA2 + wifi_log_submodule |= WIFI_LOG_SUBMODULE_WPA2; #endif esp_wifi_internal_set_log_level(wifi_log_level); esp_wifi_internal_set_log_mod(wifi_log_submodule); diff --git a/components/esp8266/source/esp_wifi_os_adapter.c b/components/esp8266/source/esp_wifi_os_adapter.c index 26827555..61f0c6df 100644 --- a/components/esp8266/source/esp_wifi_os_adapter.c +++ b/components/esp8266/source/esp_wifi_os_adapter.c @@ -145,3 +145,27 @@ void *__wifi_task_top_sp(void) return pxCurrentTCB[0]; } + +void* __wifi_semphr_create(uint32_t max, uint32_t init) +{ + return (void*)xSemaphoreCreateCounting(max, init); +} + +void __wifi_semphr_delete(void* semphr) +{ + vSemaphoreDelete(semphr); +} + +int32_t __wifi_semphr_take(void* semphr, uint32_t block_time_tick) +{ + if (block_time_tick == OSI_FUNCS_TIME_BLOCKING) { + return (int32_t)xSemaphoreTake(semphr, portMAX_DELAY); + } else { + return (int32_t)xSemaphoreTake(semphr, block_time_tick); + } +} + +int32_t __wifi_semphr_give(void* semphr) +{ + return (int32_t)xSemaphoreGive(semphr); +} \ No newline at end of file diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index 3329f6a8..0620d299 100644 --- a/components/wpa_supplicant/CMakeLists.txt +++ b/components/wpa_supplicant/CMakeLists.txt @@ -1,5 +1,5 @@ -set(COMPONENT_SRCDIRS "src/crypto" "src/fast_crypto" "src/wps" "port" "src") +set(COMPONENT_SRCDIRS "src/crypto" "src/fast_crypto" "src/wps" "src/wpa2/eap_peer" "src/wpa2/tls" "src/wpa2/utils" "port" "src") set(COMPONENT_ADD_INCLUDEDIRS "include" "port/include") set(COMPONENT_PRIV_REQUIRES "mbedtls" "freertos" "heap" "newlib" "util") @@ -11,6 +11,13 @@ target_compile_definitions(${COMPONENT_LIB} PRIVATE __ets__ EMBEDDED_SUPP ESPRESSIF_USE + IEEE8021X_EAPOL + EAP_PEER_METHOD + EAP_MSCHAPv2 + EAP_TTLS + EAP_TLS + EAP_PEAP + USE_WPA2_TASK CONFIG_WPS2 CONFIG_WPS_PIN USE_WPS_TASK diff --git a/components/wpa_supplicant/component.mk b/components/wpa_supplicant/component.mk index 452a0ac1..f4003f95 100644 --- a/components/wpa_supplicant/component.mk +++ b/components/wpa_supplicant/component.mk @@ -1,4 +1,4 @@ -COMPONENT_ADD_INCLUDEDIRS := include include/wps port/include -COMPONENT_SRCDIRS := src/crypto src/wps src/fast_crypto src port +COMPONENT_ADD_INCLUDEDIRS := include include/wps include/wpa2 port/include +COMPONENT_SRCDIRS := src/crypto src/wps src/fast_crypto src/wpa2/eap_peer src/wpa2/tls src/wpa2/utils src port -CFLAGS += -DEMBEDDED_SUPP -D__ets__ -DESPRESSIF_USE -DCONFIG_WPS2 -DCONFIG_WPS_PIN -DUSE_WPS_TASK -DESP8266_WORKAROUND -Wno-strict-aliasing \ No newline at end of file +CFLAGS += -DEMBEDDED_SUPP -D__ets__ -DESPRESSIF_USE -DIEEE8021X_EAPOL -DEAP_PEER_METHOD -DEAP_TLS -DEAP_TTLS -DEAP_PEAP -DEAP_MSCHAPv2 -DUSE_WPA2_TASK -DCONFIG_WPS2 -DCONFIG_WPS_PIN -DUSE_WPS_TASK -DESP8266_WORKAROUND -Wno-unused-but-set-variable -Wno-empty-body \ No newline at end of file diff --git a/components/wpa_supplicant/include/crypto/crypto.h b/components/wpa_supplicant/include/crypto/crypto.h index 0a015afc..f6b7b2f2 100644 --- a/components/wpa_supplicant/include/crypto/crypto.h +++ b/components/wpa_supplicant/include/crypto/crypto.h @@ -112,7 +112,7 @@ int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, */ int fast_sha256_vector(size_t num_elem, const uint8_t *addr[], const size_t *len, uint8_t *mac); - + /** * des_encrypt - Encrypt one block with DES * @clear: 8 octets (in) @@ -189,6 +189,21 @@ struct crypto_hash; struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, size_t key_len); +/** + * fast_crypto_hash_init - Initialize hash/HMAC function + * @alg: Hash algorithm + * @key: Key for keyed hash (e.g., HMAC) or %NULL if not needed + * @key_len: Length of the key in bytes + * Returns: Pointer to hash context to use with other hash functions or %NULL + * on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_hash * fast_crypto_hash_init(enum crypto_hash_alg alg, const uint8_t *key, + size_t key_len); + /** * crypto_hash_update - Add data to hash calculation * @ctx: Context pointer from crypto_hash_init() @@ -201,6 +216,18 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, */ void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len); +/** + * fast_crypto_hash_update - Add data to hash calculation + * @ctx: Context pointer from crypto_hash_init() + * @data: Data buffer to add + * @len: Length of the buffer + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void fast_crypto_hash_update(struct crypto_hash *ctx, const uint8_t *data, size_t len); + /** * crypto_hash_finish - Complete hash calculation * @ctx: Context pointer from crypto_hash_init() @@ -220,6 +247,25 @@ void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len); */ int crypto_hash_finish(struct crypto_hash *ctx, u8 *hash, size_t *len); +/** + * fast_crypto_hash_finish - Complete hash calculation + * @ctx: Context pointer from crypto_hash_init() + * @hash: Buffer for hash value or %NULL if caller is just freeing the hash + * context + * @len: Pointer to length of the buffer or %NULL if caller is just freeing the + * hash context; on return, this is set to the actual length of the hash value + * Returns: 0 on success, -1 if buffer is too small (len set to needed length), + * or -2 on other failures (including failed crypto_hash_update() operations) + * + * This function calculates the hash value and frees the context buffer that + * was used for hash calculation. + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int fast_crypto_hash_finish(struct crypto_hash *ctx, uint8_t *hash, size_t *len); + enum crypto_cipher_alg { CRYPTO_CIPHER_NULL = 0, CRYPTO_CIPHER_ALG_AES, CRYPTO_CIPHER_ALG_3DES, @@ -245,6 +291,22 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, const u8 *iv, const u8 *key, size_t key_len); +/** + * fast_crypto_cipher_init - Initialize block/stream cipher function + * @alg: Cipher algorithm + * @iv: Initialization vector for block ciphers or %NULL for stream ciphers + * @key: Cipher key + * @key_len: Length of key in bytes + * Returns: Pointer to cipher context to use with other cipher functions or + * %NULL on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_cipher * fast_crypto_cipher_init(enum crypto_cipher_alg alg, + const uint8_t *iv, const uint8_t *key, + size_t key_len); /** * crypto_cipher_encrypt - Cipher encrypt * @ctx: Context pointer from crypto_cipher_init() @@ -260,6 +322,21 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, int __must_check crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, u8 *crypt, size_t len); +/** + * fast_crypto_cipher_encrypt - Cipher encrypt + * @ctx: Context pointer from crypto_cipher_init() + * @plain: Plaintext to cipher + * @crypt: Resulting ciphertext + * @len: Length of the plaintext + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check fast_crypto_cipher_encrypt(struct crypto_cipher *ctx, + const uint8_t *plain, uint8_t *crypt, size_t len); + /** * crypto_cipher_decrypt - Cipher decrypt * @ctx: Context pointer from crypto_cipher_init() @@ -275,6 +352,21 @@ int __must_check crypto_cipher_encrypt(struct crypto_cipher *ctx, int __must_check crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, u8 *plain, size_t len); +/** + * fast_crypto_cipher_decrypt - Cipher decrypt + * @ctx: Context pointer from crypto_cipher_init() + * @crypt: Ciphertext to decrypt + * @plain: Resulting plaintext + * @len: Length of the cipher text + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check fast_crypto_cipher_decrypt(struct crypto_cipher *ctx, + const uint8_t *crypt, uint8_t *plain, size_t len); + /** * crypto_cipher_decrypt - Free cipher context * @ctx: Context pointer from crypto_cipher_init() @@ -285,6 +377,15 @@ int __must_check crypto_cipher_decrypt(struct crypto_cipher *ctx, */ void crypto_cipher_deinit(struct crypto_cipher *ctx); +/** + * fast_crypto_cipher_decrypt - Free cipher context + * @ctx: Context pointer from crypto_cipher_init() + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void fast_crypto_cipher_deinit(struct crypto_cipher *ctx); struct crypto_public_key; struct crypto_private_key; @@ -505,4 +606,363 @@ int __must_check fast_crypto_mod_exp(const uint8_t *base, size_t base_len, int rc4_skip(const u8 *key, size_t keylen, size_t skip, u8 *data, size_t data_len); + +/** + * crypto_get_random - Generate cryptographically strong pseudy-random bytes + * @buf: Buffer for data + * @len: Number of bytes to generate + * Returns: 0 on success, -1 on failure + * + * If the PRNG does not have enough entropy to ensure unpredictable byte + * sequence, this functions must return -1. + */ +int crypto_get_random(void *buf, size_t len); + + +/** + * struct crypto_bignum - bignum + * + * Internal data structure for bignum implementation. The contents is specific + * to the used crypto library. + */ +struct crypto_bignum; + +/** + * crypto_bignum_init - Allocate memory for bignum + * Returns: Pointer to allocated bignum or %NULL on failure + */ +struct crypto_bignum * crypto_bignum_init(void); + +/** + * crypto_bignum_init_set - Allocate memory for bignum and set the value + * @buf: Buffer with unsigned binary value + * @len: Length of buf in octets + * Returns: Pointer to allocated bignum or %NULL on failure + */ +struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len); + +/** + * crypto_bignum_deinit - Free bignum + * @n: Bignum from crypto_bignum_init() or crypto_bignum_init_set() + * @clear: Whether to clear the value from memory + */ +void crypto_bignum_deinit(struct crypto_bignum *n, int clear); + +/** + * crypto_bignum_to_bin - Set binary buffer to unsigned bignum + * @a: Bignum + * @buf: Buffer for the binary number + * @len: Length of @buf in octets + * @padlen: Length in octets to pad the result to or 0 to indicate no padding + * Returns: Number of octets written on success, -1 on failure + */ +int crypto_bignum_to_bin(const struct crypto_bignum *a, + u8 *buf, size_t buflen, size_t padlen); + +/** + * crypto_bignum_add - c = a + b + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result of a + b + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_add(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c); + +/** + * crypto_bignum_mod - c = a % b + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result of a % b + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_mod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c); + +/** + * crypto_bignum_exptmod - Modular exponentiation: d = a^b (mod c) + * @a: Bignum; base + * @b: Bignum; exponent + * @c: Bignum; modulus + * @d: Bignum; used to store the result of a^b (mod c) + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_exptmod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + const struct crypto_bignum *c, + struct crypto_bignum *d); + +/** + * crypto_bignum_inverse - Inverse a bignum so that a * c = 1 (mod b) + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_inverse(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c); + +/** + * crypto_bignum_sub - c = a - b + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result of a - b + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_sub(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c); + +/** + * crypto_bignum_div - c = a / b + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result of a / b + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_div(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c); + +/** + * crypto_bignum_mulmod - d = a * b (mod c) + * @a: Bignum + * @b: Bignum + * @c: Bignum + * @d: Bignum; used to store the result of (a * b) % c + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_mulmod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + const struct crypto_bignum *c, + struct crypto_bignum *d); + +/** + * crypto_bignum_cmp - Compare two bignums + * @a: Bignum + * @b: Bignum + * Returns: -1 if a < b, 0 if a == b, or 1 if a > b + */ +int crypto_bignum_cmp(const struct crypto_bignum *a, + const struct crypto_bignum *b); + +/** + * crypto_bignum_bits - Get size of a bignum in bits + * @a: Bignum + * Returns: Number of bits in the bignum + */ +int crypto_bignum_bits(const struct crypto_bignum *a); + +/** + * crypto_bignum_is_zero - Is the given bignum zero + * @a: Bignum + * Returns: 1 if @a is zero or 0 if not + */ +int crypto_bignum_is_zero(const struct crypto_bignum *a); + +/** + * crypto_bignum_is_one - Is the given bignum one + * @a: Bignum + * Returns: 1 if @a is one or 0 if not + */ +int crypto_bignum_is_one(const struct crypto_bignum *a); + +/** + * crypto_bignum_legendre - Compute the Legendre symbol (a/p) + * @a: Bignum + * @p: Bignum + * Returns: Legendre symbol -1,0,1 on success; -2 on calculation failure + */ +int crypto_bignum_legendre(const struct crypto_bignum *a, + const struct crypto_bignum *p); + + +/** + * struct crypto_ec - Elliptic curve context + * + * Internal data structure for EC implementation. The contents is specific + * to the used crypto library. + */ +struct crypto_ec; + +/** + * crypto_ec_init - Initialize elliptic curve context + * @group: Identifying number for the ECC group (IANA "Group Description" + * attribute registrty for RFC 2409) + * Returns: Pointer to EC context or %NULL on failure + */ +struct crypto_ec * crypto_ec_init(int group); + +/** + * crypto_ec_deinit - Deinitialize elliptic curve context + * @e: EC context from crypto_ec_init() + */ +void crypto_ec_deinit(struct crypto_ec *e); + +/** + * crypto_ec_prime_len - Get length of the prime in octets + * @e: EC context from crypto_ec_init() + * Returns: Length of the prime defining the group + */ +size_t crypto_ec_prime_len(struct crypto_ec *e); + +/** + * crypto_ec_prime_len_bits - Get length of the prime in bits + * @e: EC context from crypto_ec_init() + * Returns: Length of the prime defining the group in bits + */ +size_t crypto_ec_prime_len_bits(struct crypto_ec *e); + +/** + * crypto_ec_get_prime - Get prime defining an EC group + * @e: EC context from crypto_ec_init() + * Returns: Prime (bignum) defining the group + */ +const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e); + +/** + * crypto_ec_get_order - Get order of an EC group + * @e: EC context from crypto_ec_init() + * Returns: Order (bignum) of the group + */ +const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e); + +/** + * struct crypto_ec_point - Elliptic curve point + * + * Internal data structure for EC implementation to represent a point. The + * contents is specific to the used crypto library. + */ +struct crypto_ec_point; + +/** + * crypto_ec_point_init - Initialize data for an EC point + * @e: EC context from crypto_ec_init() + * Returns: Pointer to EC point data or %NULL on failure + */ +struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e); + +/** + * crypto_ec_point_deinit - Deinitialize EC point data + * @p: EC point data from crypto_ec_point_init() + * @clear: Whether to clear the EC point value from memory + */ +void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear); + +/** + * crypto_ec_point_to_bin - Write EC point value as binary data + * @e: EC context from crypto_ec_init() + * @p: EC point data from crypto_ec_point_init() + * @x: Buffer for writing the binary data for x coordinate or %NULL if not used + * @y: Buffer for writing the binary data for y coordinate or %NULL if not used + * Returns: 0 on success, -1 on failure + * + * This function can be used to write an EC point as binary data in a format + * that has the x and y coordinates in big endian byte order fields padded to + * the length of the prime defining the group. + */ +int crypto_ec_point_to_bin(struct crypto_ec *e, + const struct crypto_ec_point *point, u8 *x, u8 *y); + +/** + * crypto_ec_point_from_bin - Create EC point from binary data + * @e: EC context from crypto_ec_init() + * @val: Binary data to read the EC point from + * Returns: Pointer to EC point data or %NULL on failure + * + * This function readers x and y coordinates of the EC point from the provided + * buffer assuming the values are in big endian byte order with fields padded to + * the length of the prime defining the group. + */ +struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e, + const u8 *val); + +/** + * crypto_bignum_add - c = a + b + * @e: EC context from crypto_ec_init() + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result of a + b + * Returns: 0 on success, -1 on failure + */ +int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a, + const struct crypto_ec_point *b, + struct crypto_ec_point *c); + +/** + * crypto_bignum_mul - res = b * p + * @e: EC context from crypto_ec_init() + * @p: EC point + * @b: Bignum + * @res: EC point; used to store the result of b * p + * Returns: 0 on success, -1 on failure + */ +int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p, + const struct crypto_bignum *b, + struct crypto_ec_point *res); + +/** + * crypto_ec_point_invert - Compute inverse of an EC point + * @e: EC context from crypto_ec_init() + * @p: EC point to invert (and result of the operation) + * Returns: 0 on success, -1 on failure + */ +int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p); + +/** + * crypto_ec_point_solve_y_coord - Solve y coordinate for an x coordinate + * @e: EC context from crypto_ec_init() + * @p: EC point to use for the returning the result + * @x: x coordinate + * @y_bit: y-bit (0 or 1) for selecting the y value to use + * Returns: 0 on success, -1 on failure + */ +int crypto_ec_point_solve_y_coord(struct crypto_ec *e, + struct crypto_ec_point *p, + const struct crypto_bignum *x, int y_bit); + +/** + * crypto_ec_point_compute_y_sqr - Compute y^2 = x^3 + ax + b + * @e: EC context from crypto_ec_init() + * @x: x coordinate + * Returns: y^2 on success, %NULL failure + */ +struct crypto_bignum * +crypto_ec_point_compute_y_sqr(struct crypto_ec *e, + const struct crypto_bignum *x); + +/** + * crypto_ec_point_is_at_infinity - Check whether EC point is neutral element + * @e: EC context from crypto_ec_init() + * @p: EC point + * Returns: 1 if the specified EC point is the neutral element of the group or + * 0 if not + */ +int crypto_ec_point_is_at_infinity(struct crypto_ec *e, + const struct crypto_ec_point *p); + +/** + * crypto_ec_point_is_on_curve - Check whether EC point is on curve + * @e: EC context from crypto_ec_init() + * @p: EC point + * Returns: 1 if the specified EC point is on the curve or 0 if not + */ +int crypto_ec_point_is_on_curve(struct crypto_ec *e, + const struct crypto_ec_point *p); + +/** + * crypto_ec_point_cmp - Compare two EC points + * @e: EC context from crypto_ec_init() + * @a: EC point + * @b: EC point + * Returns: 0 on equal, non-zero otherwise + */ +int crypto_ec_point_cmp(const struct crypto_ec *e, + const struct crypto_ec_point *a, + const struct crypto_ec_point *b); + + #endif /* CRYPTO_H */ diff --git a/components/wpa_supplicant/include/crypto/ms_funcs.h b/components/wpa_supplicant/include/crypto/ms_funcs.h new file mode 100644 index 00000000..dadb7d93 --- /dev/null +++ b/components/wpa_supplicant/include/crypto/ms_funcs.h @@ -0,0 +1,60 @@ +/* + * WPA Supplicant / shared MSCHAPV2 helper functions + * + * + */ + +#ifndef MS_FUNCS_H +#define MS_FUNCS_H + +int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, + const u8 *username, size_t username_len, + const u8 *password, size_t password_len, + u8 *response); + +int generate_nt_response_pwhash(const u8 *auth_challenge, + const u8 *peer_challenge, + const u8 *username, size_t username_len, + const u8 *password_hash, + u8 *response); +int generate_authenticator_response(const u8 *password, size_t password_len, + const u8 *peer_challenge, + const u8 *auth_challenge, + const u8 *username, size_t username_len, + const u8 *nt_response, u8 *response); +int generate_authenticator_response_pwhash( + const u8 *password_hash, + const u8 *peer_challenge, const u8 *auth_challenge, + const u8 *username, size_t username_len, + const u8 *nt_response, u8 *response); +int nt_challenge_response(const u8 *challenge, const u8 *password, + size_t password_len, u8 *response); + +void challenge_response(const u8 *challenge, const u8 *password_hash, + u8 *response); +int nt_password_hash(const u8 *password, size_t password_len, + u8 *password_hash); +int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash); +int get_master_key(const u8 *password_hash_hash, const u8 *nt_response, + u8 *master_key); +int get_asymetric_start_key(const u8 *master_key, u8 *session_key, + size_t session_key_len, int is_send, + int is_server); +int encrypt_pw_block_with_password_hash( + const u8 *password, size_t password_len, + const u8 *password_hash, u8 *pw_block); +int __must_check encry_pw_block_with_password_hash( + const u8 *password, size_t password_len, + const u8 *password_hash, u8 *pw_block); +int __must_check new_password_encrypted_with_old_nt_password_hash( + const u8 *new_password, size_t new_password_len, + const u8 *old_password, size_t old_password_len, + u8 *encrypted_pw_block); +void nt_password_hash_encrypted_with_block(const u8 *password_hash, + const u8 *block, u8 *cypher); +int old_nt_password_hash_encrypted_with_new_nt_password_hash( + const u8 *new_password, size_t new_password_len, + const u8 *old_password, size_t old_password_len, + u8 *encrypted_password_hash); + +#endif /* MS_FUNCS_H */ diff --git a/components/wpa_supplicant/include/wpa/common.h b/components/wpa_supplicant/include/wpa/common.h index 97d72fed..c3e8546a 100755 --- a/components/wpa_supplicant/include/wpa/common.h +++ b/components/wpa_supplicant/include/wpa/common.h @@ -309,6 +309,7 @@ TCHAR * wpa_strdup_tchar(const char *str); const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len); char * wpa_config_parse_string(const char *value, size_t *len); +char * dup_binstr(const void *src, size_t len); static inline int is_zero_ether_addr(const u8 *a) { diff --git a/components/wpa_supplicant/include/wpa/wpa.h b/components/wpa_supplicant/include/wpa/wpa.h index 8a39d376..f73152db 100755 --- a/components/wpa_supplicant/include/wpa/wpa.h +++ b/components/wpa_supplicant/include/wpa/wpa.h @@ -104,15 +104,15 @@ struct wpa_sm { uint8_t flags; void (* sendto) (u8 *wpadata, u16 wpadatalen); - 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); - int (*get_ppkey) (uint8 *ifx, int *alg, uint8 *addr, int *key_idx, - uint8 *key, size_t key_len, int key_entry_valid); - void (*wpa_deauthenticate)(uint8 reason_code); + void (*config_assoc_ie) (uint8_t proto, u8 *assoc_buf, u32 assoc_wpa_ie_len); + void (*install_ppkey) (enum wpa_alg alg, uint8_t *addr, int key_idx, int set_tx, + uint8_t *seq, size_t seq_len, uint8_t *key, size_t key_len, int key_entry_valid); + int (*get_ppkey) (uint8_t *ifx, int *alg, uint8_t *addr, int *key_idx, + uint8_t *key, size_t key_len, int key_entry_valid); + void (*wpa_deauthenticate)(uint8_t reason_code); void (*wpa_neg_complete)(); struct wpa_gtk_data gd; //used for calllback save param - uint16 key_info; //used for txcallback param + uint16_t key_info; //used for txcallback param }; struct l2_ethhdr { @@ -188,12 +188,12 @@ struct l2_ethhdr { #define KEYENTRY_TABLE_MAP(key_entry_valid) ((key_entry_valid)%5) -void pp_michael_mic_failure(uint16 isunicast); +void pp_michael_mic_failure(uint16_t isunicast); void wpa_sm_set_state(enum wpa_states state); -int ppGetKey(uint8 *ifx, int *alg, uint8 *addr, int *key_idx, - uint8 *key, size_t key_len, int key_entry_valid); +int ppGetKey(uint8_t *ifx, int *alg, uint8_t *addr, int *key_idx, + uint8_t *key, size_t key_len, int key_entry_valid); #endif /* WPA_H */ diff --git a/components/wpa_supplicant/include/wpa2/eap_peer/eap.h b/components/wpa_supplicant/include/wpa2/eap_peer/eap.h new file mode 100644 index 00000000..9e1c3efa --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/eap_peer/eap.h @@ -0,0 +1,54 @@ +/* + * EAP peer state machine functions (RFC 4137) + * Copyright (c) 2004-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_H +#define EAP_H + +#include "wpa/defs.h" +#include "wpa2/eap_peer/eap_defs.h" + +struct eap_sm; + +struct eap_method_type { + int vendor; + 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 */ diff --git a/components/wpa_supplicant/include/wpa2/eap_peer/eap_common.h b/components/wpa_supplicant/include/wpa2/eap_peer/eap_common.h new file mode 100644 index 00000000..38c57100 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/eap_peer/eap_common.h @@ -0,0 +1,23 @@ +/* + * EAP common peer/server definitions + * Copyright (c) 2004-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_COMMON_H +#define EAP_COMMON_H + +#include "wpa/wpabuf.h" + +int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload); +const u8 * eap_hdr_validate(int vendor, EapType eap_type, + const struct wpabuf *msg, size_t *plen); +struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len, + u8 code, u8 identifier); +void eap_update_len(struct wpabuf *msg); +u8 eap_get_id(const struct wpabuf *msg); +EapType eap_get_type(const struct wpabuf *msg); + +#endif /* EAP_COMMON_H */ diff --git a/components/wpa_supplicant/include/wpa2/eap_peer/eap_config.h b/components/wpa_supplicant/include/wpa2/eap_peer/eap_config.h new file mode 100644 index 00000000..f95dcda3 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/eap_peer/eap_config.h @@ -0,0 +1,249 @@ +/* + * EAP peer configuration data + * Copyright (c) 2003-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_CONFIG_H +#define EAP_CONFIG_H + +/** + * struct eap_peer_config - EAP peer configuration/credentials + */ +struct eap_peer_config { + /** + * identity - EAP Identity + * + * This field is used to set the real user identity or NAI (for + * EAP-PSK/PAX/SAKE/GPSK). + */ + u8 *identity; + + /** + * identity_len - EAP Identity length + */ + size_t identity_len; + + u8 *anonymous_identity; + + size_t anonymous_identity_len; + + /** + * password - Password string for EAP + * + * This field can include either the plaintext password (default + * option) or a NtPasswordHash (16-byte MD4 hash of the unicode + * presentation of the password) if flags field has + * EAP_CONFIG_FLAGS_PASSWORD_NTHASH bit set to 1. NtPasswordHash can + * only be used with authentication mechanism that use this hash as the + * starting point for operation: MSCHAP and MSCHAPv2 (EAP-MSCHAPv2, + * EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP). + * + * In addition, this field is used to configure a pre-shared key for + * EAP-PSK/PAX/SAKE/GPSK. The length of the PSK must be 16 for EAP-PSK + * and EAP-PAX and 32 for EAP-SAKE. EAP-GPSK can use a variable length + * PSK. + */ + u8 *password; + + /** + * password_len - Length of password field + */ + size_t password_len; + + /** + * ca_cert - File path to CA certificate file (PEM/DER) + * + * This file can have one or more trusted CA certificates. If ca_cert + * and ca_path are not included, server certificate will not be + * verified. This is insecure and a trusted CA certificate should + * always be configured when using EAP-TLS/TTLS/PEAP. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + * + * Alternatively, this can be used to only perform matching of the + * server certificate (SHA-256 hash of the DER encoded X.509 + * certificate). In this case, the possible CA certificates in the + * server certificate chain are ignored and only the server certificate + * is verified. This is configured with the following format: + * hash:://server/sha256/cert_hash_in_hex + * For example: "hash://server/sha256/ + * 5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a" + * + * On Windows, trusted CA certificates can be loaded from the system + * certificate store by setting this to cert_store://name, e.g., + * ca_cert="cert_store://CA" or ca_cert="cert_store://ROOT". + * Note that when running wpa_supplicant as an application, the user + * certificate store (My user account) is used, whereas computer store + * (Computer account) is used when running wpasvc as a service. + */ + u8 *ca_cert; + + /** + * ca_path - Directory path for CA certificate files (PEM) + * + * This path may contain multiple CA certificates in OpenSSL format. + * Common use for this is to point to system trusted CA list which is + * often installed into directory like /etc/ssl/certs. If configured, + * these certificates are added to the list of trusted CAs. ca_cert + * may also be included in that case, but it is not required. + */ + u8 *ca_path; + + /** + * client_cert - File path to client certificate file (PEM/DER) + * + * This field is used with EAP method that use TLS authentication. + * Usually, this is only configured for EAP-TLS, even though this could + * in theory be used with EAP-TTLS and EAP-PEAP, too. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + u8 *client_cert; + + /** + * private_key - File path to client private key file (PEM/DER/PFX) + * + * When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be + * commented out. Both the private key and certificate will be read + * from the PKCS#12 file in this case. Full path to the file should be + * used since working directory may change when wpa_supplicant is run + * in the background. + * + * Windows certificate store can be used by leaving client_cert out and + * configuring private_key in one of the following formats: + * + * cert://substring_to_match + * + * hash://certificate_thumbprint_in_hex + * + * For example: private_key="hash://63093aa9c47f56ae88334c7b65a4" + * + * Note that when running wpa_supplicant as an application, the user + * certificate store (My user account) is used, whereas computer store + * (Computer account) is used when running wpasvc as a service. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + u8 *private_key; + + /** + * private_key_passwd - Password for private key file + * + * If left out, this will be asked through control interface. + */ + 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 + * + * This field is used to configure PIN for SIM and smartcards for + * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a + * smartcard is used for private key operations. + * + * If left out, this will be asked through control interface. + */ + char *pin; + + int mschapv2_retry; + u8 *new_password; + size_t new_password_len; + + /** + * fragment_size - Maximum EAP fragment size in bytes (default 1398) + * + * This value limits the fragment size for EAP methods that support + * fragmentation (e.g., EAP-TLS and EAP-PEAP). This value should be set + * small enough to make the EAP messages fit in MTU of the network + * interface used for EAPOL. The default value is suitable for most + * cases. + */ + int fragment_size; + +#define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0) +#define EAP_CONFIG_FLAGS_EXT_PASSWORD BIT(1) + /** + * flags - Network configuration flags (bitfield) + * + * This variable is used for internal flags to describe further details + * for the network parameters. + * bit 0 = password is represented as a 16-byte NtPasswordHash value + * instead of plaintext password + * bit 1 = password is stored in external storage; the value in the + * password field is the name of that external entry + */ + u32 flags; + + /** + * ocsp - Whether to use/require OCSP to check server certificate + * + * 0 = do not use OCSP stapling (TLS certificate status extension) + * 1 = try to use OCSP stapling, but not require response + * 2 = require valid OCSP stapling response + */ + int ocsp; +}; + + +/** + * struct wpa_config_blob - Named configuration blob + * + * This data structure is used to provide storage for binary objects to store + * abstract information like certificates and private keys inlined with the + * configuration data. + */ +struct wpa_config_blob { + /** + * name - Blob name + */ + char *name; + + /** + * data - Pointer to binary data + */ + const u8 *data; + + /** + * len - Length of binary data + */ + size_t len; + + /** + * next - Pointer to next blob in the configuration + */ + struct wpa_config_blob *next; +}; + +#endif /* EAP_CONFIG_H */ diff --git a/components/wpa_supplicant/include/wpa2/eap_peer/eap_defs.h b/components/wpa_supplicant/include/wpa2/eap_peer/eap_defs.h new file mode 100644 index 00000000..10995d38 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/eap_peer/eap_defs.h @@ -0,0 +1,92 @@ +/* + * EAP server/peer: Shared EAP definitions + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_DEFS_H +#define EAP_DEFS_H + +/* RFC 3748 - Extensible Authentication Protocol (EAP) */ + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct eap_hdr { + u8 code; + u8 identifier; + be16 length; /* including code and identifier; network byte order */ + /* followed by length-4 octets of data */ +} STRUCT_PACKED; + + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3, + EAP_CODE_FAILURE = 4 }; + +/* EAP Request and Response data begins with one octet Type. Success and + * Failure do not have additional data. */ + +/* + * EAP Method Types as allocated by IANA: + * http://www.iana.org/assignments/eap-numbers + */ +typedef enum { + EAP_TYPE_NONE = 0, + EAP_TYPE_IDENTITY = 1 /* RFC 3748 */, + EAP_TYPE_NOTIFICATION = 2 /* RFC 3748 */, + EAP_TYPE_NAK = 3 /* Response only, RFC 3748 */, + EAP_TYPE_MD5 = 4, /* RFC 3748 */ + EAP_TYPE_OTP = 5 /* RFC 3748 */, + EAP_TYPE_GTC = 6, /* RFC 3748 */ + EAP_TYPE_TLS = 13 /* RFC 2716 */, + EAP_TYPE_LEAP = 17 /* Cisco proprietary */, + EAP_TYPE_SIM = 18 /* RFC 4186 */, + EAP_TYPE_TTLS = 21 /* RFC 5281 */, + EAP_TYPE_AKA = 23 /* RFC 4187 */, + EAP_TYPE_PEAP = 25 /* draft-josefsson-pppext-eap-tls-eap-06.txt */, + EAP_TYPE_MSCHAPV2 = 26 /* draft-kamath-pppext-eap-mschapv2-00.txt */, + EAP_TYPE_TLV = 33 /* draft-josefsson-pppext-eap-tls-eap-07.txt */, + EAP_TYPE_TNC = 38 /* TNC IF-T v1.0-r3; note: tentative assignment; + * type 38 has previously been allocated for + * EAP-HTTP Digest, (funk.com) */, + EAP_TYPE_FAST = 43 /* RFC 4851 */, + EAP_TYPE_PAX = 46 /* RFC 4746 */, + EAP_TYPE_PSK = 47 /* RFC 4764 */, + EAP_TYPE_SAKE = 48 /* RFC 4763 */, + EAP_TYPE_IKEV2 = 49 /* RFC 5106 */, + EAP_TYPE_AKA_PRIME = 50 /* RFC 5448 */, + EAP_TYPE_GPSK = 51 /* RFC 5433 */, + EAP_TYPE_PWD = 52 /* RFC 5931 */, + EAP_TYPE_EKE = 53 /* RFC 6124 */, + EAP_TYPE_EXPANDED = 254 /* RFC 3748 */ +} EapType; + + +/* SMI Network Management Private Enterprise Code for vendor specific types */ +enum { + EAP_VENDOR_IETF = 0, + EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */, + EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */, + EAP_VENDOR_HOSTAP = 39068 /* hostapd/wpa_supplicant project */ +}; + +struct eap_expand { + u8 vendor_id[3]; + be32 vendor_type; + u8 opcode; +} STRUCT_PACKED; + +#define EAP_VENDOR_UNAUTH_TLS EAP_VENDOR_HOSTAP +#define EAP_VENDOR_TYPE_UNAUTH_TLS 1 + +#define EAP_MSK_LEN 64 +#define EAP_EMSK_LEN 64 + +#endif /* EAP_DEFS_H */ diff --git a/components/wpa_supplicant/include/wpa2/eap_peer/eap_i.h b/components/wpa_supplicant/include/wpa2/eap_peer/eap_i.h new file mode 100644 index 00000000..5554fcc4 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/eap_peer/eap_i.h @@ -0,0 +1,145 @@ +/* + * EAP peer state machines internal structures (RFC 4137) + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_I_H +#define EAP_I_H + +#include "wpa/wpabuf.h" +#include "eap.h" +#include "eap_common.h" +#include "eap_config.h" +#include "esp_wpa2.h" + +#include "esp_wifi_crypto_types.h" + +/* RFC 4137 - EAP Peer state machine */ + +typedef enum { + DECISION_FAIL, DECISION_COND_SUCC, DECISION_UNCOND_SUCC +} EapDecision; + +typedef enum { + METHOD_NONE, METHOD_INIT, METHOD_CONT, METHOD_MAY_CONT, METHOD_DONE +} EapMethodState; + +/** + * struct eap_method_ret - EAP return values from struct eap_method::process() + * + * These structure contains OUT variables for the interface between peer state + * machine and methods (RFC 4137, Sect. 4.2). eapRespData will be returned as + * the return value of struct eap_method::process() so it is not included in + * this structure. + */ +struct eap_method_ret { + /** + * ignore - Whether method decided to drop the current packed (OUT) + */ + Boolean ignore; + + /** + * methodState - Method-specific state (IN/OUT) + */ + EapMethodState methodState; + + /** + * decision - Authentication decision (OUT) + */ + EapDecision decision; + + /** + * allowNotifications - Whether method allows notifications (OUT) + */ + 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 3 + +/** + * struct eap_sm - EAP state machine data + */ +struct eap_sm { + void *eap_method_priv; + + void *ssl_ctx; + + unsigned int workaround; +///////////////////////////////////////////////// + struct pbuf *outbuf; + struct wpa_config_blob blob[BLOB_NUM]; + struct eap_peer_config config; + u8 current_identifier; + u8 ownaddr[ETH_ALEN]; +#ifdef USE_WPA2_TASK +#define SIG_WPA2_NUM 2 + 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 index 00000000..7d518dec --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/eap_peer/eap_methods.h @@ -0,0 +1,39 @@ +/* + * EAP peer: Method registration + * Copyright (c) 2004-2007, Jouni Malinen + * + * 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" +#include "eap_config.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); + +void eap_peer_unregister_methods(void); +int eap_peer_register_methods(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 index 00000000..7aad0dff --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/eap_peer/eap_peap_common.h @@ -0,0 +1,16 @@ +/* + * EAP-PEAP common routines + * Copyright (c) 2008-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_PEAP_COMMON_H +#define EAP_PEAP_COMMON_H + +int peap_prfplus(int version, const u8 *key, size_t key_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *buf, size_t buf_len); + +#endif /* EAP_PEAP_COMMON_H */ diff --git a/components/wpa_supplicant/include/wpa2/eap_peer/eap_tls.h b/components/wpa_supplicant/include/wpa2/eap_peer/eap_tls.h new file mode 100644 index 00000000..a8a386f2 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/eap_peer/eap_tls.h @@ -0,0 +1,25 @@ +/* + * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions + * Copyright (c) 2004-2009, 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_TLS_H +#define EAP_TLS_H + +#include "eap_i.h" +#include "eap_common.h" +#include "eap.h" +#include "wpa/wpabuf.h" + +void * eap_tls_init(struct eap_sm *sm); +void eap_tls_deinit(struct eap_sm *sm, void *priv); +struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData); + +u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len); + +#endif /* EAP_TLS_H */ diff --git a/components/wpa_supplicant/include/wpa2/eap_peer/eap_tls_common.h b/components/wpa_supplicant/include/wpa2/eap_peer/eap_tls_common.h new file mode 100644 index 00000000..1a5e0f89 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/eap_peer/eap_tls_common.h @@ -0,0 +1,131 @@ +/* + * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions + * Copyright (c) 2004-2009, 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_TLS_COMMON_H +#define EAP_TLS_COMMON_H + +/** + * struct eap_ssl_data - TLS data for EAP methods + */ +struct eap_ssl_data { + /** + * conn - TLS connection context data from tls_connection_init() + */ + struct tls_connection *conn; + + /** + * tls_out - TLS message to be sent out in fragments + */ + struct wpabuf *tls_out; + + /** + * tls_out_pos - The current position in the outgoing TLS message + */ + size_t tls_out_pos; + + /** + * tls_out_limit - Maximum fragment size for outgoing TLS messages + */ + size_t tls_out_limit; + + /** + * tls_in - Received TLS message buffer for re-assembly + */ + struct wpabuf *tls_in; + + /** + * tls_in_left - Number of remaining bytes in the incoming TLS message + */ + size_t tls_in_left; + + /** + * tls_in_total - Total number of bytes in the incoming TLS message + */ + size_t tls_in_total; + + /** + * phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel) + */ + int phase2; + + /** + * include_tls_length - Whether the TLS length field is included even + * if the TLS data is not fragmented + */ + int include_tls_length; + + /** + * eap - EAP state machine allocated with eap_peer_sm_init() + */ + struct eap_sm *eap; + + /** + * ssl_ctx - TLS library context to use for the connection + */ + void *ssl_ctx; + + /** + * eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) + */ + u8 eap_type; +}; + + +/* EAP TLS Flags */ +#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80 +#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40 +#define EAP_TLS_FLAGS_START 0x20 +#define EAP_TLS_VERSION_MASK 0x07 + + /* could be up to 128 bytes, but only the first 64 bytes are used */ +#define EAP_TLS_KEY_LEN 64 + +/* dummy type used as a flag for UNAUTH-TLS */ +#define EAP_UNAUTH_TLS_TYPE 255 + + +int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, + struct eap_peer_config *config, u8 eap_type); +void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); +u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, + const char *label, size_t len); +u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, + struct eap_ssl_data *data, u8 eap_type, + size_t *len); +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); +struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type, + int peap_version); +int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data); +int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, + char *buf, size_t buflen, int verbose); +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); +void eap_peer_tls_reset_input(struct eap_ssl_data *data); +void eap_peer_tls_reset_output(struct eap_ssl_data *data); +int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data, + const struct wpabuf *in_data, + struct wpabuf **in_decrypted); +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); +int eap_peer_select_phase2_methods(struct eap_peer_config *config, + const char *prefix, + struct eap_method_type **types, + size_t *num_types); +int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types, + struct eap_hdr *hdr, struct wpabuf **resp); + +#endif /* EAP_TLS_COMMON_H */ 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 index 00000000..3286055a --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/eap_peer/eap_tlv_common.h @@ -0,0 +1,112 @@ +/* + * EAP-TLV definitions (draft-josefsson-pppext-eap-tls-eap-10.txt) + * Copyright (c) 2004-2008, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_TLV_COMMON_H +#define EAP_TLV_COMMON_H + +/* EAP-TLV TLVs (draft-josefsson-ppext-eap-tls-eap-10.txt) */ +#define EAP_TLV_RESULT_TLV 3 /* Acknowledged Result */ +#define EAP_TLV_NAK_TLV 4 +#define EAP_TLV_ERROR_CODE_TLV 5 +#define EAP_TLV_CONNECTION_BINDING_TLV 6 +#define EAP_TLV_VENDOR_SPECIFIC_TLV 7 +#define EAP_TLV_URI_TLV 8 +#define EAP_TLV_EAP_PAYLOAD_TLV 9 +#define EAP_TLV_INTERMEDIATE_RESULT_TLV 10 +#define EAP_TLV_PAC_TLV 11 /* RFC 5422, Section 4.2 */ +#define EAP_TLV_CRYPTO_BINDING_TLV 12 +#define EAP_TLV_CALLING_STATION_ID_TLV 13 +#define EAP_TLV_CALLED_STATION_ID_TLV 14 +#define EAP_TLV_NAS_PORT_TYPE_TLV 15 +#define EAP_TLV_SERVER_IDENTIFIER_TLV 16 +#define EAP_TLV_IDENTITY_TYPE_TLV 17 +#define EAP_TLV_SERVER_TRUSTED_ROOT_TLV 18 +#define EAP_TLV_REQUEST_ACTION_TLV 19 +#define EAP_TLV_PKCS7_TLV 20 + +#define EAP_TLV_RESULT_SUCCESS 1 +#define EAP_TLV_RESULT_FAILURE 2 + +#define EAP_TLV_TYPE_MANDATORY 0x8000 +#define EAP_TLV_TYPE_MASK 0x3fff + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct eap_tlv_hdr { + be16 tlv_type; + be16 length; +} STRUCT_PACKED; + +struct eap_tlv_nak_tlv { + be16 tlv_type; + be16 length; + be32 vendor_id; + be16 nak_type; +} STRUCT_PACKED; + +struct eap_tlv_result_tlv { + be16 tlv_type; + be16 length; + be16 status; +} STRUCT_PACKED; + +/* RFC 4851, Section 4.2.7 - Intermediate-Result TLV */ +struct eap_tlv_intermediate_result_tlv { + be16 tlv_type; + be16 length; + be16 status; + /* Followed by optional TLVs */ +} STRUCT_PACKED; + +/* RFC 4851, Section 4.2.8 - Crypto-Binding TLV */ +struct eap_tlv_crypto_binding_tlv { + be16 tlv_type; + be16 length; + u8 reserved; + u8 version; + u8 received_version; + u8 subtype; + u8 nonce[32]; + u8 compound_mac[20]; +} STRUCT_PACKED; + +struct eap_tlv_pac_ack_tlv { + be16 tlv_type; + be16 length; + be16 pac_type; + be16 pac_len; + be16 result; +} STRUCT_PACKED; + +/* RFC 4851, Section 4.2.9 - Request-Action TLV */ +struct eap_tlv_request_action_tlv { + be16 tlv_type; + be16 length; + be16 action; +} STRUCT_PACKED; + +/* RFC 5422, Section 4.2.6 - PAC-Type TLV */ +struct eap_tlv_pac_type_tlv { + be16 tlv_type; /* PAC_TYPE_PAC_TYPE */ + be16 length; + be16 pac_type; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST 0 +#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE 1 + +#define EAP_TLV_ACTION_PROCESS_TLV 1 +#define EAP_TLV_ACTION_NEGOTIATE_EAP 2 + +#endif /* EAP_TLV_COMMON_H */ 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 index 00000000..17901d42 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/eap_peer/eap_ttls.h @@ -0,0 +1,65 @@ +/* + * EAP server/peer: EAP-TTLS (RFC 5281) + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_TTLS_H +#define EAP_TTLS_H + +struct ttls_avp { + be32 avp_code; + be32 avp_length; /* 8-bit flags, 24-bit length; + * length includes AVP header */ + /* optional 32-bit Vendor-ID */ + /* Data */ +}; + +struct ttls_avp_vendor { + be32 avp_code; + be32 avp_length; /* 8-bit flags, 24-bit length; + * length includes AVP header */ + be32 vendor_id; + /* Data */ +}; + +#define AVP_FLAGS_VENDOR 0x80 +#define AVP_FLAGS_MANDATORY 0x40 + +#define AVP_PAD(start, pos) \ +do { \ + int __pad; \ + __pad = (4 - (((pos) - (start)) & 3)) & 3; \ + os_memset((pos), 0, __pad); \ + pos += __pad; \ +} while (0) + + +/* RFC 2865 */ +#define RADIUS_ATTR_USER_NAME 1 +#define RADIUS_ATTR_USER_PASSWORD 2 +#define RADIUS_ATTR_CHAP_PASSWORD 3 +#define RADIUS_ATTR_REPLY_MESSAGE 18 +#define RADIUS_ATTR_CHAP_CHALLENGE 60 +#define RADIUS_ATTR_EAP_MESSAGE 79 + +/* RFC 2548 */ +#define RADIUS_VENDOR_ID_MICROSOFT 311 +#define RADIUS_ATTR_MS_CHAP_RESPONSE 1 +#define RADIUS_ATTR_MS_CHAP_ERROR 2 +#define RADIUS_ATTR_MS_CHAP_NT_ENC_PW 6 +#define RADIUS_ATTR_MS_CHAP_CHALLENGE 11 +#define RADIUS_ATTR_MS_CHAP2_RESPONSE 25 +#define RADIUS_ATTR_MS_CHAP2_SUCCESS 26 +#define RADIUS_ATTR_MS_CHAP2_CPW 27 + +#define EAP_TTLS_MSCHAPV2_CHALLENGE_LEN 16 +#define EAP_TTLS_MSCHAPV2_RESPONSE_LEN 50 +#define EAP_TTLS_MSCHAP_CHALLENGE_LEN 8 +#define EAP_TTLS_MSCHAP_RESPONSE_LEN 50 +#define EAP_TTLS_CHAP_CHALLENGE_LEN 16 +#define EAP_TTLS_CHAP_PASSWORD_LEN 16 + +#endif /* EAP_TTLS_H */ 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 index 00000000..ef750819 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/eap_peer/mschapv2.h @@ -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 */ diff --git a/components/wpa_supplicant/include/wpa2/tls/asn1.h b/components/wpa_supplicant/include/wpa2/tls/asn1.h new file mode 100644 index 00000000..6342c4cc --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/asn1.h @@ -0,0 +1,66 @@ +/* + * ASN.1 DER parsing + * Copyright (c) 2006, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef ASN1_H +#define ASN1_H + +#define ASN1_TAG_EOC 0x00 /* not used with DER */ +#define ASN1_TAG_BOOLEAN 0x01 +#define ASN1_TAG_INTEGER 0x02 +#define ASN1_TAG_BITSTRING 0x03 +#define ASN1_TAG_OCTETSTRING 0x04 +#define ASN1_TAG_NULL 0x05 +#define ASN1_TAG_OID 0x06 +#define ASN1_TAG_OBJECT_DESCRIPTOR 0x07 /* not yet parsed */ +#define ASN1_TAG_EXTERNAL 0x08 /* not yet parsed */ +#define ASN1_TAG_REAL 0x09 /* not yet parsed */ +#define ASN1_TAG_ENUMERATED 0x0A /* not yet parsed */ +#define ASN1_TAG_UTF8STRING 0x0C /* not yet parsed */ +#define ANS1_TAG_RELATIVE_OID 0x0D +#define ASN1_TAG_SEQUENCE 0x10 /* shall be constructed */ +#define ASN1_TAG_SET 0x11 +#define ASN1_TAG_NUMERICSTRING 0x12 /* not yet parsed */ +#define ASN1_TAG_PRINTABLESTRING 0x13 +#define ASN1_TAG_TG1STRING 0x14 /* not yet parsed */ +#define ASN1_TAG_VIDEOTEXSTRING 0x15 /* not yet parsed */ +#define ASN1_TAG_IA5STRING 0x16 +#define ASN1_TAG_UTCTIME 0x17 +#define ASN1_TAG_GENERALIZEDTIME 0x18 /* not yet parsed */ +#define ASN1_TAG_GRAPHICSTRING 0x19 /* not yet parsed */ +#define ASN1_TAG_VISIBLESTRING 0x1A +#define ASN1_TAG_GENERALSTRING 0x1B /* not yet parsed */ +#define ASN1_TAG_UNIVERSALSTRING 0x1C /* not yet parsed */ +#define ASN1_TAG_BMPSTRING 0x1D /* not yet parsed */ + +#define ASN1_CLASS_UNIVERSAL 0 +#define ASN1_CLASS_APPLICATION 1 +#define ASN1_CLASS_CONTEXT_SPECIFIC 2 +#define ASN1_CLASS_PRIVATE 3 + + +struct asn1_hdr { + const u8 *payload; + u8 identifier, class, constructed; + unsigned int tag, length; +}; + +#define ASN1_MAX_OID_LEN 20 +struct asn1_oid { + unsigned long oid[ASN1_MAX_OID_LEN]; + size_t len; +}; + + +int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr); +int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid); +int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid, + const u8 **next); +void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len); +unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len); + +#endif /* ASN1_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/bignum.h b/components/wpa_supplicant/include/wpa2/tls/bignum.h new file mode 100644 index 00000000..f25e2678 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/bignum.h @@ -0,0 +1,38 @@ +/* + * Big number math + * Copyright (c) 2006, Jouni Malinen + * + * 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. + */ + +#ifndef BIGNUM_H +#define BIGNUM_H + +struct bignum; + +struct bignum * bignum_init(void); +void bignum_deinit(struct bignum *n); +size_t bignum_get_unsigned_bin_len(struct bignum *n); +int bignum_get_unsigned_bin(const struct bignum *n, u8 *buf, size_t *len); +int bignum_set_unsigned_bin(struct bignum *n, const u8 *buf, size_t len); +int bignum_cmp(const struct bignum *a, const struct bignum *b); +int bignum_cmp_d(const struct bignum *a, unsigned long b); +int bignum_add(const struct bignum *a, const struct bignum *b, + struct bignum *c); +int bignum_sub(const struct bignum *a, const struct bignum *b, + struct bignum *c); +int bignum_mul(const struct bignum *a, const struct bignum *b, + struct bignum *c); +int bignum_mulmod(const struct bignum *a, const struct bignum *b, + const struct bignum *c, struct bignum *d); +int bignum_exptmod(const struct bignum *a, const struct bignum *b, + const struct bignum *c, struct bignum *d); + +#endif /* BIGNUM_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/libtommath.h b/components/wpa_supplicant/include/wpa2/tls/libtommath.h new file mode 100644 index 00000000..9be311e8 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/libtommath.h @@ -0,0 +1,3441 @@ +/* + * Minimal code for RSA support from LibTomMath 0.41 + * http://libtom.org/ + * http://libtom.org/files/ltm-0.41.tar.bz2 + * This library was released in public domain by Tom St Denis. + * + * The combination in this file may not use all of the optimized algorithms + * from LibTomMath and may be considerable slower than the LibTomMath with its + * default settings. The main purpose of having this version here is to make it + * easier to build bignum.c wrapper without having to install and build an + * external library. + * + * If CONFIG_INTERNAL_LIBTOMMATH is defined, bignum.c includes this + * libtommath.c file instead of using the external LibTomMath library. + */ +#include "os.h" +#include "stdarg.h" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + +#define BN_MP_INVMOD_C +#define BN_S_MP_EXPTMOD_C /* Note: #undef in tommath_superclass.h; this would + * require BN_MP_EXPTMOD_FAST_C instead */ +#define BN_S_MP_MUL_DIGS_C +#define BN_MP_INVMOD_SLOW_C +#define BN_S_MP_SQR_C +#define BN_S_MP_MUL_HIGH_DIGS_C /* Note: #undef in tommath_superclass.h; this + * would require other than mp_reduce */ + +#ifdef LTM_FAST + +/* Use faster div at the cost of about 1 kB */ +#define BN_MP_MUL_D_C + +/* Include faster exptmod (Montgomery) at the cost of about 2.5 kB in code */ +#define BN_MP_EXPTMOD_FAST_C +#define BN_MP_MONTGOMERY_SETUP_C +#define BN_FAST_MP_MONTGOMERY_REDUCE_C +#define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C +#define BN_MP_MUL_2_C + +/* Include faster sqr at the cost of about 0.5 kB in code */ +#define BN_FAST_S_MP_SQR_C + +#else /* LTM_FAST */ + +#define BN_MP_DIV_SMALL +#define BN_MP_INIT_MULTI_C +#define BN_MP_CLEAR_MULTI_C +#define BN_MP_ABS_C +#endif /* LTM_FAST */ + +/* Current uses do not require support for negative exponent in exptmod, so we + * can save about 1.5 kB in leaving out invmod. */ +#define LTM_NO_NEG_EXP + +/* from tommath.h */ + +#ifndef MIN + #define MIN(x,y) ((x)<(y)?(x):(y)) +#endif + +#ifndef MAX + #define MAX(x,y) ((x)>(y)?(x):(y)) +#endif + +#define OPT_CAST(x) (x *) + +typedef unsigned long mp_digit; +typedef u64 mp_word; + +#define DIGIT_BIT 28 +#define MP_28BIT + + +#define XMALLOC os_malloc +#define XFREE os_free +#define XREALLOC os_realloc + + +#define MP_MASK ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1)) + +#define MP_LT -1 /* less than */ +#define MP_EQ 0 /* equal to */ +#define MP_GT 1 /* greater than */ + +#define MP_ZPOS 0 /* positive integer */ +#define MP_NEG 1 /* negative */ + +#define MP_OKAY 0 /* ok result */ +#define MP_MEM -2 /* out of mem */ +#define MP_VAL -3 /* invalid input */ + +#define MP_YES 1 /* yes response */ +#define MP_NO 0 /* no response */ + +typedef int mp_err; + +/* define this to use lower memory usage routines (exptmods mostly) */ +#define MP_LOW_MEM + +/* default precision */ +#ifndef MP_PREC + #ifndef MP_LOW_MEM + #define MP_PREC 32 /* default digits of precision */ + #else + #define MP_PREC 8 /* default digits of precision */ + #endif +#endif + +/* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */ +#define MP_WARRAY (1 << (sizeof(mp_word) * CHAR_BIT - 2 * DIGIT_BIT + 1)) + +/* the infamous mp_int structure */ +typedef struct { + int used, alloc, sign; + mp_digit *dp; +} mp_int; + + +/* ---> Basic Manipulations <--- */ +#define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO) +#define mp_iseven(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 0)) ? MP_YES : MP_NO) +#define mp_isodd(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 1)) ? MP_YES : MP_NO) + + +/* prototypes for copied functions */ +#define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1) +static int s_mp_exptmod(mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode); +static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs); +static int s_mp_sqr(mp_int * a, mp_int * b); +static int s_mp_mul_high_digs(mp_int * a, mp_int * b, mp_int * c, int digs); + +static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs); + +#ifdef BN_MP_INIT_MULTI_C +static int mp_init_multi(mp_int *mp, ...); +#endif +#ifdef BN_MP_CLEAR_MULTI_C +static void mp_clear_multi(mp_int *mp, ...); +#endif +static int mp_lshd(mp_int * a, int b); +static void mp_set(mp_int * a, mp_digit b); +static void mp_clamp(mp_int * a); +static void mp_exch(mp_int * a, mp_int * b); +static void mp_rshd(mp_int * a, int b); +static void mp_zero(mp_int * a); +static int mp_mod_2d(mp_int * a, int b, mp_int * c); +static int mp_div_2d(mp_int * a, int b, mp_int * c, mp_int * d); +static int mp_init_copy(mp_int * a, mp_int * b); +static int mp_mul_2d(mp_int * a, int b, mp_int * c); +#ifndef LTM_NO_NEG_EXP +static int mp_div_2(mp_int * a, mp_int * b); +static int mp_invmod(mp_int * a, mp_int * b, mp_int * c); +static int mp_invmod_slow(mp_int * a, mp_int * b, mp_int * c); +#endif /* LTM_NO_NEG_EXP */ +static int mp_copy(mp_int * a, mp_int * b); +static int mp_count_bits(mp_int * a); +static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d); +static int mp_mod(mp_int * a, mp_int * b, mp_int * c); +static int mp_grow(mp_int * a, int size); +static int mp_cmp_mag(mp_int * a, mp_int * b); +#ifdef BN_MP_ABS_C +static int mp_abs(mp_int * a, mp_int * b); +#endif +static int mp_sqr(mp_int * a, mp_int * b); +static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d); +static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d); +static int mp_2expt(mp_int * a, int b); +static int mp_reduce_setup(mp_int * a, mp_int * b); +static int mp_reduce(mp_int * x, mp_int * m, mp_int * mu); +static int mp_init_size(mp_int * a, int size); +#ifdef BN_MP_EXPTMOD_FAST_C +static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode); +#endif /* BN_MP_EXPTMOD_FAST_C */ +#ifdef BN_FAST_S_MP_SQR_C +static int fast_s_mp_sqr (mp_int * a, mp_int * b); +#endif /* BN_FAST_S_MP_SQR_C */ +#ifdef BN_MP_MUL_D_C +static int mp_mul_d (mp_int * a, mp_digit b, mp_int * c); +#endif /* BN_MP_MUL_D_C */ + + + +/* functions from bn_.c */ + + +/* reverse an array, used for radix code */ +static void +bn_reverse (unsigned char *s, int len) +{ + int ix, iy; + unsigned char t; + + ix = 0; + iy = len - 1; + while (ix < iy) { + t = s[ix]; + s[ix] = s[iy]; + s[iy] = t; + ++ix; + --iy; + } +} + + +/* low level addition, based on HAC pp.594, Algorithm 14.7 */ +static int +s_mp_add (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int *x; + int olduse, res, min, max; + + /* find sizes, we let |a| <= |b| which means we have to sort + * them. "x" will point to the input with the most digits + */ + if (a->used > b->used) { + min = b->used; + max = a->used; + x = a; + } else { + min = a->used; + max = b->used; + x = b; + } + + /* init result */ + if (c->alloc < max + 1) { + if ((res = mp_grow (c, max + 1)) != MP_OKAY) { + return res; + } + } + + /* get old used digit count and set new one */ + olduse = c->used; + c->used = max + 1; + + { + register mp_digit u, *tmpa, *tmpb, *tmpc; + register int i; + + /* alias for digit pointers */ + + /* first input */ + tmpa = a->dp; + + /* second input */ + tmpb = b->dp; + + /* destination */ + tmpc = c->dp; + + /* zero the carry */ + u = 0; + for (i = 0; i < min; i++) { + /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */ + *tmpc = *tmpa++ + *tmpb++ + u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)DIGIT_BIT); + + /* take away carry bit from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* now copy higher words if any, that is in A+B + * if A or B has more digits add those in + */ + if (min != max) { + for (; i < max; i++) { + /* T[i] = X[i] + U */ + *tmpc = x->dp[i] + u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)DIGIT_BIT); + + /* take away carry bit from T[i] */ + *tmpc++ &= MP_MASK; + } + } + + /* add carry */ + *tmpc++ = u; + + /* clear digits above oldused */ + for (i = c->used; i < olduse; i++) { + *tmpc++ = 0; + } + } + + mp_clamp (c); + return MP_OKAY; +} + + +/* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */ +static int +s_mp_sub (mp_int * a, mp_int * b, mp_int * c) +{ + int olduse, res, min, max; + + /* find sizes */ + min = b->used; + max = a->used; + + /* init result */ + if (c->alloc < max) { + if ((res = mp_grow (c, max)) != MP_OKAY) { + return res; + } + } + olduse = c->used; + c->used = max; + + { + register mp_digit u, *tmpa, *tmpb, *tmpc; + register int i; + + /* alias for digit pointers */ + tmpa = a->dp; + tmpb = b->dp; + tmpc = c->dp; + + /* set carry to zero */ + u = 0; + for (i = 0; i < min; i++) { + /* T[i] = A[i] - B[i] - U */ + *tmpc = *tmpa++ - *tmpb++ - u; + + /* U = carry bit of T[i] + * Note this saves performing an AND operation since + * if a carry does occur it will propagate all the way to the + * MSB. As a result a single shift is enough to get the carry + */ + u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); + + /* Clear carry from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* now copy higher words if any, e.g. if A has more digits than B */ + for (; i < max; i++) { + /* T[i] = A[i] - U */ + *tmpc = *tmpa++ - u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); + + /* Clear carry from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* clear digits above used (since we may not have grown result above) */ + for (i = c->used; i < olduse; i++) { + *tmpc++ = 0; + } + } + + mp_clamp (c); + return MP_OKAY; +} + + +/* init a new mp_int */ +static int +mp_init (mp_int * a) +{ + int i; + + /* allocate memory required and clear it */ + a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * MP_PREC); + if (a->dp == NULL) { + return MP_MEM; + } + + /* set the digits to zero */ + for (i = 0; i < MP_PREC; i++) { + a->dp[i] = 0; + } + + /* set the used to zero, allocated digits to the default precision + * and sign to positive */ + a->used = 0; + a->alloc = MP_PREC; + a->sign = MP_ZPOS; + + return MP_OKAY; +} + + +/* clear one (frees) */ +static void +mp_clear (mp_int * a) +{ + int i; + + /* only do anything if a hasn't been freed previously */ + if (a->dp != NULL) { + /* first zero the digits */ + for (i = 0; i < a->used; i++) { + a->dp[i] = 0; + } + + /* free ram */ + XFREE(a->dp); + + /* reset members to make debugging easier */ + a->dp = NULL; + a->alloc = a->used = 0; + a->sign = MP_ZPOS; + } +} + + +/* high level addition (handles signs) */ +static int +mp_add (mp_int * a, mp_int * b, mp_int * c) +{ + int sa, sb, res; + + /* get sign of both inputs */ + sa = a->sign; + sb = b->sign; + + /* handle two cases, not four */ + if (sa == sb) { + /* both positive or both negative */ + /* add their magnitudes, copy the sign */ + c->sign = sa; + res = s_mp_add (a, b, c); + } else { + /* one positive, the other negative */ + /* subtract the one with the greater magnitude from */ + /* the one of the lesser magnitude. The result gets */ + /* the sign of the one with the greater magnitude. */ + if (mp_cmp_mag (a, b) == MP_LT) { + c->sign = sb; + res = s_mp_sub (b, a, c); + } else { + c->sign = sa; + res = s_mp_sub (a, b, c); + } + } + return res; +} + + +/* high level subtraction (handles signs) */ +static int +mp_sub (mp_int * a, mp_int * b, mp_int * c) +{ + int sa, sb, res; + + sa = a->sign; + sb = b->sign; + + if (sa != sb) { + /* subtract a negative from a positive, OR */ + /* subtract a positive from a negative. */ + /* In either case, ADD their magnitudes, */ + /* and use the sign of the first number. */ + c->sign = sa; + res = s_mp_add (a, b, c); + } else { + /* subtract a positive from a positive, OR */ + /* subtract a negative from a negative. */ + /* First, take the difference between their */ + /* magnitudes, then... */ + if (mp_cmp_mag (a, b) != MP_LT) { + /* Copy the sign from the first */ + c->sign = sa; + /* The first has a larger or equal magnitude */ + res = s_mp_sub (a, b, c); + } else { + /* The result has the *opposite* sign from */ + /* the first number. */ + c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS; + /* The second has a larger magnitude */ + res = s_mp_sub (b, a, c); + } + } + return res; +} + + +/* high level multiplication (handles sign) */ +static int +mp_mul (mp_int * a, mp_int * b, mp_int * c) +{ + int res, neg; + neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + + /* use Toom-Cook? */ +#ifdef BN_MP_TOOM_MUL_C + if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) { + res = mp_toom_mul(a, b, c); + } else +#endif +#ifdef BN_MP_KARATSUBA_MUL_C + /* use Karatsuba? */ + if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) { + res = mp_karatsuba_mul (a, b, c); + } else +#endif + { + /* can we use the fast multiplier? + * + * The fast multiplier can be used if the output will + * have less than MP_WARRAY digits and the number of + * digits won't affect carry propagation + */ +#ifdef BN_FAST_S_MP_MUL_DIGS_C + int digs = a->used + b->used + 1; + + if ((digs < MP_WARRAY) && + MIN(a->used, b->used) <= + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + res = fast_s_mp_mul_digs (a, b, c, digs); + } else +#endif +#ifdef BN_S_MP_MUL_DIGS_C + res = s_mp_mul (a, b, c); /* uses s_mp_mul_digs */ +#else +#error mp_mul could fail + res = MP_VAL; +#endif + + } + c->sign = (c->used > 0) ? neg : MP_ZPOS; + return res; +} + + +/* d = a * b (mod c) */ +static int +mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + int res; + mp_int t; + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_mul (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + res = mp_mod (&t, c, d); + mp_clear (&t); + return res; +} + + +/* c = a mod b, 0 <= c < b */ +static int +mp_mod (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int t; + int res; + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_div (a, b, NULL, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + + if (t.sign != b->sign) { + res = mp_add (b, &t, c); + } else { + res = MP_OKAY; + mp_exch (&t, c); + } + + mp_clear (&t); + return res; +} + + +/* this is a shell function that calls either the normal or Montgomery + * exptmod functions. Originally the call to the montgomery code was + * 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 +mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) +{ +#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) { + return MP_VAL; + } + + /* if exponent X is negative we have to recurse */ + if (X->sign == MP_NEG) { +#ifdef LTM_NO_NEG_EXP + return MP_VAL; +#else /* LTM_NO_NEG_EXP */ +#ifdef BN_MP_INVMOD_C + mp_int tmpG, tmpX; + int err; + + /* first compute 1/G mod P */ + if ((err = mp_init(&tmpG)) != MP_OKAY) { + return err; + } + if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) { + mp_clear(&tmpG); + return err; + } + + /* now get |X| */ + if ((err = mp_init(&tmpX)) != MP_OKAY) { + mp_clear(&tmpG); + return err; + } + if ((err = mp_abs(X, &tmpX)) != MP_OKAY) { + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; + } + + /* and now compute (1/G)**|X| instead of G**X [X < 0] */ + err = mp_exptmod(&tmpG, &tmpX, P, Y); + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; +#else +#error mp_exptmod would always fail + /* no invmod */ + return MP_VAL; +#endif +#endif /* LTM_NO_NEG_EXP */ + } + +/* modified diminished radix reduction */ +#if defined(BN_MP_REDUCE_IS_2K_L_C) && defined(BN_MP_REDUCE_2K_L_C) && defined(BN_S_MP_EXPTMOD_C) + if (mp_reduce_is_2k_l(P) == MP_YES) { + return s_mp_exptmod(G, X, P, Y, 1); + } +#endif + +#ifdef BN_MP_DR_IS_MODULUS_C + /* is it a DR modulus? */ + dr = mp_dr_is_modulus(P); +#endif + +#ifdef BN_MP_REDUCE_IS_2K_C + /* if not, is it a unrestricted DR modulus? */ + if (dr == 0) { + dr = mp_reduce_is_2k(P) << 1; + } +#endif + + /* if the modulus is odd or dr != 0 use the montgomery method */ +#ifdef BN_MP_EXPTMOD_FAST_C + if (mp_isodd (P) == 1 || dr != 0) { + return mp_exptmod_fast (G, X, P, Y, dr); + } else { +#endif +#ifdef BN_S_MP_EXPTMOD_C + /* otherwise use the generic Barrett reduction technique */ + return s_mp_exptmod (G, X, P, Y, 0); +#else +#error mp_exptmod could fail + /* no exptmod for evens */ + return MP_VAL; +#endif +#ifdef BN_MP_EXPTMOD_FAST_C + } +#endif +} + + +/* compare two ints (signed)*/ +static int +mp_cmp (mp_int * a, mp_int * b) +{ + /* compare based on sign */ + if (a->sign != b->sign) { + if (a->sign == MP_NEG) { + return MP_LT; + } else { + return MP_GT; + } + } + + /* compare digits */ + if (a->sign == MP_NEG) { + /* if negative compare opposite direction */ + return mp_cmp_mag(b, a); + } else { + return mp_cmp_mag(a, b); + } +} + + +/* compare a digit */ +static int +mp_cmp_d(mp_int * a, mp_digit b) +{ + /* compare based on sign */ + if (a->sign == MP_NEG) { + return MP_LT; + } + + /* compare based on magnitude */ + if (a->used > 1) { + return MP_GT; + } + + /* compare the only digit of a to b */ + if (a->dp[0] > b) { + return MP_GT; + } else if (a->dp[0] < b) { + return MP_LT; + } else { + return MP_EQ; + } +} + + +#ifndef LTM_NO_NEG_EXP +/* hac 14.61, pp608 */ +static int +mp_invmod (mp_int * a, mp_int * b, mp_int * c) +{ + /* b cannot be negative */ + if (b->sign == MP_NEG || mp_iszero(b) == 1) { + return MP_VAL; + } + +#ifdef BN_FAST_MP_INVMOD_C + /* if the modulus is odd we can use a faster routine instead */ + if (mp_isodd (b) == 1) { + return fast_mp_invmod (a, b, c); + } +#endif + +#ifdef BN_MP_INVMOD_SLOW_C + return mp_invmod_slow(a, b, c); +#endif + +#ifndef BN_FAST_MP_INVMOD_C +#ifndef BN_MP_INVMOD_SLOW_C +#error mp_invmod would always fail +#endif +#endif + return MP_VAL; +} +#endif /* LTM_NO_NEG_EXP */ + + +/* get the size for an unsigned equivalent */ +static int +mp_unsigned_bin_size (mp_int * a) +{ + int size = mp_count_bits (a); + return (size / 8 + ((size & 7) != 0 ? 1 : 0)); +} + + +#ifndef LTM_NO_NEG_EXP +/* hac 14.61, pp608 */ +static int +mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int x, y, u, v, A, B, C, D; + int res; + + /* b cannot be negative */ + if (b->sign == MP_NEG || mp_iszero(b) == 1) { + return MP_VAL; + } + + /* init temps */ + if ((res = mp_init_multi(&x, &y, &u, &v, + &A, &B, &C, &D, NULL)) != MP_OKAY) { + return res; + } + + /* x = a, y = b */ + if ((res = mp_mod(a, b, &x)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_copy (b, &y)) != MP_OKAY) { + goto LBL_ERR; + } + + /* 2. [modified] if x,y are both even then return an error! */ + if (mp_iseven (&x) == 1 && mp_iseven (&y) == 1) { + res = MP_VAL; + goto LBL_ERR; + } + + /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ + if ((res = mp_copy (&x, &u)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_copy (&y, &v)) != MP_OKAY) { + goto LBL_ERR; + } + mp_set (&A, 1); + mp_set (&D, 1); + +top: + /* 4. while u is even do */ + while (mp_iseven (&u) == 1) { + /* 4.1 u = u/2 */ + if ((res = mp_div_2 (&u, &u)) != MP_OKAY) { + goto LBL_ERR; + } + /* 4.2 if A or B is odd then */ + if (mp_isodd (&A) == 1 || mp_isodd (&B) == 1) { + /* A = (A+y)/2, B = (B-x)/2 */ + if ((res = mp_add (&A, &y, &A)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* A = A/2, B = B/2 */ + if ((res = mp_div_2 (&A, &A)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_div_2 (&B, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 5. while v is even do */ + while (mp_iseven (&v) == 1) { + /* 5.1 v = v/2 */ + if ((res = mp_div_2 (&v, &v)) != MP_OKAY) { + goto LBL_ERR; + } + /* 5.2 if C or D is odd then */ + if (mp_isodd (&C) == 1 || mp_isodd (&D) == 1) { + /* C = (C+y)/2, D = (D-x)/2 */ + if ((res = mp_add (&C, &y, &C)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* C = C/2, D = D/2 */ + if ((res = mp_div_2 (&C, &C)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_div_2 (&D, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 6. if u >= v then */ + if (mp_cmp (&u, &v) != MP_LT) { + /* u = u - v, A = A - C, B = B - D */ + if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&A, &C, &A)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } else { + /* v - v - u, C = C - A, D = D - B */ + if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&C, &A, &C)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* if not zero goto step 4 */ + if (mp_iszero (&u) == 0) + goto top; + + /* now a = C, b = D, gcd == g*v */ + + /* if v != 1 then there is no inverse */ + if (mp_cmp_d (&v, 1) != MP_EQ) { + res = MP_VAL; + goto LBL_ERR; + } + + /* if its too low */ + while (mp_cmp_d(&C, 0) == MP_LT) { + if ((res = mp_add(&C, b, &C)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* too big */ + while (mp_cmp_mag(&C, b) != MP_LT) { + if ((res = mp_sub(&C, b, &C)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* C is now the inverse */ + mp_exch (&C, c); + res = MP_OKAY; +LBL_ERR:mp_clear_multi (&x, &y, &u, &v, &A, &B, &C, &D, NULL); + return res; +} +#endif /* LTM_NO_NEG_EXP */ + + +/* compare maginitude of two ints (unsigned) */ +static int +mp_cmp_mag (mp_int * a, mp_int * b) +{ + int n; + mp_digit *tmpa, *tmpb; + + /* compare based on # of non-zero digits */ + if (a->used > b->used) { + return MP_GT; + } + + if (a->used < b->used) { + return MP_LT; + } + + /* alias for a */ + tmpa = a->dp + (a->used - 1); + + /* alias for b */ + tmpb = b->dp + (a->used - 1); + + /* compare based on digits */ + for (n = 0; n < a->used; ++n, --tmpa, --tmpb) { + if (*tmpa > *tmpb) { + return MP_GT; + } + + if (*tmpa < *tmpb) { + return MP_LT; + } + } + return MP_EQ; +} + + +/* reads a unsigned char array, assumes the msb is stored first [big endian] */ +static int +mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c) +{ + int res; + + /* make sure there are at least two digits */ + if (a->alloc < 2) { + if ((res = mp_grow(a, 2)) != MP_OKAY) { + return res; + } + } + + /* zero the int */ + mp_zero (a); + + /* read the bytes in */ + while (c-- > 0) { + if ((res = mp_mul_2d (a, 8, a)) != MP_OKAY) { + return res; + } + +#ifndef MP_8BIT + a->dp[0] |= *b++; + a->used += 1; +#else + a->dp[0] = (*b & MP_MASK); + a->dp[1] |= ((*b++ >> 7U) & 1); + a->used += 2; +#endif + } + mp_clamp (a); + return MP_OKAY; +} + + +/* store in unsigned [big endian] format */ +static int +mp_to_unsigned_bin (mp_int * a, unsigned char *b) +{ + int x, res; + mp_int t; + + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + + x = 0; + while (mp_iszero (&t) == 0) { +#ifndef MP_8BIT + b[x++] = (unsigned char) (t.dp[0] & 255); +#else + b[x++] = (unsigned char) (t.dp[0] | ((t.dp[1] & 0x01) << 7)); +#endif + if ((res = mp_div_2d (&t, 8, &t, NULL)) != MP_OKAY) { + mp_clear (&t); + return res; + } + } + bn_reverse (b, x); + mp_clear (&t); + return MP_OKAY; +} + + +/* shift right by a certain bit count (store quotient in c, optional remainder in d) */ +static int +mp_div_2d (mp_int * a, int b, mp_int * c, mp_int * d) +{ + mp_digit D, r, rr; + int x, res; + mp_int t; + + + /* if the shift count is <= 0 then we do no work */ + if (b <= 0) { + res = mp_copy (a, c); + if (d != NULL) { + mp_zero (d); + } + return res; + } + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + /* get the remainder */ + if (d != NULL) { + if ((res = mp_mod_2d (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + } + + /* copy */ + if ((res = mp_copy (a, c)) != MP_OKAY) { + mp_clear (&t); + return res; + } + + /* shift by as many digits in the bit count */ + if (b >= (int)DIGIT_BIT) { + mp_rshd (c, b / DIGIT_BIT); + } + + /* shift any bit count < DIGIT_BIT */ + D = (mp_digit) (b % DIGIT_BIT); + if (D != 0) { + register mp_digit *tmpc, mask, shift; + + /* mask */ + mask = (((mp_digit)1) << D) - 1; + + /* shift for lsb */ + shift = DIGIT_BIT - D; + + /* alias */ + tmpc = c->dp + (c->used - 1); + + /* carry */ + r = 0; + for (x = c->used - 1; x >= 0; x--) { + /* get the lower bits of this word in a temp */ + rr = *tmpc & mask; + + /* shift the current word and mix in the carry bits from the previous word */ + *tmpc = (*tmpc >> D) | (r << shift); + --tmpc; + + /* set the carry to the carry bits of the current word found above */ + r = rr; + } + } + mp_clamp (c); + if (d != NULL) { + mp_exch (&t, d); + } + mp_clear (&t); + return MP_OKAY; +} + + +static int +mp_init_copy (mp_int * a, mp_int * b) +{ + int res; + + if ((res = mp_init (a)) != MP_OKAY) { + return res; + } + return mp_copy (b, a); +} + + +/* set to zero */ +static void +mp_zero (mp_int * a) +{ + int n; + mp_digit *tmp; + + a->sign = MP_ZPOS; + a->used = 0; + + tmp = a->dp; + for (n = 0; n < a->alloc; n++) { + *tmp++ = 0; + } +} + + +/* copy, b = a */ +static int +mp_copy (mp_int * a, mp_int * b) +{ + int res, n; + + /* if dst == src do nothing */ + if (a == b) { + return MP_OKAY; + } + + /* grow dest */ + if (b->alloc < a->used) { + if ((res = mp_grow (b, a->used)) != MP_OKAY) { + return res; + } + } + + /* zero b and copy the parameters over */ + { + register mp_digit *tmpa, *tmpb; + + /* pointer aliases */ + + /* source */ + tmpa = a->dp; + + /* destination */ + tmpb = b->dp; + + /* copy all the digits */ + for (n = 0; n < a->used; n++) { + *tmpb++ = *tmpa++; + } + + /* clear high digits */ + for (; n < b->used; n++) { + *tmpb++ = 0; + } + } + + /* copy used count and sign */ + b->used = a->used; + b->sign = a->sign; + return MP_OKAY; +} + + +/* shift right a certain amount of digits */ +static void +mp_rshd (mp_int * a, int b) +{ + int x; + + /* if b <= 0 then ignore it */ + if (b <= 0) { + return; + } + + /* if b > used then simply zero it and return */ + if (a->used <= b) { + mp_zero (a); + return; + } + + { + register mp_digit *bottom, *top; + + /* shift the digits down */ + + /* bottom */ + bottom = a->dp; + + /* top [offset into digits] */ + top = a->dp + b; + + /* this is implemented as a sliding window where + * the window is b-digits long and digits from + * the top of the window are copied to the bottom + * + * e.g. + + b-2 | b-1 | b0 | b1 | b2 | ... | bb | ----> + /\ | ----> + \-------------------/ ----> + */ + for (x = 0; x < (a->used - b); x++) { + *bottom++ = *top++; + } + + /* zero the top digits */ + for (; x < a->used; x++) { + *bottom++ = 0; + } + } + + /* remove excess digits */ + a->used -= b; +} + + +/* swap the elements of two integers, for cases where you can't simply swap the + * mp_int pointers around + */ +static void +mp_exch (mp_int * a, mp_int * b) +{ + mp_int t; + + t = *a; + *a = *b; + *b = t; +} + + +/* trim unused digits + * + * This is used to ensure that leading zero digits are + * trimed and the leading "used" digit will be non-zero + * Typically very fast. Also fixes the sign if there + * are no more leading digits + */ +static void +mp_clamp (mp_int * a) +{ + /* decrease used while the most significant digit is + * zero. + */ + while (a->used > 0 && a->dp[a->used - 1] == 0) { + --(a->used); + } + + /* reset the sign flag if used == 0 */ + if (a->used == 0) { + a->sign = MP_ZPOS; + } +} + + +/* grow as required */ +static int +mp_grow (mp_int * a, int size) +{ + int i; + mp_digit *tmp; + + /* if the alloc size is smaller alloc more ram */ + if (a->alloc < size) { + /* ensure there are always at least MP_PREC digits extra on top */ + size += (MP_PREC * 2) - (size % MP_PREC); + + /* reallocate the array a->dp + * + * We store the return in a temporary variable + * in case the operation failed we don't want + * to overwrite the dp member of a. + */ + tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * size); + if (tmp == NULL) { + /* reallocation failed but "a" is still valid [can be freed] */ + return MP_MEM; + } + + /* reallocation succeeded so set a->dp */ + a->dp = tmp; + + /* zero excess digits */ + i = a->alloc; + a->alloc = size; + for (; i < a->alloc; i++) { + a->dp[i] = 0; + } + } + return MP_OKAY; +} + + +#ifdef BN_MP_ABS_C +/* b = |a| + * + * Simple function copies the input and fixes the sign to positive + */ +static int +mp_abs (mp_int * a, mp_int * b) +{ + int res; + + /* copy a to b */ + if (a != b) { + if ((res = mp_copy (a, b)) != MP_OKAY) { + return res; + } + } + + /* force the sign of b to positive */ + b->sign = MP_ZPOS; + + return MP_OKAY; +} +#endif + + +/* set to a digit */ +static void +mp_set (mp_int * a, mp_digit b) +{ + mp_zero (a); + a->dp[0] = b & MP_MASK; + a->used = (a->dp[0] != 0) ? 1 : 0; +} + + +#ifndef LTM_NO_NEG_EXP +/* b = a/2 */ +static int +mp_div_2(mp_int * a, mp_int * b) +{ + int x, res, oldused; + + /* copy */ + if (b->alloc < a->used) { + if ((res = mp_grow (b, a->used)) != MP_OKAY) { + return res; + } + } + + oldused = b->used; + b->used = a->used; + { + register mp_digit r, rr, *tmpa, *tmpb; + + /* source alias */ + tmpa = a->dp + b->used - 1; + + /* dest alias */ + tmpb = b->dp + b->used - 1; + + /* carry */ + r = 0; + for (x = b->used - 1; x >= 0; x--) { + /* get the carry for the next iteration */ + rr = *tmpa & 1; + + /* shift the current digit, add in carry and store */ + *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1)); + + /* forward carry to next iteration */ + r = rr; + } + + /* zero excess digits */ + tmpb = b->dp + b->used; + for (x = b->used; x < oldused; x++) { + *tmpb++ = 0; + } + } + b->sign = a->sign; + mp_clamp (b); + return MP_OKAY; +} +#endif /* LTM_NO_NEG_EXP */ + + +/* shift left by a certain bit count */ +static int +mp_mul_2d (mp_int * a, int b, mp_int * c) +{ + mp_digit d; + int res; + + /* copy */ + if (a != c) { + if ((res = mp_copy (a, c)) != MP_OKAY) { + return res; + } + } + + if (c->alloc < (int)(c->used + b/DIGIT_BIT + 1)) { + if ((res = mp_grow (c, c->used + b / DIGIT_BIT + 1)) != MP_OKAY) { + return res; + } + } + + /* shift by as many digits in the bit count */ + if (b >= (int)DIGIT_BIT) { + if ((res = mp_lshd (c, b / DIGIT_BIT)) != MP_OKAY) { + return res; + } + } + + /* shift any bit count < DIGIT_BIT */ + d = (mp_digit) (b % DIGIT_BIT); + if (d != 0) { + register mp_digit *tmpc, shift, mask, r, rr; + register int x; + + /* bitmask for carries */ + mask = (((mp_digit)1) << d) - 1; + + /* shift for msbs */ + shift = DIGIT_BIT - d; + + /* alias */ + tmpc = c->dp; + + /* carry */ + r = 0; + for (x = 0; x < c->used; x++) { + /* get the higher bits of the current word */ + rr = (*tmpc >> shift) & mask; + + /* shift the current word and OR in the carry */ + *tmpc = ((*tmpc << d) | r) & MP_MASK; + ++tmpc; + + /* set the carry to the carry bits of the current word */ + r = rr; + } + + /* set final carry */ + if (r != 0) { + c->dp[(c->used)++] = r; + } + } + mp_clamp (c); + return MP_OKAY; +} + + +#ifdef BN_MP_INIT_MULTI_C +static int +mp_init_multi(mp_int *mp, ...) +{ + mp_err res = MP_OKAY; /* Assume ok until proven otherwise */ + int n = 0; /* Number of ok inits */ + mp_int* cur_arg = mp; + va_list args; + + va_start(args, mp); /* init args to next argument from caller */ + while (cur_arg != NULL) { + if (mp_init(cur_arg) != MP_OKAY) { + /* Oops - error! Back-track and mp_clear what we already + succeeded in init-ing, then return error. + */ + va_list clean_args; + + /* end the current list */ + va_end(args); + + /* now start cleaning up */ + cur_arg = mp; + va_start(clean_args, mp); + while (n--) { + mp_clear(cur_arg); + cur_arg = va_arg(clean_args, mp_int*); + } + va_end(clean_args); + res = MP_MEM; + break; + } + n++; + cur_arg = va_arg(args, mp_int*); + } + va_end(args); + return res; /* Assumed ok, if error flagged above. */ +} +#endif + + +#ifdef BN_MP_CLEAR_MULTI_C +static void +mp_clear_multi(mp_int *mp, ...) +{ + mp_int* next_mp = mp; + va_list args; + va_start(args, mp); + while (next_mp != NULL) { + mp_clear(next_mp); + next_mp = va_arg(args, mp_int*); + } + va_end(args); +} +#endif + + +/* shift left a certain amount of digits */ +static int +mp_lshd (mp_int * a, int b) +{ + int x, res; + + /* if its less than zero return */ + if (b <= 0) { + return MP_OKAY; + } + + /* grow to fit the new digits */ + if (a->alloc < a->used + b) { + if ((res = mp_grow (a, a->used + b)) != MP_OKAY) { + return res; + } + } + + { + register mp_digit *top, *bottom; + + /* increment the used by the shift amount then copy upwards */ + a->used += b; + + /* top */ + top = a->dp + a->used - 1; + + /* base */ + bottom = a->dp + a->used - 1 - b; + + /* much like mp_rshd this is implemented using a sliding window + * except the window goes the otherway around. Copying from + * the bottom to the top. see bn_mp_rshd.c for more info. + */ + for (x = a->used - 1; x >= b; x--) { + *top-- = *bottom--; + } + + /* zero the lower digits */ + top = a->dp; + for (x = 0; x < b; x++) { + *top++ = 0; + } + } + return MP_OKAY; +} + + +/* returns the number of bits in an int */ +static int +mp_count_bits (mp_int * a) +{ + int r; + mp_digit q; + + /* shortcut */ + if (a->used == 0) { + return 0; + } + + /* get number of digits and add that */ + r = (a->used - 1) * DIGIT_BIT; + + /* take the last digit and count the bits in it */ + q = a->dp[a->used - 1]; + while (q > ((mp_digit) 0)) { + ++r; + q >>= ((mp_digit) 1); + } + return r; +} + + +/* calc a value mod 2**b */ +static int +mp_mod_2d (mp_int * a, int b, mp_int * c) +{ + int x, res; + + /* if b is <= 0 then zero the int */ + if (b <= 0) { + mp_zero (c); + return MP_OKAY; + } + + /* if the modulus is larger than the value than return */ + if (b >= (int) (a->used * DIGIT_BIT)) { + res = mp_copy (a, c); + return res; + } + + /* copy */ + if ((res = mp_copy (a, c)) != MP_OKAY) { + return res; + } + + /* zero digits above the last digit of the modulus */ + for (x = (b / DIGIT_BIT) + ((b % DIGIT_BIT) == 0 ? 0 : 1); x < c->used; x++) { + c->dp[x] = 0; + } + /* clear the digit that is not completely outside/inside the modulus */ + c->dp[b / DIGIT_BIT] &= + (mp_digit) ((((mp_digit) 1) << (((mp_digit) b) % DIGIT_BIT)) - ((mp_digit) 1)); + mp_clamp (c); + return MP_OKAY; +} + + +#ifdef BN_MP_DIV_SMALL + +/* slower bit-bang division... also smaller */ +static int +mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + mp_int ta, tb, tq, q; + int res, n, n2; + + /* is divisor zero ? */ + if (mp_iszero (b) == 1) { + return MP_VAL; + } + + /* if a < b then q=0, r = a */ + if (mp_cmp_mag (a, b) == MP_LT) { + if (d != NULL) { + res = mp_copy (a, d); + } else { + res = MP_OKAY; + } + if (c != NULL) { + mp_zero (c); + } + return res; + } + + /* init our temps */ + if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL) != MP_OKAY)) { + return res; + } + + + mp_set(&tq, 1); + n = mp_count_bits(a) - mp_count_bits(b); + if (((res = mp_abs(a, &ta)) != MP_OKAY) || + ((res = mp_abs(b, &tb)) != MP_OKAY) || + ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) || + ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) { + goto LBL_ERR; + } + + while (n-- >= 0) { + if (mp_cmp(&tb, &ta) != MP_GT) { + if (((res = mp_sub(&ta, &tb, &ta)) != MP_OKAY) || + ((res = mp_add(&q, &tq, &q)) != MP_OKAY)) { + goto LBL_ERR; + } + } + if (((res = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) || + ((res = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY)) { + goto LBL_ERR; + } + } + + /* now q == quotient and ta == remainder */ + n = a->sign; + n2 = (a->sign == b->sign ? MP_ZPOS : MP_NEG); + if (c != NULL) { + mp_exch(c, &q); + c->sign = (mp_iszero(c) == MP_YES) ? MP_ZPOS : n2; + } + if (d != NULL) { + mp_exch(d, &ta); + d->sign = (mp_iszero(d) == MP_YES) ? MP_ZPOS : n; + } +LBL_ERR: + mp_clear_multi(&ta, &tb, &tq, &q, NULL); + return res; +} + +#else + +/* integer signed division. + * c*b + d == a [e.g. a/b, c=quotient, d=remainder] + * HAC pp.598 Algorithm 14.20 + * + * Note that the description in HAC is horribly + * incomplete. For example, it doesn't consider + * the case where digits are removed from 'x' in + * the inner loop. It also doesn't consider the + * case that y has fewer than three digits, etc.. + * + * The overall algorithm is as described as + * 14.20 from HAC but fixed to treat these cases. +*/ +static int +mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + mp_int q, x, y, t1, t2; + int res, n, t, i, norm, neg; + + /* is divisor zero ? */ + if (mp_iszero (b) == 1) { + return MP_VAL; + } + + /* if a < b then q=0, r = a */ + if (mp_cmp_mag (a, b) == MP_LT) { + if (d != NULL) { + res = mp_copy (a, d); + } else { + res = MP_OKAY; + } + if (c != NULL) { + mp_zero (c); + } + return res; + } + + if ((res = mp_init_size (&q, a->used + 2)) != MP_OKAY) { + return res; + } + q.used = a->used + 2; + + if ((res = mp_init (&t1)) != MP_OKAY) { + goto LBL_Q; + } + + if ((res = mp_init (&t2)) != MP_OKAY) { + goto LBL_T1; + } + + if ((res = mp_init_copy (&x, a)) != MP_OKAY) { + goto LBL_T2; + } + + if ((res = mp_init_copy (&y, b)) != MP_OKAY) { + goto LBL_X; + } + + /* fix the sign */ + neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + x.sign = y.sign = MP_ZPOS; + + /* normalize both x and y, ensure that y >= b/2, [b == 2**DIGIT_BIT] */ + norm = mp_count_bits(&y) % DIGIT_BIT; + if (norm < (int)(DIGIT_BIT-1)) { + norm = (DIGIT_BIT-1) - norm; + if ((res = mp_mul_2d (&x, norm, &x)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_mul_2d (&y, norm, &y)) != MP_OKAY) { + goto LBL_Y; + } + } else { + norm = 0; + } + + /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */ + n = x.used - 1; + t = y.used - 1; + + /* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */ + if ((res = mp_lshd (&y, n - t)) != MP_OKAY) { /* y = y*b**{n-t} */ + goto LBL_Y; + } + + while (mp_cmp (&x, &y) != MP_LT) { + ++(q.dp[n - t]); + if ((res = mp_sub (&x, &y, &x)) != MP_OKAY) { + goto LBL_Y; + } + } + + /* reset y by shifting it back down */ + mp_rshd (&y, n - t); + + /* step 3. for i from n down to (t + 1) */ + for (i = n; i >= (t + 1); i--) { + if (i > x.used) { + continue; + } + + /* step 3.1 if xi == yt then set q{i-t-1} to b-1, + * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */ + if (x.dp[i] == y.dp[t]) { + q.dp[i - t - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1); + } else { + mp_word tmp; + tmp = ((mp_word) x.dp[i]) << ((mp_word) DIGIT_BIT); + tmp |= ((mp_word) x.dp[i - 1]); + tmp /= ((mp_word) y.dp[t]); + if (tmp > (mp_word) MP_MASK) + tmp = MP_MASK; + q.dp[i - t - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK)); + } + + /* while (q{i-t-1} * (yt * b + y{t-1})) > + xi * b**2 + xi-1 * b + xi-2 + + do q{i-t-1} -= 1; + */ + q.dp[i - t - 1] = (q.dp[i - t - 1] + 1) & MP_MASK; + do { + q.dp[i - t - 1] = (q.dp[i - t - 1] - 1) & MP_MASK; + + /* find left hand */ + mp_zero (&t1); + t1.dp[0] = (t - 1 < 0) ? 0 : y.dp[t - 1]; + t1.dp[1] = y.dp[t]; + t1.used = 2; + if ((res = mp_mul_d (&t1, q.dp[i - t - 1], &t1)) != MP_OKAY) { + goto LBL_Y; + } + + /* find right hand */ + t2.dp[0] = (i - 2 < 0) ? 0 : x.dp[i - 2]; + t2.dp[1] = (i - 1 < 0) ? 0 : x.dp[i - 1]; + t2.dp[2] = x.dp[i]; + t2.used = 3; + } while (mp_cmp_mag(&t1, &t2) == MP_GT); + + /* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */ + if ((res = mp_mul_d (&y, q.dp[i - t - 1], &t1)) != MP_OKAY) { + goto LBL_Y; + } + + if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) { + goto LBL_Y; + } + + if ((res = mp_sub (&x, &t1, &x)) != MP_OKAY) { + goto LBL_Y; + } + + /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */ + if (x.sign == MP_NEG) { + if ((res = mp_copy (&y, &t1)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_add (&x, &t1, &x)) != MP_OKAY) { + goto LBL_Y; + } + + q.dp[i - t - 1] = (q.dp[i - t - 1] - 1UL) & MP_MASK; + } + } + + /* now q is the quotient and x is the remainder + * [which we have to normalize] + */ + + /* get sign before writing to c */ + x.sign = x.used == 0 ? MP_ZPOS : a->sign; + + if (c != NULL) { + mp_clamp (&q); + mp_exch (&q, c); + c->sign = neg; + } + + if (d != NULL) { + mp_div_2d (&x, norm, &x, NULL); + mp_exch (&x, d); + } + + res = MP_OKAY; + +LBL_Y:mp_clear (&y); +LBL_X:mp_clear (&x); +LBL_T2:mp_clear (&t2); +LBL_T1:mp_clear (&t1); +LBL_Q:mp_clear (&q); + return res; +} + +#endif + + +#ifdef MP_LOW_MEM + #define TAB_SIZE 32 +#else + #define TAB_SIZE 256 +#endif + +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; + mp_digit buf; + int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + int (*redux)(mp_int*,mp_int*,mp_int*); + + /* find window size */ + x = mp_count_bits (X); + if (x <= 7) { + winsize = 2; + } else if (x <= 36) { + winsize = 3; + } else if (x <= 140) { + winsize = 4; + } else if (x <= 450) { + winsize = 5; + } else if (x <= 1303) { + winsize = 6; + } else if (x <= 3529) { + winsize = 7; + } else { + winsize = 8; + } + +#ifdef MP_LOW_MEM + if (winsize > 5) { + winsize = 5; + } +#endif + + /* init M array */ + /* init first cell */ + if ((err = mp_init(&M[1])) != MP_OKAY) { + return err; + } + + /* now init the second half of the array */ + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + if ((err = mp_init(&M[x])) != MP_OKAY) { + for (y = 1<<(winsize-1); y < x; y++) { + mp_clear (&M[y]); + } + mp_clear(&M[1]); + return err; + } + } + + /* create mu, used for Barrett reduction */ + if ((err = mp_init (&mu)) != MP_OKAY) { + goto LBL_M; + } + + if (redmode == 0) { + if ((err = mp_reduce_setup (&mu, P)) != MP_OKAY) { + goto LBL_MU; + } + redux = mp_reduce; + } else { + if ((err = mp_reduce_2k_setup_l (P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + redux = mp_reduce_2k_l; + } + + /* create M table + * + * The M table contains powers of the base, + * e.g. M[x] = G**x mod P + * + * The first half of the table is not + * computed though accept for M[0] and M[1] + */ + if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) { + goto LBL_MU; + } + + /* compute the value at M[1<<(winsize-1)] by squaring + * M[1] (winsize-1) times + */ + if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_MU; + } + + for (x = 0; x < (winsize - 1); x++) { + /* square it */ + if ((err = mp_sqr (&M[1 << (winsize - 1)], + &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_MU; + } + + /* reduce modulo P */ + if ((err = redux (&M[1 << (winsize - 1)], P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + } + + /* create upper table, that is M[x] = M[x-1] * M[1] (mod P) + * for x = (2**(winsize - 1) + 1) to (2**winsize - 1) + */ + for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { + if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { + goto LBL_MU; + } + if ((err = redux (&M[x], P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + } + + /* setup result */ + if ((err = mp_init (&res)) != MP_OKAY) { + goto LBL_MU; + } + mp_set (&res, 1); + + /* set initial mode and bit cnt */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = X->used - 1; + bitcpy = 0; + bitbuf = 0; + + for (;;) { + /* grab next digit as required */ + if (--bitcnt == 0) { + /* if digidx == -1 we are out of digits */ + if (digidx == -1) { + break; + } + /* read next digit and reset the bitcnt */ + buf = X->dp[digidx--]; + bitcnt = (int) DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1; + buf <<= (mp_digit)1; + + /* if the bit is zero and mode == 0 then we ignore it + * These represent the leading zero bits before the first 1 bit + * in the exponent. Technically this opt is not required but it + * does lower the # of trivial squaring/reductions used + */ + if (mode == 0 && y == 0) { + continue; + } + + /* if the bit is zero and mode == 1 then we square */ + if (mode == 1 && y == 0) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + continue; + } + + /* else we add it to the window */ + bitbuf |= (y << (winsize - ++bitcpy)); + mode = 2; + + if (bitcpy == winsize) { + /* ok window is filled so square as required and multiply */ + /* square first */ + for (x = 0; x < winsize; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* then multiply */ + if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + + /* empty window and reset */ + bitcpy = 0; + bitbuf = 0; + mode = 1; + } + } + + /* if bits remain then square/multiply */ + if (mode == 2 && bitcpy > 0) { + /* square then multiply if the bit is set */ + for (x = 0; x < bitcpy; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + } + } + } + + mp_exch (&res, Y); + err = MP_OKAY; +LBL_RES:mp_clear (&res); +LBL_MU:mp_clear (&mu); +LBL_M: + mp_clear(&M[1]); + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + mp_clear (&M[x]); + } + return err; +} + + +/* computes b = a*a */ +static int +mp_sqr (mp_int * a, mp_int * b) +{ + int res; + +#ifdef BN_MP_TOOM_SQR_C + /* use Toom-Cook? */ + if (a->used >= TOOM_SQR_CUTOFF) { + res = mp_toom_sqr(a, b); + /* Karatsuba? */ + } else +#endif +#ifdef BN_MP_KARATSUBA_SQR_C +if (a->used >= KARATSUBA_SQR_CUTOFF) { + res = mp_karatsuba_sqr (a, b); + } else +#endif + { +#ifdef BN_FAST_S_MP_SQR_C + /* can we use the fast comba multiplier? */ + if ((a->used * 2 + 1) < MP_WARRAY && + a->used < + (1 << (sizeof(mp_word) * CHAR_BIT - 2*DIGIT_BIT - 1))) { + res = fast_s_mp_sqr (a, b); + } else +#endif +#ifdef BN_S_MP_SQR_C + res = s_mp_sqr (a, b); +#else +#error mp_sqr could fail + res = MP_VAL; +#endif + } + b->sign = MP_ZPOS; + return res; +} + + +/* reduces a modulo n where n is of the form 2**p - d + This differs from reduce_2k since "d" can be larger + than a single digit. +*/ +static int +mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d) +{ + mp_int q; + int p, res; + + if ((res = mp_init(&q)) != MP_OKAY) { + return res; + } + + p = mp_count_bits(n); +top: + /* q = a/2**p, a = a mod 2**p */ + if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { + goto ERR; + } + + /* q = q * d */ + if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { + goto ERR; + } + + /* a = a + q */ + if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { + goto ERR; + } + + if (mp_cmp_mag(a, n) != MP_LT) { + s_mp_sub(a, n, a); + goto top; + } + +ERR: + mp_clear(&q); + return res; +} + + +/* determines the setup value */ +static int +mp_reduce_2k_setup_l(mp_int *a, mp_int *d) +{ + int res; + mp_int tmp; + + if ((res = mp_init(&tmp)) != MP_OKAY) { + return res; + } + + if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) { + goto ERR; + } + + if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) { + goto ERR; + } + +ERR: + mp_clear(&tmp); + return res; +} + + +/* computes a = 2**b + * + * Simple algorithm which zeroes the int, grows it then just sets one bit + * as required. + */ +static int +mp_2expt (mp_int * a, int b) +{ + int res; + + /* zero a as per default */ + mp_zero (a); + + /* grow a to accommodate the single bit */ + if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) { + return res; + } + + /* set the used count of where the bit will go */ + a->used = b / DIGIT_BIT + 1; + + /* put the single bit in its place */ + a->dp[b / DIGIT_BIT] = ((mp_digit)1) << (b % DIGIT_BIT); + + return MP_OKAY; +} + + +/* pre-calculate the value required for Barrett reduction + * For a given modulus "b" it calulates the value required in "a" + */ +static int +mp_reduce_setup (mp_int * a, mp_int * b) +{ + int res; + + if ((res = mp_2expt (a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) { + return res; + } + return mp_div (a, b, a, NULL); +} + + +/* reduces x mod m, assumes 0 < x < m**2, mu is + * precomputed via mp_reduce_setup. + * From HAC pp.604 Algorithm 14.42 + */ +static int +mp_reduce (mp_int * x, mp_int * m, mp_int * mu) +{ + mp_int q; + int res, um = m->used; + + /* q = x */ + if ((res = mp_init_copy (&q, x)) != MP_OKAY) { + return res; + } + + /* q1 = x / b**(k-1) */ + mp_rshd (&q, um - 1); + + /* according to HAC this optimization is ok */ + if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) { + if ((res = mp_mul (&q, mu, &q)) != MP_OKAY) { + goto CLEANUP; + } + } else { +#ifdef BN_S_MP_MUL_HIGH_DIGS_C + if ((res = s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { + goto CLEANUP; + } +#elif defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) + if ((res = fast_s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { + goto CLEANUP; + } +#else + { +#error mp_reduce would always fail + res = MP_VAL; + goto CLEANUP; + } +#endif + } + + /* q3 = q2 / b**(k+1) */ + mp_rshd (&q, um + 1); + + /* x = x mod b**(k+1), quick (no division) */ + if ((res = mp_mod_2d (x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) { + goto CLEANUP; + } + + /* q = q * m mod b**(k+1), quick (no division) */ + if ((res = s_mp_mul_digs (&q, m, &q, um + 1)) != MP_OKAY) { + goto CLEANUP; + } + + /* x = x - q */ + if ((res = mp_sub (x, &q, x)) != MP_OKAY) { + goto CLEANUP; + } + + /* If x < 0, add b**(k+1) to it */ + if (mp_cmp_d (x, 0) == MP_LT) { + mp_set (&q, 1); + if ((res = mp_lshd (&q, um + 1)) != MP_OKAY) { + goto CLEANUP; + } + if ((res = mp_add (x, &q, x)) != MP_OKAY) { + goto CLEANUP; + } + } + + /* Back off if it's too big */ + while (mp_cmp (x, m) != MP_LT) { + if ((res = s_mp_sub (x, m, x)) != MP_OKAY) { + goto CLEANUP; + } + } + +CLEANUP: + mp_clear (&q); + + return res; +} + + +/* multiplies |a| * |b| and only computes up to digs digits of result + * HAC pp. 595, Algorithm 14.12 Modified so you can control how + * many digits of output are created. + */ +static int +s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + mp_int t; + int res, pa, pb, ix, iy; + mp_digit u; + mp_word r; + mp_digit tmpx, *tmpt, *tmpy; + + /* can we use the fast multiplier? */ + if (((digs) < MP_WARRAY) && + MIN (a->used, b->used) < + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + return fast_s_mp_mul_digs (a, b, c, digs); + } + + if ((res = mp_init_size (&t, digs)) != MP_OKAY) { + return res; + } + t.used = digs; + + /* compute the digits of the product directly */ + pa = a->used; + for (ix = 0; ix < pa; ix++) { + /* set the carry to zero */ + u = 0; + + /* limit ourselves to making digs digits of output */ + pb = MIN (b->used, digs - ix); + + /* setup some aliases */ + /* copy of the digit from a used within the nested loop */ + tmpx = a->dp[ix]; + + /* an alias for the destination shifted ix places */ + tmpt = t.dp + ix; + + /* an alias for the digits of b */ + tmpy = b->dp; + + /* compute the columns of the output and propagate the carry */ + for (iy = 0; iy < pb; iy++) { + /* compute the column as a mp_word */ + r = ((mp_word)*tmpt) + + ((mp_word)tmpx) * ((mp_word)*tmpy++) + + ((mp_word) u); + + /* the new column is the lower part of the result */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get the carry word from the result */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + /* set carry if it is placed below digs */ + if (ix + iy < digs) { + *tmpt = u; + } + } + + mp_clamp (&t); + mp_exch (&t, c); + + mp_clear (&t); + return MP_OKAY; +} + + +/* Fast (comba) multiplier + * + * This is the fast column-array [comba] multiplier. It is + * designed to compute the columns of the product first + * then handle the carries afterwards. This has the effect + * of making the nested loops that compute the columns very + * simple and schedulable on super-scalar processors. + * + * This has been modified to produce a variable number of + * digits of output so if say only a half-product is required + * you don't have to compute the upper half (a feature + * required for fast Barrett reduction). + * + * Based on Algorithm 14.12 on pp.595 of HAC. + * + */ +static int +fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + int olduse, res, pa, ix, iz; + mp_digit W[MP_WARRAY]; + register mp_word _W; + + /* grow the destination as required */ + if (c->alloc < digs) { + if ((res = mp_grow (c, digs)) != MP_OKAY) { + return res; + } + } + + /* number of output digits to produce */ + pa = MIN(digs, a->used + b->used); + + /* clear the carry */ + _W = 0; + for (ix = 0; ix < pa; ix++) { + int tx, ty; + int iy; + mp_digit *tmpx, *tmpy; + + /* get offsets into the two bignums */ + ty = MIN(b->used-1, ix); + tx = ix - ty; + + /* setup temp aliases */ + tmpx = a->dp + tx; + tmpy = b->dp + ty; + + /* this is the number of times the loop will iterrate, essentially + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MIN(a->used-tx, ty+1); + + /* execute loop */ + for (iz = 0; iz < iy; ++iz) { + _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); + + } + + /* store term */ + W[ix] = ((mp_digit)_W) & MP_MASK; + + /* make next carry */ + _W = _W >> ((mp_word)DIGIT_BIT); + } + + /* setup dest */ + olduse = c->used; + c->used = pa; + + { + register mp_digit *tmpc; + tmpc = c->dp; + for (ix = 0; ix < pa+1; ix++) { + /* now extract the previous digit [below the carry] */ + *tmpc++ = W[ix]; + } + + /* clear unused digits [that existed in the old copy of c] */ + for (; ix < olduse; ix++) { + *tmpc++ = 0; + } + } + mp_clamp (c); + return MP_OKAY; +} + + +/* init an mp_init for a given size */ +static int +mp_init_size (mp_int * a, int size) +{ + int x; + + /* pad size so there are always extra digits */ + size += (MP_PREC * 2) - (size % MP_PREC); + + /* alloc mem */ + a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * size); + if (a->dp == NULL) { + return MP_MEM; + } + + /* set the members */ + a->used = 0; + a->alloc = size; + a->sign = MP_ZPOS; + + /* zero the digits */ + for (x = 0; x < size; x++) { + a->dp[x] = 0; + } + + return MP_OKAY; +} + + +/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */ +static int +s_mp_sqr (mp_int * a, mp_int * b) +{ + mp_int t; + int res, ix, iy, pa; + mp_word r; + mp_digit u, tmpx, *tmpt; + + pa = a->used; + if ((res = mp_init_size (&t, 2*pa + 1)) != MP_OKAY) { + return res; + } + + /* default used is maximum possible size */ + t.used = 2*pa + 1; + + for (ix = 0; ix < pa; ix++) { + /* first calculate the digit at 2*ix */ + /* calculate double precision result */ + r = ((mp_word) t.dp[2*ix]) + + ((mp_word)a->dp[ix])*((mp_word)a->dp[ix]); + + /* store lower part in result */ + t.dp[ix+ix] = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get the carry */ + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + + /* left hand side of A[ix] * A[iy] */ + tmpx = a->dp[ix]; + + /* alias for where to store the results */ + tmpt = t.dp + (2*ix + 1); + + for (iy = ix + 1; iy < pa; iy++) { + /* first calculate the product */ + r = ((mp_word)tmpx) * ((mp_word)a->dp[iy]); + + /* now calculate the double precision result, note we use + * addition instead of *2 since it's easier to optimize + */ + r = ((mp_word) *tmpt) + r + r + ((mp_word) u); + + /* store lower part */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get carry */ + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + } + /* propagate upwards */ + while (u != ((mp_digit) 0)) { + r = ((mp_word) *tmpt) + ((mp_word) u); + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + } + } + + mp_clamp (&t); + mp_exch (&t, b); + mp_clear (&t); + return MP_OKAY; +} + + +/* multiplies |a| * |b| and does not compute the lower digs digits + * [meant to get the higher part of the product] + */ +static int +s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + mp_int t; + int res, pa, pb, ix, iy; + mp_digit u; + mp_word r; + mp_digit tmpx, *tmpt, *tmpy; + + /* can we use the fast multiplier? */ +#ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C + if (((a->used + b->used + 1) < MP_WARRAY) + && MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + return fast_s_mp_mul_high_digs (a, b, c, digs); + } +#endif + + if ((res = mp_init_size (&t, a->used + b->used + 1)) != MP_OKAY) { + return res; + } + t.used = a->used + b->used + 1; + + pa = a->used; + pb = b->used; + for (ix = 0; ix < pa; ix++) { + /* clear the carry */ + u = 0; + + /* left hand side of A[ix] * B[iy] */ + tmpx = a->dp[ix]; + + /* alias to the address of where the digits will be stored */ + tmpt = &(t.dp[digs]); + + /* alias for where to read the right hand side from */ + tmpy = b->dp + (digs - ix); + + for (iy = digs - ix; iy < pb; iy++) { + /* calculate the double precision result */ + r = ((mp_word)*tmpt) + + ((mp_word)tmpx) * ((mp_word)*tmpy++) + + ((mp_word) u); + + /* get the lower part */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* carry the carry */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + *tmpt = u; + } + mp_clamp (&t); + mp_exch (&t, c); + mp_clear (&t); + return MP_OKAY; +} + + +#ifdef BN_MP_MONTGOMERY_SETUP_C +/* setups the montgomery reduction stuff */ +static int +mp_montgomery_setup (mp_int * n, mp_digit * rho) +{ + mp_digit x, b; + +/* fast inversion mod 2**k + * + * Based on the fact that + * + * XA = 1 (mod 2**n) => (X(2-XA)) A = 1 (mod 2**2n) + * => 2*X*A - X*X*A*A = 1 + * => 2*(1) - (1) = 1 + */ + b = n->dp[0]; + + if ((b & 1) == 0) { + return MP_VAL; + } + + x = (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */ + x *= 2 - b * x; /* here x*a==1 mod 2**8 */ +#if !defined(MP_8BIT) + x *= 2 - b * x; /* here x*a==1 mod 2**16 */ +#endif +#if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT)) + x *= 2 - b * x; /* here x*a==1 mod 2**32 */ +#endif +#ifdef MP_64BIT + x *= 2 - b * x; /* here x*a==1 mod 2**64 */ +#endif + + /* rho = -1/m mod b */ + *rho = (unsigned long)(((mp_word)1 << ((mp_word) DIGIT_BIT)) - x) & MP_MASK; + + return MP_OKAY; +} +#endif + + +#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C +/* computes xR**-1 == x (mod N) via Montgomery Reduction + * + * This is an optimized implementation of montgomery_reduce + * which uses the comba method to quickly calculate the columns of the + * reduction. + * + * Based on Algorithm 14.32 on pp.601 of HAC. +*/ +int +fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) +{ + int ix, res, olduse; + mp_word W[MP_WARRAY]; + + /* get old used count */ + olduse = x->used; + + /* grow a as required */ + if (x->alloc < n->used + 1) { + if ((res = mp_grow (x, n->used + 1)) != MP_OKAY) { + return res; + } + } + + /* first we have to get the digits of the input into + * an array of double precision words W[...] + */ + { + register mp_word *_W; + register mp_digit *tmpx; + + /* alias for the W[] array */ + _W = W; + + /* alias for the digits of x*/ + tmpx = x->dp; + + /* copy the digits of a into W[0..a->used-1] */ + for (ix = 0; ix < x->used; ix++) { + *_W++ = *tmpx++; + } + + /* zero the high words of W[a->used..m->used*2] */ + for (; ix < n->used * 2 + 1; ix++) { + *_W++ = 0; + } + } + + /* now we proceed to zero successive digits + * from the least significant upwards + */ + for (ix = 0; ix < n->used; ix++) { + /* mu = ai * m' mod b + * + * We avoid a double precision multiplication (which isn't required) + * by casting the value down to a mp_digit. Note this requires + * that W[ix-1] have the carry cleared (see after the inner loop) + */ + register mp_digit mu; + mu = (mp_digit) (((W[ix] & MP_MASK) * rho) & MP_MASK); + + /* a = a + mu * m * b**i + * + * This is computed in place and on the fly. The multiplication + * by b**i is handled by offseting which columns the results + * are added to. + * + * Note the comba method normally doesn't handle carries in the + * inner loop In this case we fix the carry from the previous + * column since the Montgomery reduction requires digits of the + * result (so far) [see above] to work. This is + * handled by fixing up one carry after the inner loop. The + * carry fixups are done in order so after these loops the + * first m->used words of W[] have the carries fixed + */ + { + register int iy; + register mp_digit *tmpn; + register mp_word *_W; + + /* alias for the digits of the modulus */ + tmpn = n->dp; + + /* Alias for the columns set by an offset of ix */ + _W = W + ix; + + /* inner loop */ + for (iy = 0; iy < n->used; iy++) { + *_W++ += ((mp_word)mu) * ((mp_word)*tmpn++); + } + } + + /* now fix carry for next digit, W[ix+1] */ + W[ix + 1] += W[ix] >> ((mp_word) DIGIT_BIT); + } + + /* now we have to propagate the carries and + * shift the words downward [all those least + * significant digits we zeroed]. + */ + { + register mp_digit *tmpx; + register mp_word *_W, *_W1; + + /* nox fix rest of carries */ + + /* alias for current word */ + _W1 = W + ix; + + /* alias for next word, where the carry goes */ + _W = W + ++ix; + + for (; ix <= n->used * 2 + 1; ix++) { + *_W++ += *_W1++ >> ((mp_word) DIGIT_BIT); + } + + /* copy out, A = A/b**n + * + * The result is A/b**n but instead of converting from an + * array of mp_word to mp_digit than calling mp_rshd + * we just copy them in the right order + */ + + /* alias for destination word */ + tmpx = x->dp; + + /* alias for shifted double precision result */ + _W = W + n->used; + + for (ix = 0; ix < n->used + 1; ix++) { + *tmpx++ = (mp_digit)(*_W++ & ((mp_word) MP_MASK)); + } + + /* zero oldused digits, if the input a was larger than + * m->used+1 we'll have to clear the digits + */ + for (; ix < olduse; ix++) { + *tmpx++ = 0; + } + } + + /* set the max used and clamp */ + x->used = n->used + 1; + mp_clamp (x); + + /* if A >= m then A = A - m */ + if (mp_cmp_mag (x, n) != MP_LT) { + return s_mp_sub (x, n, x); + } + return MP_OKAY; +} +#endif + + +#ifdef BN_MP_MUL_2_C +/* b = a*2 */ +static int +mp_mul_2(mp_int * a, mp_int * b) +{ + int x, res, oldused; + + /* grow to accommodate result */ + if (b->alloc < a->used + 1) { + if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) { + return res; + } + } + + oldused = b->used; + b->used = a->used; + + { + register mp_digit r, rr, *tmpa, *tmpb; + + /* alias for source */ + tmpa = a->dp; + + /* alias for dest */ + tmpb = b->dp; + + /* carry */ + r = 0; + for (x = 0; x < a->used; x++) { + + /* get what will be the *next* carry bit from the + * MSB of the current digit + */ + rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1)); + + /* now shift up this digit, add in the carry [from the previous] */ + *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK; + + /* copy the carry that would be from the source + * digit into the next iteration + */ + r = rr; + } + + /* new leading digit? */ + if (r != 0) { + /* add a MSB which is always 1 at this point */ + *tmpb = 1; + ++(b->used); + } + + /* now zero any excess digits on the destination + * that we didn't write to + */ + tmpb = b->dp + b->used; + for (x = b->used; x < oldused; x++) { + *tmpb++ = 0; + } + } + b->sign = a->sign; + return MP_OKAY; +} +#endif + + +#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C +/* + * shifts with subtractions when the result is greater than 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 +mp_montgomery_calc_normalization (mp_int * a, mp_int * b) +{ + int x, bits, res; + + /* how many bits of last digit does b use */ + bits = mp_count_bits (b) % DIGIT_BIT; + + if (b->used > 1) { + if ((res = mp_2expt (a, (b->used - 1) * DIGIT_BIT + bits - 1)) != MP_OKAY) { + return res; + } + } else { + mp_set(a, 1); + bits = 1; + } + + + /* now compute C = A * B mod b */ + for (x = bits - 1; x < (int)DIGIT_BIT; x++) { + if ((res = mp_mul_2 (a, a)) != MP_OKAY) { + return res; + } + if (mp_cmp_mag (a, b) != MP_LT) { + if ((res = s_mp_sub (a, b, a)) != MP_OKAY) { + return res; + } + } + } + + return MP_OKAY; +} +#endif + + +#ifdef BN_MP_EXPTMOD_FAST_C +/* computes Y == G**X mod P, HAC pp.616, Algorithm 14.85 + * + * Uses a left-to-right k-ary sliding window to compute the modular exponentiation. + * The value of k changes based on the size of the exponent. + * + * Uses Montgomery or Diminished Radix reduction [whichever appropriate] + */ + +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; + mp_digit buf, mp; + int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + + /* use a pointer to the reduction algorithm. This allows us to use + * one of many reduction algorithms without modding the guts of + * the code with if statements everywhere. + */ + int (*redux)(mp_int*,mp_int*,mp_digit); + + /* find window size */ + x = mp_count_bits (X); + if (x <= 7) { + winsize = 2; + } else if (x <= 36) { + winsize = 3; + } else if (x <= 140) { + winsize = 4; + } else if (x <= 450) { + winsize = 5; + } else if (x <= 1303) { + winsize = 6; + } else if (x <= 3529) { + winsize = 7; + } else { + winsize = 8; + } + +#ifdef MP_LOW_MEM + if (winsize > 5) { + winsize = 5; + } +#endif + + /* init M array */ + /* init first cell */ + if ((err = mp_init(&M[1])) != MP_OKAY) { + return err; + } + + /* now init the second half of the array */ + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + if ((err = mp_init(&M[x])) != MP_OKAY) { + for (y = 1<<(winsize-1); y < x; y++) { + mp_clear (&M[y]); + } + mp_clear(&M[1]); + return err; + } + } + + /* determine and setup reduction code */ + if (redmode == 0) { +#ifdef BN_MP_MONTGOMERY_SETUP_C + /* now setup montgomery */ + if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) { + goto LBL_M; + } +#else + err = MP_VAL; + goto LBL_M; +#endif + + /* automatically pick the comba one if available (saves quite a few calls/ifs) */ +#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C + if (((P->used * 2 + 1) < MP_WARRAY) && + P->used < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + redux = fast_mp_montgomery_reduce; + } else +#endif + { +#ifdef BN_MP_MONTGOMERY_REDUCE_C + /* use slower baseline Montgomery method */ + redux = mp_montgomery_reduce; +#else + err = MP_VAL; + goto LBL_M; +#endif + } + } else if (redmode == 1) { +#if defined(BN_MP_DR_SETUP_C) && defined(BN_MP_DR_REDUCE_C) + /* setup DR reduction for moduli of the form B**k - b */ + mp_dr_setup(P, &mp); + redux = mp_dr_reduce; +#else + err = MP_VAL; + goto LBL_M; +#endif + } else { +#if defined(BN_MP_REDUCE_2K_SETUP_C) && defined(BN_MP_REDUCE_2K_C) + /* setup DR reduction for moduli of the form 2**k - b */ + if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) { + goto LBL_M; + } + redux = mp_reduce_2k; +#else + err = MP_VAL; + goto LBL_M; +#endif + } + + /* setup result */ + if ((err = mp_init (&res)) != MP_OKAY) { + goto LBL_M; + } + + /* create M table + * + + * + * The first half of the table is not computed though accept for M[0] and M[1] + */ + + if (redmode == 0) { +#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C + /* now we need R mod m */ + if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) { + goto LBL_RES; + } +#else + err = MP_VAL; + goto LBL_RES; +#endif + + /* now set M[1] to G * R mod m */ + if ((err = mp_mulmod (G, &res, P, &M[1])) != MP_OKAY) { + goto LBL_RES; + } + } else { + mp_set(&res, 1); + if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) { + goto LBL_RES; + } + } + + /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */ + if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_RES; + } + + for (x = 0; x < (winsize - 1); x++) { + if ((err = mp_sqr (&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&M[1 << (winsize - 1)], P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* create upper table */ + for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { + if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&M[x], P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* set initial mode and bit cnt */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = X->used - 1; + bitcpy = 0; + bitbuf = 0; + + for (;;) { + /* grab next digit as required */ + if (--bitcnt == 0) { + /* if digidx == -1 we are out of digits so break */ + if (digidx == -1) { + break; + } + /* read next digit and reset bitcnt */ + buf = X->dp[digidx--]; + bitcnt = (int)DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1; + buf <<= (mp_digit)1; + + /* if the bit is zero and mode == 0 then we ignore it + * These represent the leading zero bits before the first 1 bit + * in the exponent. Technically this opt is not required but it + * does lower the # of trivial squaring/reductions used + */ + if (mode == 0 && y == 0) { + continue; + } + + /* if the bit is zero and mode == 1 then we square */ + if (mode == 1 && y == 0) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + continue; + } + + /* else we add it to the window */ + bitbuf |= (y << (winsize - ++bitcpy)); + mode = 2; + + if (bitcpy == winsize) { + /* ok window is filled so square as required and multiply */ + /* square first */ + for (x = 0; x < winsize; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* then multiply */ + if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + + /* empty window and reset */ + bitcpy = 0; + bitbuf = 0; + mode = 1; + } + } + + /* if bits remain then square/multiply */ + if (mode == 2 && bitcpy > 0) { + /* square then multiply if the bit is set */ + for (x = 0; x < bitcpy; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + + /* get next bit of the window */ + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + } + } + + if (redmode == 0) { + /* fixup result if Montgomery reduction is used + * recall that any value in a Montgomery system is + * actually multiplied by R mod n. So we have + * to reduce one more time to cancel out the factor + * of R. + */ + if ((err = redux(&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* swap res with Y */ + mp_exch (&res, Y); + err = MP_OKAY; +LBL_RES:mp_clear (&res); +LBL_M: + mp_clear(&M[1]); + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + mp_clear (&M[x]); + } + return err; +} +#endif + + +#ifdef BN_FAST_S_MP_SQR_C +/* the jist of squaring... + * you do like mult except the offset of the tmpx [one that + * starts closer to zero] can't equal the offset of tmpy. + * So basically you set up iy like before then you min it with + * (ty-tx) so that it never happens. You double all those + * you add in the inner loop + +After that loop you do the squares and add them in. +*/ + +static int +fast_s_mp_sqr (mp_int * a, mp_int * b) +{ + int olduse, res, pa, ix, iz; + mp_digit W[MP_WARRAY], *tmpx; + mp_word W1; + + /* grow the destination as required */ + pa = a->used + a->used; + if (b->alloc < pa) { + if ((res = mp_grow (b, pa)) != MP_OKAY) { + return res; + } + } + + /* number of output digits to produce */ + W1 = 0; + for (ix = 0; ix < pa; ix++) { + int tx, ty, iy; + mp_word _W; + mp_digit *tmpy; + + /* clear counter */ + _W = 0; + + /* get offsets into the two bignums */ + ty = MIN(a->used-1, ix); + tx = ix - ty; + + /* setup temp aliases */ + tmpx = a->dp + tx; + tmpy = a->dp + ty; + + /* this is the number of times the loop will iterrate, essentially + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MIN(a->used-tx, ty+1); + + /* now for squaring tx can never equal ty + * we halve the distance since they approach at a rate of 2x + * and we have to round because odd cases need to be executed + */ + iy = MIN(iy, (ty-tx+1)>>1); + + /* execute loop */ + for (iz = 0; iz < iy; iz++) { + _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); + } + + /* double the inner product and add carry */ + _W = _W + _W + W1; + + /* even columns have the square term in them */ + if ((ix&1) == 0) { + _W += ((mp_word)a->dp[ix>>1])*((mp_word)a->dp[ix>>1]); + } + + /* store it */ + W[ix] = (mp_digit)(_W & MP_MASK); + + /* make next carry */ + W1 = _W >> ((mp_word)DIGIT_BIT); + } + + /* setup dest */ + olduse = b->used; + b->used = a->used+a->used; + + { + mp_digit *tmpb; + tmpb = b->dp; + for (ix = 0; ix < pa; ix++) { + *tmpb++ = W[ix] & MP_MASK; + } + + /* clear unused digits [that existed in the old copy of c] */ + for (; ix < olduse; ix++) { + *tmpb++ = 0; + } + } + mp_clamp (b); + return MP_OKAY; +} +#endif + + +#ifdef BN_MP_MUL_D_C +/* multiply by a digit */ +static int +mp_mul_d (mp_int * a, mp_digit b, mp_int * c) +{ + mp_digit u, *tmpa, *tmpc; + mp_word r; + int ix, res, olduse; + + /* make sure c is big enough to hold a*b */ + if (c->alloc < a->used + 1) { + if ((res = mp_grow (c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* get the original destinations used count */ + olduse = c->used; + + /* set the sign */ + c->sign = a->sign; + + /* alias for a->dp [source] */ + tmpa = a->dp; + + /* alias for c->dp [dest] */ + tmpc = c->dp; + + /* zero carry */ + u = 0; + + /* compute columns */ + for (ix = 0; ix < a->used; ix++) { + /* compute product and carry sum for this term */ + r = ((mp_word) u) + ((mp_word)*tmpa++) * ((mp_word)b); + + /* mask off higher bits to get a single digit */ + *tmpc++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* send carry into next iteration */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + + /* store final carry [if any] and increment ix offset */ + *tmpc++ = u; + ++ix; + + /* now zero digits above the top */ + while (ix++ < olduse) { + *tmpc++ = 0; + } + + /* set used count */ + c->used = a->used + 1; + mp_clamp(c); + + return MP_OKAY; +} +#endif diff --git a/components/wpa_supplicant/include/wpa2/tls/pkcs1.h b/components/wpa_supplicant/include/wpa2/tls/pkcs1.h new file mode 100644 index 00000000..ed64defa --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/pkcs1.h @@ -0,0 +1,22 @@ +/* + * PKCS #1 (RSA Encryption) + * Copyright (c) 2006-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PKCS1_H +#define PKCS1_H + +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); +int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen); +int pkcs1_decrypt_public_key(struct crypto_rsa_key *key, + const u8 *crypt, size_t crypt_len, + u8 *plain, size_t *plain_len); + +#endif /* PKCS1_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/pkcs5.h b/components/wpa_supplicant/include/wpa2/tls/pkcs5.h new file mode 100644 index 00000000..20ddadc4 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/pkcs5.h @@ -0,0 +1,16 @@ +/* + * PKCS #5 (Password-based Encryption) + * Copyright (c) 2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PKCS5_H +#define PKCS5_H + +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); + +#endif /* PKCS5_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/pkcs8.h b/components/wpa_supplicant/include/wpa2/tls/pkcs8.h new file mode 100644 index 00000000..bebf840b --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/pkcs8.h @@ -0,0 +1,16 @@ +/* + * PKCS #8 (Private-key information syntax) + * Copyright (c) 2006-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PKCS8_H +#define PKCS8_H + +struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len); +struct crypto_private_key * +pkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd); + +#endif /* PKCS8_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/rsa.h b/components/wpa_supplicant/include/wpa2/tls/rsa.h new file mode 100644 index 00000000..c236a9df --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/rsa.h @@ -0,0 +1,23 @@ +/* + * RSA + * Copyright (c) 2006, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef RSA_H +#define RSA_H + +struct crypto_rsa_key; + +struct crypto_rsa_key * +crypto_rsa_import_public_key(const u8 *buf, size_t len); +struct crypto_rsa_key * +crypto_rsa_import_private_key(const u8 *buf, size_t len); +size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key); +int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen, + struct crypto_rsa_key *key, int use_private); +void crypto_rsa_free(struct crypto_rsa_key *key); + +#endif /* RSA_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/tls.h b/components/wpa_supplicant/include/wpa2/tls/tls.h new file mode 100644 index 00000000..983999b5 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/tls.h @@ -0,0 +1,537 @@ +/* + * SSL/TLS interface definition + * Copyright (c) 2004-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLS_H +#define TLS_H + +struct tls_connection; + +struct tls_keys { + const u8 *master_key; /* TLS master secret */ + size_t master_key_len; + const u8 *client_random; + size_t client_random_len; + const u8 *server_random; + size_t server_random_len; +}; + +enum tls_event { + TLS_CERT_CHAIN_SUCCESS, + TLS_CERT_CHAIN_FAILURE, + TLS_PEER_CERTIFICATE, + TLS_ALERT +}; + +/* + * Note: These are used as identifier with external programs and as such, the + * values must not be changed. + */ +enum tls_fail_reason { + TLS_FAIL_UNSPECIFIED = 0, + TLS_FAIL_UNTRUSTED = 1, + TLS_FAIL_REVOKED = 2, + TLS_FAIL_NOT_YET_VALID = 3, + TLS_FAIL_EXPIRED = 4, + TLS_FAIL_SUBJECT_MISMATCH = 5, + TLS_FAIL_ALTSUBJECT_MISMATCH = 6, + TLS_FAIL_BAD_CERTIFICATE = 7, + TLS_FAIL_SERVER_CHAIN_PROBE = 8 +}; + +union tls_event_data { + struct { + int depth; + const char *subject; + enum tls_fail_reason reason; + const char *reason_txt; + const struct wpabuf *cert; + } cert_fail; + + struct { + int depth; + const char *subject; + const struct wpabuf *cert; + const u8 *hash; + size_t hash_len; + } peer_cert; + + struct { + int is_local; + const char *type; + const char *description; + } alert; +}; + +struct tls_config { + const char *opensc_engine_path; + const char *pkcs11_engine_path; + const char *pkcs11_module_path; + int fips_mode; + int cert_in_cb; + + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data); + void *cb_ctx; +}; + +#define TLS_CONN_ALLOW_SIGN_RSA_MD5 BIT(0) +#define TLS_CONN_DISABLE_TIME_CHECKS BIT(1) +#define TLS_CONN_DISABLE_SESSION_TICKET BIT(2) +#define TLS_CONN_REQUEST_OCSP BIT(3) +#define TLS_CONN_REQUIRE_OCSP BIT(4) + +/** + * struct tls_connection_params - Parameters for TLS connection + * @ca_cert: File or reference name for CA X.509 certificate in PEM or DER + * format + * @ca_cert_blob: ca_cert as inlined data or %NULL if not used + * @ca_cert_blob_len: ca_cert_blob length + * @ca_path: Path to CA certificates (OpenSSL specific) + * @subject_match: String to match in the subject of the peer certificate or + * %NULL to allow all subjects + * @altsubject_match: String to match in the alternative subject of the peer + * certificate or %NULL to allow all alternative subjects + * @client_cert: File or reference name for client X.509 certificate in PEM or + * DER format + * @client_cert_blob: client_cert as inlined data or %NULL if not used + * @client_cert_blob_len: client_cert_blob length + * @private_key: File or reference name for client private key in PEM or DER + * format (traditional format (RSA PRIVATE KEY) or PKCS#8 (PRIVATE KEY) + * @private_key_blob: private_key as inlined data or %NULL if not used + * @private_key_blob_len: private_key_blob length + * @private_key_passwd: Passphrase for decrypted private key, %NULL if no + * passphrase is used. + * @dh_file: File name for DH/DSA data in PEM format, or %NULL if not used + * @dh_blob: dh_file as inlined data or %NULL if not used + * @dh_blob_len: dh_blob length + * @engine: 1 = use engine (e.g., a smartcard) for private key operations + * (this is OpenSSL specific for now) + * @engine_id: engine id string (this is OpenSSL specific for now) + * @ppin: pointer to the pin variable in the configuration + * (this is OpenSSL specific for now) + * @key_id: the private key's id when using engine (this is OpenSSL + * specific for now) + * @cert_id: the certificate's id when using engine + * @ca_cert_id: the CA certificate's id when using engine + * @flags: Parameter options (TLS_CONN_*) + * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response + * or %NULL if OCSP is not enabled + * + * TLS connection parameters to be configured with tls_connection_set_params() + * and tls_global_set_params(). + * + * Certificates and private key can be configured either as a reference name + * (file path or reference to certificate store) or by providing the same data + * as a pointer to the data in memory. Only one option will be used for each + * field. + */ +struct tls_connection_params { + const char *ca_cert; + const u8 *ca_cert_blob; + size_t ca_cert_blob_len; + const char *ca_path; + const char *subject_match; + const char *altsubject_match; + const char *client_cert; + const u8 *client_cert_blob; + size_t client_cert_blob_len; + const char *private_key; + const u8 *private_key_blob; + size_t private_key_blob_len; + const char *private_key_passwd; + const char *dh_file; + const u8 *dh_blob; + size_t dh_blob_len; + + /* OpenSSL specific variables */ + int engine; + const char *engine_id; + const char *pin; + const char *key_id; + const char *cert_id; + const char *ca_cert_id; + + unsigned int flags; + const char *ocsp_stapling_response; +}; + + +/** + * tls_init - Initialize TLS library + * @conf: Configuration data for TLS library + * Returns: Context data to be used as tls_ctx in calls to other functions, + * or %NULL on failure. + * + * Called once during program startup and once for each RSN pre-authentication + * session. In other words, there can be two concurrent TLS contexts. If global + * library initialization is needed (i.e., one that is shared between both + * authentication types), the TLS library wrapper should maintain a reference + * counter and do global initialization only when moving from 0 to 1 reference. + */ +void * tls_init(void); + +/** + * tls_deinit - Deinitialize TLS library + * @tls_ctx: TLS context data from tls_init() + * + * Called once during program shutdown and once for each RSN pre-authentication + * session. If global library deinitialization is needed (i.e., one that is + * shared between both authentication types), the TLS library wrapper should + * maintain a reference counter and do global deinitialization only when moving + * from 1 to 0 references. + */ +void tls_deinit(void *tls_ctx); + +/** + * tls_get_errors - Process pending errors + * @tls_ctx: TLS context data from tls_init() + * Returns: Number of found error, 0 if no errors detected. + * + * Process all pending TLS errors. + */ +int tls_get_errors(void *tls_ctx); + +/** + * tls_connection_init - Initialize a new TLS connection + * @tls_ctx: TLS context data from tls_init() + * Returns: Connection context data, conn for other function calls + */ +struct tls_connection * tls_connection_init(void *tls_ctx); + +/** + * tls_connection_deinit - Free TLS connection data + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Release all resources allocated for TLS connection. + */ +void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_established - Has the TLS connection been completed? + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: 1 if TLS connection has been completed, 0 if not. + */ +int tls_connection_established(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_shutdown - Shutdown TLS connection + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: 0 on success, -1 on failure + * + * Shutdown current TLS connection without releasing all resources. New + * connection can be started by using the same conn without having to call + * tls_connection_init() or setting certificates etc. again. The new + * connection should try to use session resumption. + */ +int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn); + +enum { + TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED = -3, + TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED = -2 +}; + +/** + * tls_connection_set_params - Set TLS connection parameters + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @params: Connection parameters + * Returns: 0 on success, -1 on failure, + * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing + * PKCS#11 engine failure, or + * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the + * PKCS#11 engine private key. + */ +int __must_check +tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params); + +/** + * tls_global_set_params - Set TLS parameters for all TLS connection + * @tls_ctx: TLS context data from tls_init() + * @params: Global TLS parameters + * Returns: 0 on success, -1 on failure, + * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing + * PKCS#11 engine failure, or + * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the + * PKCS#11 engine private key. + */ +int __must_check tls_global_set_params( + void *tls_ctx, const struct tls_connection_params *params); + +/** + * tls_global_set_verify - Set global certificate verification options + * @tls_ctx: TLS context data from tls_init() + * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate, + * 2 = verify CRL for all certificates + * Returns: 0 on success, -1 on failure + */ +int __must_check tls_global_set_verify(void *tls_ctx, int check_crl); + +/** + * tls_connection_set_verify - Set certificate verification options + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @verify_peer: 1 = verify peer certificate + * Returns: 0 on success, -1 on failure + */ +int __must_check tls_connection_set_verify(void *tls_ctx, + struct tls_connection *conn, + int verify_peer); + +/** + * tls_connection_get_keys - Get master key and random data from TLS connection + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @keys: Structure of key/random data (filled on success) + * Returns: 0 on success, -1 on failure + */ +int __must_check tls_connection_get_keys(void *tls_ctx, + struct tls_connection *conn, + struct tls_keys *keys); + +/** + * tls_connection_prf - Use TLS-PRF to derive keying material + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @label: Label (e.g., description of the key) for PRF + * @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 + * + * This function is optional to implement if tls_connection_get_keys() provides + * access to master secret and server/client random values. If these values are + * not exported from the TLS library, tls_connection_prf() is required so that + * further keying material can be derived from the master secret. If not + * implemented, the function will still need to be defined, but it can just + * return -1. Example implementation of this function is in tls_prf_sha1_md5() + * when it is called with seed set to client_random|server_random (or + * server_random|client_random). + */ +int __must_check tls_connection_prf(void *tls_ctx, + struct tls_connection *conn, + const char *label, + int server_random_first, + u8 *out, size_t out_len); + +/** + * tls_connection_handshake - Process TLS handshake (client side) + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Input data from TLS server + * @appl_data: Pointer to application data pointer, or %NULL if dropped + * Returns: Output data, %NULL on failure + * + * The caller is responsible for freeing the returned output data. If the final + * handshake message includes application data, this is decrypted and + * appl_data (if not %NULL) is set to point this data. The caller is + * responsible for freeing appl_data. + * + * This function is used during TLS handshake. The first call is done with + * in_data == %NULL and the library is expected to return ClientHello packet. + * This packet is then send to the server and a response from server is given + * to TLS library by calling this function again with in_data pointing to the + * TLS message from the server. + * + * If the TLS handshake fails, this function may return %NULL. However, if the + * TLS library has a TLS alert to send out, that should be returned as the + * output data. In this case, tls_connection_get_failed() must return failure + * (> 0). + * + * tls_connection_established() should return 1 once the TLS handshake has been + * completed successfully. + */ +struct wpabuf * tls_connection_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data); + +struct wpabuf * tls_connection_handshake2(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data, + int *more_data_needed); + +/** + * tls_connection_server_handshake - Process TLS handshake (server side) + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Input data from TLS peer + * @appl_data: Pointer to application data pointer, or %NULL if dropped + * Returns: Output data, %NULL on failure + * + * The caller is responsible for freeing the returned output data. + */ +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data); + +/** + * tls_connection_encrypt - Encrypt data into TLS tunnel + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Plaintext data to be encrypted + * Returns: Encrypted TLS data or %NULL on failure + * + * This function is used after TLS handshake has been completed successfully to + * send data in the encrypted tunnel. The caller is responsible for freeing the + * returned output data. + */ +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data); + +/** + * tls_connection_decrypt - Decrypt data from TLS tunnel + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Encrypted TLS data + * Returns: Decrypted TLS data or %NULL on failure + * + * This function is used after TLS handshake has been completed successfully to + * receive data from the encrypted tunnel. The caller is responsible for + * freeing the returned output data. + */ +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data); + +struct wpabuf * tls_connection_decrypt2(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + int *more_data_needed); + +/** + * tls_connection_resumed - Was session resumption used + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: 1 if current session used session resumption, 0 if not + */ +int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn); + +enum { + TLS_CIPHER_NONE, + TLS_CIPHER_RC4_SHA /* 0x0005 */, + TLS_CIPHER_AES128_SHA /* 0x002f */, + TLS_CIPHER_RSA_DHE_AES128_SHA /* 0x0031 */, + TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */ +}; + +/** + * tls_connection_set_cipher_list - Configure acceptable cipher suites + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers + * (TLS_CIPHER_*). + * Returns: 0 on success, -1 on failure + */ +int __must_check tls_connection_set_cipher_list(void *tls_ctx, + struct tls_connection *conn, + u8 *ciphers); + +/** + * tls_get_cipher - Get current cipher name + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_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 __must_check tls_get_cipher(void *tls_ctx, struct tls_connection *conn, + char *buf, size_t buflen); + +/** + * tls_connection_enable_workaround - Enable TLS workaround options + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: 0 on success, -1 on failure + * + * This function is used to enable connection-specific workaround options for + * buffer SSL/TLS implementations. + */ +int __must_check tls_connection_enable_workaround(void *tls_ctx, + struct tls_connection *conn); + +/** + * tls_connection_client_hello_ext - Set TLS extension for ClientHello + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_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 __must_check tls_connection_client_hello_ext(void *tls_ctx, + struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len); + +/** + * tls_connection_get_failed - Get connection failure status + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns >0 if connection has failed, 0 if not. + */ +int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_get_read_alerts - Get connection read alert status + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: Number of times a fatal read (remote end reported error) has + * happened during this connection. + */ +int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_get_write_alerts - Get connection write alert status + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: Number of times a fatal write (locally detected error) has happened + * during this connection. + */ +int tls_connection_get_write_alerts(void *tls_ctx, + struct tls_connection *conn); + +/** + * tls_connection_get_keyblock_size - Get TLS key_block size + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: Size of the key_block for the negotiated cipher suite or -1 on + * failure + */ +int tls_connection_get_keyblock_size(void *tls_ctx, + struct tls_connection *conn); + +/** + * tls_capabilities - Get supported TLS capabilities + * @tls_ctx: TLS context data from tls_init() + * Returns: Bit field of supported TLS capabilities (TLS_CAPABILITY_*) + */ +unsigned int tls_capabilities(void *tls_ctx); + +typedef int (*tls_session_ticket_cb) +(void *ctx, const u8 *ticket, size_t len, const u8 *client_random, + const u8 *server_random, u8 *master_secret); + +int __must_check tls_connection_set_session_ticket_cb( + void *tls_ctx, struct tls_connection *conn, + tls_session_ticket_cb cb, void *ctx); + +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); + +#endif /* TLS_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/tlsv1_client.h b/components/wpa_supplicant/include/wpa2/tls/tlsv1_client.h new file mode 100644 index 00000000..8ec85f1a --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/tlsv1_client.h @@ -0,0 +1,54 @@ +/* + * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) + * Copyright (c) 2006-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLSV1_CLIENT_H +#define TLSV1_CLIENT_H + +#include "tlsv1_cred.h" + +struct tlsv1_client; + +int tlsv1_client_global_init(void); +void tlsv1_client_global_deinit(void); +struct tlsv1_client * tlsv1_client_init(void); +void tlsv1_client_deinit(struct tlsv1_client *conn); +int tlsv1_client_established(struct tlsv1_client *conn); +int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, + int server_random_first, u8 *out, size_t out_len); +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); +int tlsv1_client_encrypt(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len); +struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + int *need_more_data); +int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, + size_t buflen); +int tlsv1_client_shutdown(struct tlsv1_client *conn); +int tlsv1_client_resumed(struct tlsv1_client *conn); +int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type, + const u8 *data, size_t data_len); +int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys); +int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn); +int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers); +int tlsv1_client_set_cred(struct tlsv1_client *conn, + struct tlsv1_credentials *cred); +void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled); + +typedef int (*tlsv1_client_session_ticket_cb) +(void *ctx, const u8 *ticket, size_t len, const u8 *client_random, + const u8 *server_random, u8 *master_secret); + +void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn, + tlsv1_client_session_ticket_cb cb, + void *ctx); + +#endif /* TLSV1_CLIENT_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/tlsv1_client_i.h b/components/wpa_supplicant/include/wpa2/tls/tlsv1_client_i.h new file mode 100644 index 00000000..55fdcf8d --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/tlsv1_client_i.h @@ -0,0 +1,84 @@ +/* + * TLSv1 client - internal structures + * Copyright (c) 2006-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLSV1_CLIENT_I_H +#define TLSV1_CLIENT_I_H + +struct tlsv1_client { + enum { + CLIENT_HELLO, SERVER_HELLO, SERVER_CERTIFICATE, + SERVER_KEY_EXCHANGE, SERVER_CERTIFICATE_REQUEST, + SERVER_HELLO_DONE, CLIENT_KEY_EXCHANGE, CHANGE_CIPHER_SPEC, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, ACK_FINISHED, + ESTABLISHED, FAILED + } state; + + struct tlsv1_record_layer rl; + + u8 session_id[TLS_SESSION_ID_MAX_LEN]; + size_t session_id_len; + u8 client_random[TLS_RANDOM_LEN]; + u8 server_random[TLS_RANDOM_LEN]; + u8 master_secret[TLS_MASTER_SECRET_LEN]; + + u8 alert_level; + u8 alert_description; + + unsigned int certificate_requested:1; + unsigned int session_resumed:1; + unsigned int session_ticket_included:1; + unsigned int use_session_ticket:1; + unsigned int disable_time_checks:1; + + struct crypto_public_key *server_rsa_key; + + struct tls_verify_hash verify; + +#define MAX_CIPHER_COUNT 30 + u16 cipher_suites[MAX_CIPHER_COUNT]; + size_t num_cipher_suites; + + u16 prev_cipher_suite; + + u8 *client_hello_ext; + size_t client_hello_ext_len; + + /* The prime modulus used for Diffie-Hellman */ + u8 *dh_p; + size_t dh_p_len; + /* The generator used for Diffie-Hellman */ + u8 *dh_g; + size_t dh_g_len; + /* The server's Diffie-Hellman public value */ + u8 *dh_ys; + size_t dh_ys_len; + + struct tlsv1_credentials *cred; + + tlsv1_client_session_ticket_cb session_ticket_cb; + void *session_ticket_cb_ctx; + + struct wpabuf *partial_input; +}; + + +void tls_alert(struct tlsv1_client *conn, u8 level, u8 description); +void tlsv1_client_free_dh(struct tlsv1_client *conn); +int tls_derive_pre_master_secret(u8 *pre_master_secret); +int tls_derive_keys(struct tlsv1_client *conn, + const u8 *pre_master_secret, size_t pre_master_secret_len); +u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len); +u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level, + u8 description, size_t *out_len); +u8 * tlsv1_client_handshake_write(struct tlsv1_client *conn, size_t *out_len, + int no_appl_data); +int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct, + const u8 *buf, size_t *len, + u8 **out_data, size_t *out_len); + +#endif /* TLSV1_CLIENT_I_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/tlsv1_common.h b/components/wpa_supplicant/include/wpa2/tls/tlsv1_common.h new file mode 100644 index 00000000..f28c0cdc --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/tlsv1_common.h @@ -0,0 +1,261 @@ +/* + * TLSv1 common definitions + * Copyright (c) 2006-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLSV1_COMMON_H +#define TLSV1_COMMON_H + +#include "crypto/crypto.h" + +#define TLS_VERSION_1 0x0301 /* TLSv1 */ +#define TLS_VERSION_1_1 0x0302 /* TLSv1.1 */ +#define TLS_VERSION_1_2 0x0303 /* TLSv1.2 */ +#ifdef CONFIG_TLSV12 +#define TLS_VERSION TLS_VERSION_1_2 +#else /* CONFIG_TLSV12 */ +#ifdef CONFIG_TLSV11 +#define TLS_VERSION TLS_VERSION_1_1 +#else /* CONFIG_TLSV11 */ +#define TLS_VERSION TLS_VERSION_1 +#endif /* CONFIG_TLSV11 */ +#endif /* CONFIG_TLSV12 */ +#define TLS_RANDOM_LEN 32 +#define TLS_PRE_MASTER_SECRET_LEN 48 +#define TLS_MASTER_SECRET_LEN 48 +#define TLS_SESSION_ID_MAX_LEN 32 +#define TLS_VERIFY_DATA_LEN 12 + +/* HandshakeType */ +enum { + TLS_HANDSHAKE_TYPE_HELLO_REQUEST = 0, + TLS_HANDSHAKE_TYPE_CLIENT_HELLO = 1, + TLS_HANDSHAKE_TYPE_SERVER_HELLO = 2, + TLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET = 4 /* RFC 4507 */, + TLS_HANDSHAKE_TYPE_CERTIFICATE = 11, + TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE = 12, + TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST = 13, + TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE = 14, + TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY = 15, + TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE = 16, + TLS_HANDSHAKE_TYPE_FINISHED = 20, + TLS_HANDSHAKE_TYPE_CERTIFICATE_URL = 21 /* RFC 4366 */, + TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS = 22 /* RFC 4366 */ +}; + +/* CipherSuite */ +#define TLS_NULL_WITH_NULL_NULL 0x0000 /* RFC 2246 */ +#define TLS_RSA_WITH_NULL_MD5 0x0001 /* RFC 2246 */ +#define TLS_RSA_WITH_NULL_SHA 0x0002 /* RFC 2246 */ +#define TLS_RSA_EXPORT_WITH_RC4_40_MD5 0x0003 /* RFC 2246 */ +#define TLS_RSA_WITH_RC4_128_MD5 0x0004 /* RFC 2246 */ +#define TLS_RSA_WITH_RC4_128_SHA 0x0005 /* RFC 2246 */ +#define TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 0x0006 /* RFC 2246 */ +#define TLS_RSA_WITH_IDEA_CBC_SHA 0x0007 /* RFC 2246 */ +#define TLS_RSA_EXPORT_WITH_DES40_CBC_SHA 0x0008 /* RFC 2246 */ +#define TLS_RSA_WITH_DES_CBC_SHA 0x0009 /* RFC 2246 */ +#define TLS_RSA_WITH_3DES_EDE_CBC_SHA 0x000A /* RFC 2246 */ +#define TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA 0x000B /* RFC 2246 */ +#define TLS_DH_DSS_WITH_DES_CBC_SHA 0x000C /* RFC 2246 */ +#define TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA 0x000D /* RFC 2246 */ +#define TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA 0x000E /* RFC 2246 */ +#define TLS_DH_RSA_WITH_DES_CBC_SHA 0x000F /* RFC 2246 */ +#define TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA 0x0010 /* RFC 2246 */ +#define TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA 0x0011 /* RFC 2246 */ +#define TLS_DHE_DSS_WITH_DES_CBC_SHA 0x0012 /* RFC 2246 */ +#define TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA 0x0013 /* RFC 2246 */ +#define TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA 0x0014 /* RFC 2246 */ +#define TLS_DHE_RSA_WITH_DES_CBC_SHA 0x0015 /* RFC 2246 */ +#define TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA 0x0016 /* RFC 2246 */ +#define TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 0x0017 /* RFC 2246 */ +#define TLS_DH_anon_WITH_RC4_128_MD5 0x0018 /* RFC 2246 */ +#define TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA 0x0019 /* RFC 2246 */ +#define TLS_DH_anon_WITH_DES_CBC_SHA 0x001A /* RFC 2246 */ +#define TLS_DH_anon_WITH_3DES_EDE_CBC_SHA 0x001B /* RFC 2246 */ +#define TLS_RSA_WITH_AES_128_CBC_SHA 0x002F /* RFC 3268 */ +#define TLS_DH_DSS_WITH_AES_128_CBC_SHA 0x0030 /* RFC 3268 */ +#define TLS_DH_RSA_WITH_AES_128_CBC_SHA 0x0031 /* RFC 3268 */ +#define TLS_DHE_DSS_WITH_AES_128_CBC_SHA 0x0032 /* RFC 3268 */ +#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA 0x0033 /* RFC 3268 */ +#define TLS_DH_anon_WITH_AES_128_CBC_SHA 0x0034 /* RFC 3268 */ +#define TLS_RSA_WITH_AES_256_CBC_SHA 0x0035 /* RFC 3268 */ +#define TLS_DH_DSS_WITH_AES_256_CBC_SHA 0x0036 /* RFC 3268 */ +#define TLS_DH_RSA_WITH_AES_256_CBC_SHA 0x0037 /* RFC 3268 */ +#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA 0x0038 /* RFC 3268 */ +#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA 0x0039 /* RFC 3268 */ +#define TLS_DH_anon_WITH_AES_256_CBC_SHA 0x003A /* RFC 3268 */ +#define TLS_RSA_WITH_NULL_SHA256 0x003B /* RFC 5246 */ +#define TLS_RSA_WITH_AES_128_CBC_SHA256 0x003C /* RFC 5246 */ +#define TLS_RSA_WITH_AES_256_CBC_SHA256 0x003D /* RFC 5246 */ +#define TLS_DH_DSS_WITH_AES_128_CBC_SHA256 0x003E /* RFC 5246 */ +#define TLS_DH_RSA_WITH_AES_128_CBC_SHA256 0x003F /* RFC 5246 */ +#define TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 0x0040 /* RFC 5246 */ +#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 0x0067 /* RFC 5246 */ +#define TLS_DH_DSS_WITH_AES_256_CBC_SHA256 0x0068 /* RFC 5246 */ +#define TLS_DH_RSA_WITH_AES_256_CBC_SHA256 0x0069 /* RFC 5246 */ +#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 0x006A /* RFC 5246 */ +#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 0x006B /* RFC 5246 */ +#define TLS_DH_anon_WITH_AES_128_CBC_SHA256 0x006C /* RFC 5246 */ +#define TLS_DH_anon_WITH_AES_256_CBC_SHA256 0x006D /* RFC 5246 */ + +/* CompressionMethod */ +#define TLS_COMPRESSION_NULL 0 + +/* HashAlgorithm */ +enum { + TLS_HASH_ALG_NONE = 0, + TLS_HASH_ALG_MD5 = 1, + TLS_HASH_ALG_SHA1 = 2, + TLS_HASH_ALG_SHA224 = 3, + TLS_HASH_ALG_SHA256 = 4, + TLS_HASH_ALG_SHA384 = 5, + TLS_HASH_ALG_SHA512 = 6 +}; + +/* SignatureAlgorithm */ +enum { + TLS_SIGN_ALG_ANONYMOUS = 0, + TLS_SIGN_ALG_RSA = 1, + TLS_SIGN_ALG_DSA = 2, + TLS_SIGN_ALG_ECDSA = 3, +}; + +/* AlertLevel */ +#define TLS_ALERT_LEVEL_WARNING 1 +#define TLS_ALERT_LEVEL_FATAL 2 + +/* AlertDescription */ +#define TLS_ALERT_CLOSE_NOTIFY 0 +#define TLS_ALERT_UNEXPECTED_MESSAGE 10 +#define TLS_ALERT_BAD_RECORD_MAC 20 +#define TLS_ALERT_DECRYPTION_FAILED 21 +#define TLS_ALERT_RECORD_OVERFLOW 22 +#define TLS_ALERT_DECOMPRESSION_FAILURE 30 +#define TLS_ALERT_HANDSHAKE_FAILURE 40 +#define TLS_ALERT_BAD_CERTIFICATE 42 +#define TLS_ALERT_UNSUPPORTED_CERTIFICATE 43 +#define TLS_ALERT_CERTIFICATE_REVOKED 44 +#define TLS_ALERT_CERTIFICATE_EXPIRED 45 +#define TLS_ALERT_CERTIFICATE_UNKNOWN 46 +#define TLS_ALERT_ILLEGAL_PARAMETER 47 +#define TLS_ALERT_UNKNOWN_CA 48 +#define TLS_ALERT_ACCESS_DENIED 49 +#define TLS_ALERT_DECODE_ERROR 50 +#define TLS_ALERT_DECRYPT_ERROR 51 +#define TLS_ALERT_EXPORT_RESTRICTION 60 +#define TLS_ALERT_PROTOCOL_VERSION 70 +#define TLS_ALERT_INSUFFICIENT_SECURITY 71 +#define TLS_ALERT_INTERNAL_ERROR 80 +#define TLS_ALERT_USER_CANCELED 90 +#define TLS_ALERT_NO_RENEGOTIATION 100 +#define TLS_ALERT_UNSUPPORTED_EXTENSION 110 /* RFC 4366 */ +#define TLS_ALERT_CERTIFICATE_UNOBTAINABLE 111 /* RFC 4366 */ +#define TLS_ALERT_UNRECOGNIZED_NAME 112 /* RFC 4366 */ +#define TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE 113 /* RFC 4366 */ +#define TLS_ALERT_BAD_CERTIFICATE_HASH_VALUE 114 /* RFC 4366 */ + +/* ChangeCipherSpec */ +enum { + TLS_CHANGE_CIPHER_SPEC = 1 +}; + +/* TLS Extensions */ +#define TLS_EXT_SERVER_NAME 0 /* RFC 4366 */ +#define TLS_EXT_MAX_FRAGMENT_LENGTH 1 /* RFC 4366 */ +#define TLS_EXT_CLIENT_CERTIFICATE_URL 2 /* RFC 4366 */ +#define TLS_EXT_TRUSTED_CA_KEYS 3 /* RFC 4366 */ +#define TLS_EXT_TRUNCATED_HMAC 4 /* RFC 4366 */ +#define TLS_EXT_STATUS_REQUEST 5 /* RFC 4366 */ +#define TLS_EXT_SESSION_TICKET 35 /* RFC 4507 */ + +#define TLS_EXT_PAC_OPAQUE TLS_EXT_SESSION_TICKET /* EAP-FAST terminology */ + + +typedef enum { + TLS_KEY_X_NULL, + TLS_KEY_X_RSA, + TLS_KEY_X_RSA_EXPORT, + TLS_KEY_X_DH_DSS_EXPORT, + TLS_KEY_X_DH_DSS, + TLS_KEY_X_DH_RSA_EXPORT, + TLS_KEY_X_DH_RSA, + TLS_KEY_X_DHE_DSS_EXPORT, + TLS_KEY_X_DHE_DSS, + TLS_KEY_X_DHE_RSA_EXPORT, + TLS_KEY_X_DHE_RSA, + TLS_KEY_X_DH_anon_EXPORT, + TLS_KEY_X_DH_anon +} tls_key_exchange; + +typedef enum { + TLS_CIPHER_NULL, + TLS_CIPHER_RC4_40, + TLS_CIPHER_RC4_128, + TLS_CIPHER_RC2_CBC_40, + TLS_CIPHER_IDEA_CBC, + TLS_CIPHER_DES40_CBC, + TLS_CIPHER_DES_CBC, + TLS_CIPHER_3DES_EDE_CBC, + TLS_CIPHER_AES_128_CBC, + TLS_CIPHER_AES_256_CBC +} tls_cipher; + +typedef enum { + TLS_HASH_NULL, + TLS_HASH_MD5, + TLS_HASH_SHA, + TLS_HASH_SHA256 +} tls_hash; + +struct tls_cipher_suite { + u16 suite; + tls_key_exchange key_exchange; + tls_cipher cipher; + tls_hash hash; +}; + +typedef enum { + TLS_CIPHER_STREAM, + TLS_CIPHER_BLOCK +} tls_cipher_type; + +struct tls_cipher_data { + tls_cipher cipher; + tls_cipher_type type; + size_t key_material; + size_t expanded_key_material; + size_t block_size; /* also iv_size */ + enum crypto_cipher_alg alg; +}; + + +struct tls_verify_hash { + struct crypto_hash *md5_client; + struct crypto_hash *sha1_client; + struct crypto_hash *sha256_client; + struct crypto_hash *md5_server; + struct crypto_hash *sha1_server; + struct crypto_hash *sha256_server; + struct crypto_hash *md5_cert; + struct crypto_hash *sha1_cert; + struct crypto_hash *sha256_cert; +}; + + +const struct tls_cipher_suite * tls_get_cipher_suite(u16 suite); +const struct tls_cipher_data * tls_get_cipher_data(tls_cipher cipher); +int tls_server_key_exchange_allowed(tls_cipher cipher); +int tls_parse_cert(const u8 *buf, size_t len, struct crypto_public_key **pk); +int tls_verify_hash_init(struct tls_verify_hash *verify); +void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf, + size_t len); +void tls_verify_hash_free(struct tls_verify_hash *verify); +int tls_version_ok(u16 ver); +const char * tls_version_str(u16 ver); +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); + +#endif /* TLSV1_COMMON_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/tlsv1_cred.h b/components/wpa_supplicant/include/wpa2/tls/tlsv1_cred.h new file mode 100644 index 00000000..68fbdc92 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/tlsv1_cred.h @@ -0,0 +1,40 @@ +/* + * TLSv1 credentials + * Copyright (c) 2006-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLSV1_CRED_H +#define TLSV1_CRED_H + +struct tlsv1_credentials { + struct x509_certificate *trusted_certs; + struct x509_certificate *cert; + struct crypto_private_key *key; + + /* Diffie-Hellman parameters */ + u8 *dh_p; /* prime */ + size_t dh_p_len; + u8 *dh_g; /* generator */ + size_t dh_g_len; +}; + + +struct tlsv1_credentials * tlsv1_cred_alloc(void); +void tlsv1_cred_free(struct tlsv1_credentials *cred); +int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert, + const u8 *cert_blob, size_t cert_blob_len, + const char *path); +int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert, + const u8 *cert_blob, size_t cert_blob_len); +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); +int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file, + const u8 *dh_blob, size_t dh_blob_len); + +#endif /* TLSV1_CRED_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/tlsv1_record.h b/components/wpa_supplicant/include/wpa2/tls/tlsv1_record.h new file mode 100644 index 00000000..48abcb0d --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/tlsv1_record.h @@ -0,0 +1,71 @@ +/* + * TLSv1 Record Protocol + * Copyright (c) 2006-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLSV1_RECORD_H +#define TLSV1_RECORD_H + +#include "crypto/crypto.h" + +#define TLS_MAX_WRITE_MAC_SECRET_LEN 32 +#define TLS_MAX_WRITE_KEY_LEN 32 +#define TLS_MAX_IV_LEN 16 +#define TLS_MAX_KEY_BLOCK_LEN (2 * (TLS_MAX_WRITE_MAC_SECRET_LEN + \ + TLS_MAX_WRITE_KEY_LEN + TLS_MAX_IV_LEN)) + +#define TLS_SEQ_NUM_LEN 8 +#define TLS_RECORD_HEADER_LEN 5 + +/* ContentType */ +enum { + TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC = 20, + TLS_CONTENT_TYPE_ALERT = 21, + TLS_CONTENT_TYPE_HANDSHAKE = 22, + TLS_CONTENT_TYPE_APPLICATION_DATA = 23 +}; + +struct tlsv1_record_layer { + u16 tls_version; + + u8 write_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN]; + u8 read_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN]; + u8 write_key[TLS_MAX_WRITE_KEY_LEN]; + u8 read_key[TLS_MAX_WRITE_KEY_LEN]; + u8 write_iv[TLS_MAX_IV_LEN]; + u8 read_iv[TLS_MAX_IV_LEN]; + + size_t hash_size; + size_t key_material_len; + size_t iv_size; /* also block_size */ + + enum crypto_hash_alg hash_alg; + enum crypto_cipher_alg cipher_alg; + + u8 write_seq_num[TLS_SEQ_NUM_LEN]; + u8 read_seq_num[TLS_SEQ_NUM_LEN]; + + u16 cipher_suite; + u16 write_cipher_suite; + u16 read_cipher_suite; + + struct crypto_cipher *write_cbc; + struct crypto_cipher *read_cbc; +}; + + +int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl, + u16 cipher_suite); +int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl); +int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl); +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); +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); + +#endif /* TLSV1_RECORD_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/tlsv1_server.h b/components/wpa_supplicant/include/wpa2/tls/tlsv1_server.h new file mode 100644 index 00000000..a18c69e3 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/tlsv1_server.h @@ -0,0 +1,48 @@ +/* + * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) + * Copyright (c) 2006-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLSV1_SERVER_H +#define TLSV1_SERVER_H + +#include "tlsv1_cred.h" + +struct tlsv1_server; + +int tlsv1_server_global_init(void); +void tlsv1_server_global_deinit(void); +struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred); +void tlsv1_server_deinit(struct tlsv1_server *conn); +int tlsv1_server_established(struct tlsv1_server *conn); +int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, + int server_random_first, u8 *out, size_t out_len); +u8 * tlsv1_server_handshake(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, size_t *out_len); +int tlsv1_server_encrypt(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len); +int tlsv1_server_decrypt(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len); +int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf, + size_t buflen); +int tlsv1_server_shutdown(struct tlsv1_server *conn); +int tlsv1_server_resumed(struct tlsv1_server *conn); +int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys); +int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn); +int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers); +int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer); + +typedef int (*tlsv1_server_session_ticket_cb) +(void *ctx, const u8 *ticket, size_t len, const u8 *client_random, + const u8 *server_random, u8 *master_secret); + +void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn, + tlsv1_server_session_ticket_cb cb, + void *ctx); + +#endif /* TLSV1_SERVER_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/tlsv1_server_i.h b/components/wpa_supplicant/include/wpa2/tls/tlsv1_server_i.h new file mode 100644 index 00000000..1f61533a --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/tlsv1_server_i.h @@ -0,0 +1,71 @@ +/* + * TLSv1 server - internal structures + * Copyright (c) 2006-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLSV1_SERVER_I_H +#define TLSV1_SERVER_I_H + +struct tlsv1_server { + enum { + CLIENT_HELLO, SERVER_HELLO, SERVER_CERTIFICATE, + SERVER_KEY_EXCHANGE, SERVER_CERTIFICATE_REQUEST, + SERVER_HELLO_DONE, CLIENT_CERTIFICATE, CLIENT_KEY_EXCHANGE, + CERTIFICATE_VERIFY, CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + ESTABLISHED, FAILED + } state; + + struct tlsv1_record_layer rl; + + u8 session_id[TLS_SESSION_ID_MAX_LEN]; + size_t session_id_len; + u8 client_random[TLS_RANDOM_LEN]; + u8 server_random[TLS_RANDOM_LEN]; + u8 master_secret[TLS_MASTER_SECRET_LEN]; + + u8 alert_level; + u8 alert_description; + + struct crypto_public_key *client_rsa_key; + + struct tls_verify_hash verify; + +#define MAX_CIPHER_COUNT 30 + u16 cipher_suites[MAX_CIPHER_COUNT]; + size_t num_cipher_suites; + + u16 cipher_suite; + + struct tlsv1_credentials *cred; + + int verify_peer; + u16 client_version; + + u8 *session_ticket; + size_t session_ticket_len; + + tlsv1_server_session_ticket_cb session_ticket_cb; + void *session_ticket_cb_ctx; + + int use_session_ticket; + + u8 *dh_secret; + size_t dh_secret_len; +}; + + +void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description); +int tlsv1_server_derive_keys(struct tlsv1_server *conn, + const u8 *pre_master_secret, + size_t pre_master_secret_len); +u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len); +u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level, + u8 description, size_t *out_len); +int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct, + const u8 *buf, size_t *len); + +#endif /* TLSV1_SERVER_I_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/x509v3.h b/components/wpa_supplicant/include/wpa2/tls/x509v3.h new file mode 100644 index 00000000..91a35baf --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/x509v3.h @@ -0,0 +1,123 @@ +/* + * X.509v3 certificate parsing and processing + * Copyright (c) 2006-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef X509V3_H +#define X509V3_H + +#include "asn1.h" + +struct x509_algorithm_identifier { + struct asn1_oid oid; +}; + +struct x509_name_attr { + enum x509_name_attr_type { + X509_NAME_ATTR_NOT_USED, + X509_NAME_ATTR_DC, + X509_NAME_ATTR_CN, + X509_NAME_ATTR_C, + X509_NAME_ATTR_L, + X509_NAME_ATTR_ST, + X509_NAME_ATTR_O, + X509_NAME_ATTR_OU + } type; + char *value; +}; + +#define X509_MAX_NAME_ATTRIBUTES 20 + +struct x509_name { + struct x509_name_attr attr[X509_MAX_NAME_ATTRIBUTES]; + size_t num_attr; + char *email; /* emailAddress */ + + /* from alternative name extension */ + char *alt_email; /* rfc822Name */ + char *dns; /* dNSName */ + char *uri; /* uniformResourceIdentifier */ + u8 *ip; /* iPAddress */ + size_t ip_len; /* IPv4: 4, IPv6: 16 */ + struct asn1_oid rid; /* registeredID */ +}; + +struct x509_certificate { + struct x509_certificate *next; + enum { X509_CERT_V1 = 0, X509_CERT_V2 = 1, X509_CERT_V3 = 2 } version; + unsigned long serial_number; + struct x509_algorithm_identifier signature; + struct x509_name issuer; + struct x509_name subject; + os_time_t not_before; + os_time_t not_after; + struct x509_algorithm_identifier public_key_alg; + u8 *public_key; + size_t public_key_len; + struct x509_algorithm_identifier signature_alg; + u8 *sign_value; + size_t sign_value_len; + + /* Extensions */ + unsigned int extensions_present; +#define X509_EXT_BASIC_CONSTRAINTS (1 << 0) +#define X509_EXT_PATH_LEN_CONSTRAINT (1 << 1) +#define X509_EXT_KEY_USAGE (1 << 2) +#define X509_EXT_SUBJECT_ALT_NAME (1 << 3) +#define X509_EXT_ISSUER_ALT_NAME (1 << 4) + + /* BasicConstraints */ + int ca; /* cA */ + unsigned long path_len_constraint; /* pathLenConstraint */ + + /* KeyUsage */ + unsigned long key_usage; +#define X509_KEY_USAGE_DIGITAL_SIGNATURE (1 << 0) +#define X509_KEY_USAGE_NON_REPUDIATION (1 << 1) +#define X509_KEY_USAGE_KEY_ENCIPHERMENT (1 << 2) +#define X509_KEY_USAGE_DATA_ENCIPHERMENT (1 << 3) +#define X509_KEY_USAGE_KEY_AGREEMENT (1 << 4) +#define X509_KEY_USAGE_KEY_CERT_SIGN (1 << 5) +#define X509_KEY_USAGE_CRL_SIGN (1 << 6) +#define X509_KEY_USAGE_ENCIPHER_ONLY (1 << 7) +#define X509_KEY_USAGE_DECIPHER_ONLY (1 << 8) + + /* + * The DER format certificate follows struct x509_certificate. These + * pointers point to that buffer. + */ + const u8 *cert_start; + size_t cert_len; + const u8 *tbs_cert_start; + size_t tbs_cert_len; +}; + +enum { + X509_VALIDATE_OK, + X509_VALIDATE_BAD_CERTIFICATE, + X509_VALIDATE_UNSUPPORTED_CERTIFICATE, + X509_VALIDATE_CERTIFICATE_REVOKED, + X509_VALIDATE_CERTIFICATE_EXPIRED, + X509_VALIDATE_CERTIFICATE_UNKNOWN, + X509_VALIDATE_UNKNOWN_CA +}; + +void x509_certificate_free(struct x509_certificate *cert); +struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len); +void x509_name_string(struct x509_name *name, char *buf, size_t len); +int x509_name_compare(struct x509_name *a, struct x509_name *b); +void x509_certificate_chain_free(struct x509_certificate *cert); +int x509_certificate_check_signature(struct x509_certificate *issuer, + struct x509_certificate *cert); +int x509_certificate_chain_validate(struct x509_certificate *trusted, + struct x509_certificate *chain, + int *reason, int disable_time_checks); +struct x509_certificate * +x509_certificate_get_subject(struct x509_certificate *chain, + struct x509_name *name); +int x509_certificate_self_signed(struct x509_certificate *cert); + +#endif /* X509V3_H */ diff --git a/components/wpa_supplicant/include/wpa2/utils/base64.h b/components/wpa_supplicant/include/wpa2/utils/base64.h new file mode 100644 index 00000000..aa21fd0f --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/utils/base64.h @@ -0,0 +1,17 @@ +/* + * Base64 encoding/decoding (RFC1341) + * Copyright (c) 2005, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef BASE64_H +#define BASE64_H + +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, + size_t *out_len); + +#endif /* BASE64_H */ diff --git a/components/wpa_supplicant/include/wpa2/utils/ext_password.h b/components/wpa_supplicant/include/wpa2/utils/ext_password.h new file mode 100644 index 00000000..dfe8e6f0 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/utils/ext_password.h @@ -0,0 +1,33 @@ +/* + * External password backend + * Copyright (c) 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EXT_PASSWORD_H +#define EXT_PASSWORD_H + +struct ext_password_data; + +#ifdef CONFIG_EXT_PASSWORD + +struct ext_password_data * ext_password_init(const char *backend, + const char *params); +void ext_password_deinit(struct ext_password_data *data); + +struct wpabuf * ext_password_get(struct ext_password_data *data, + const char *name); +void ext_password_free(struct wpabuf *pw); + +#else /* CONFIG_EXT_PASSWORD */ + +#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 */ + +#endif /* EXT_PASSWORD_H */ diff --git a/components/wpa_supplicant/include/wpa2/utils/ext_password_i.h b/components/wpa_supplicant/include/wpa2/utils/ext_password_i.h new file mode 100644 index 00000000..043e7312 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/utils/ext_password_i.h @@ -0,0 +1,23 @@ +/* + * External password backend - internal definitions + * Copyright (c) 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EXT_PASSWORD_I_H +#define EXT_PASSWORD_I_H + +#include "ext_password.h" + +struct ext_password_backend { + const char *name; + void * (*init)(const char *params); + void (*deinit)(void *ctx); + struct wpabuf * (*get)(void *ctx, const char *name); +}; + +struct wpabuf * ext_password_alloc(size_t len); + +#endif /* EXT_PASSWORD_I_H */ diff --git a/components/wpa_supplicant/src/crypto/crypto_internal-cipher.c b/components/wpa_supplicant/src/crypto/crypto_internal-cipher.c new file mode 100644 index 00000000..7d897957 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/crypto_internal-cipher.c @@ -0,0 +1,268 @@ +/* + * Crypto wrapper for internal crypto implementation - Cipher wrappers + * Copyright (c) 2006-2009, Jouni Malinen + * + * 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/common.h" +#include "crypto/crypto.h" +#include "crypto/aes.h" +#if defined(CONFIG_DES) || defined(CONFIG_DES3) +#include "crypto/des_i.h" +#endif + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + + +struct crypto_cipher { + enum crypto_cipher_alg alg; + union { + struct { + size_t used_bytes; + u8 key[16]; + size_t keylen; + } rc4; + struct { + u8 cbc[32]; + void *ctx_enc; + void *ctx_dec; + } aes; +#ifdef CONFIG_DES3 + struct { + struct des3_key_s key; + u8 cbc[8]; + } des3; +#endif +#ifdef CONFIG_DES + struct { + u32 ek[32]; + u32 dk[32]; + u8 cbc[8]; + } des; +#endif + } u; +}; + + +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len) +{ + struct crypto_cipher *ctx; + + ctx = (struct crypto_cipher *)os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + ctx->alg = alg; + + switch (alg) { + case CRYPTO_CIPHER_ALG_RC4: + if (key_len > sizeof(ctx->u.rc4.key)) { + os_free(ctx); + return NULL; + } + ctx->u.rc4.keylen = key_len; + os_memcpy(ctx->u.rc4.key, key, key_len); + break; + case CRYPTO_CIPHER_ALG_AES: + ctx->u.aes.ctx_enc = aes_encrypt_init(key, key_len); + if (ctx->u.aes.ctx_enc == NULL) { + os_free(ctx); + return NULL; + } + ctx->u.aes.ctx_dec = aes_decrypt_init(key, key_len); + if (ctx->u.aes.ctx_dec == NULL) { + aes_encrypt_deinit(ctx->u.aes.ctx_enc); + os_free(ctx); + return NULL; + } + os_memcpy(ctx->u.aes.cbc, iv, AES_BLOCK_SIZE); + break; +#ifdef CONFIG_DES3 + case CRYPTO_CIPHER_ALG_3DES: + if (key_len != 24) { + os_free(ctx); + return NULL; + } + des3_key_setup(key, &ctx->u.des3.key); + os_memcpy(ctx->u.des3.cbc, iv, 8); + break; +#endif +#ifdef CONFIG_DES + case CRYPTO_CIPHER_ALG_DES: + if (key_len != 8) { + os_free(ctx); + return NULL; + } + des_key_setup(key, ctx->u.des.ek, ctx->u.des.dk); + os_memcpy(ctx->u.des.cbc, iv, 8); + break; +#endif + default: + os_free(ctx); + return NULL; + } + + return ctx; +} + + +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len) +{ + size_t i, j, blocks; + + switch (ctx->alg) { + case CRYPTO_CIPHER_ALG_RC4: + if (plain != crypt) + os_memcpy(crypt, plain, len); + rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen, + ctx->u.rc4.used_bytes, crypt, len); + ctx->u.rc4.used_bytes += len; + break; + case CRYPTO_CIPHER_ALG_AES: + if (len % AES_BLOCK_SIZE) + return -1; + blocks = len / AES_BLOCK_SIZE; + for (i = 0; i < blocks; i++) { + for (j = 0; j < AES_BLOCK_SIZE; j++) + ctx->u.aes.cbc[j] ^= plain[j]; + aes_encrypt(ctx->u.aes.ctx_enc, ctx->u.aes.cbc, + ctx->u.aes.cbc); + os_memcpy(crypt, ctx->u.aes.cbc, AES_BLOCK_SIZE); + plain += AES_BLOCK_SIZE; + crypt += AES_BLOCK_SIZE; + } + break; +#ifdef CONFIG_DES3 + case CRYPTO_CIPHER_ALG_3DES: + if (len % 8) + return -1; + blocks = len / 8; + for (i = 0; i < blocks; i++) { + for (j = 0; j < 8; j++) + ctx->u.des3.cbc[j] ^= plain[j]; + des3_encrypt(ctx->u.des3.cbc, &ctx->u.des3.key, + ctx->u.des3.cbc); + os_memcpy(crypt, ctx->u.des3.cbc, 8); + plain += 8; + crypt += 8; + } + break; +#endif +#ifdef CONFIG_DES + case CRYPTO_CIPHER_ALG_DES: + if (len % 8) + return -1; + blocks = len / 8; + for (i = 0; i < blocks; i++) { + for (j = 0; j < 8; j++) + ctx->u.des3.cbc[j] ^= plain[j]; + des_block_encrypt(ctx->u.des.cbc, ctx->u.des.ek, + ctx->u.des.cbc); + os_memcpy(crypt, ctx->u.des.cbc, 8); + plain += 8; + crypt += 8; + } + break; +#endif + default: + return -1; + } + + return 0; +} + + +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len) +{ + size_t i, j, blocks; + u8 tmp[32]; + + switch (ctx->alg) { + case CRYPTO_CIPHER_ALG_RC4: + if (plain != crypt) + os_memcpy(plain, crypt, len); + rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen, + ctx->u.rc4.used_bytes, plain, len); + ctx->u.rc4.used_bytes += len; + break; + case CRYPTO_CIPHER_ALG_AES: + if (len % AES_BLOCK_SIZE) + return -1; + blocks = len / AES_BLOCK_SIZE; + for (i = 0; i < blocks; i++) { + os_memcpy(tmp, crypt, AES_BLOCK_SIZE); + aes_decrypt(ctx->u.aes.ctx_dec, crypt, plain); + for (j = 0; j < AES_BLOCK_SIZE; j++) + plain[j] ^= ctx->u.aes.cbc[j]; + os_memcpy(ctx->u.aes.cbc, tmp, AES_BLOCK_SIZE); + plain += AES_BLOCK_SIZE; + crypt += AES_BLOCK_SIZE; + } + break; +#ifdef CONFIG_DES3 + case CRYPTO_CIPHER_ALG_3DES: + if (len % 8) + return -1; + blocks = len / 8; + for (i = 0; i < blocks; i++) { + os_memcpy(tmp, crypt, 8); + des3_decrypt(crypt, &ctx->u.des3.key, plain); + for (j = 0; j < 8; j++) + plain[j] ^= ctx->u.des3.cbc[j]; + os_memcpy(ctx->u.des3.cbc, tmp, 8); + plain += 8; + crypt += 8; + } + break; +#endif +#ifdef CONFIG_DES + case CRYPTO_CIPHER_ALG_DES: + if (len % 8) + return -1; + blocks = len / 8; + for (i = 0; i < blocks; i++) { + os_memcpy(tmp, crypt, 8); + des_block_decrypt(crypt, ctx->u.des.dk, plain); + for (j = 0; j < 8; j++) + plain[j] ^= ctx->u.des.cbc[j]; + os_memcpy(ctx->u.des.cbc, tmp, 8); + plain += 8; + crypt += 8; + } + break; +#endif + default: + return -1; + } + + return 0; +} + + +void crypto_cipher_deinit(struct crypto_cipher *ctx) +{ + switch (ctx->alg) { + case CRYPTO_CIPHER_ALG_AES: + aes_encrypt_deinit(ctx->u.aes.ctx_enc); + aes_decrypt_deinit(ctx->u.aes.ctx_dec); + break; +#ifdef CONFIG_DES3 + case CRYPTO_CIPHER_ALG_3DES: + break; +#endif + default: + break; + } + os_free(ctx); +} diff --git a/components/wpa_supplicant/src/crypto/crypto_internal-rsa.c b/components/wpa_supplicant/src/crypto/crypto_internal-rsa.c new file mode 100644 index 00000000..19934f06 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/crypto_internal-rsa.c @@ -0,0 +1,111 @@ +/* + * Crypto wrapper for internal crypto implementation - RSA parts + * Copyright (c) 2006-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "crypto/common.h" +#include "crypto/crypto.h" + +#include "wpa/includes.h" +#include "wpa/common.h" +#include "wpa/wpa_debug.h" + +#include "wpa2/tls/rsa.h" +#include "wpa2/tls/pkcs1.h" +#include "wpa2/tls/pkcs8.h" + +/* Dummy structures; these are just typecast to struct crypto_rsa_key */ +struct crypto_public_key; +struct crypto_private_key; + + +struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len) +{ + return (struct crypto_public_key *) + crypto_rsa_import_public_key(key, len); +} + + +struct crypto_private_key * crypto_private_key_import(const u8 *key, + size_t len, + const char *passwd) +{ + struct crypto_private_key *res; + + /* First, check for possible PKCS #8 encoding */ + res = pkcs8_key_import(key, len); + if (res) + return res; + + if (passwd) { + /* Try to parse as encrypted PKCS #8 */ + res = pkcs8_enc_key_import(key, len, passwd); + if (res) + return res; + } + + /* Not PKCS#8, so try to import PKCS #1 encoded RSA private key */ + wpa_printf(MSG_DEBUG, "Trying to parse PKCS #1 encoded RSA private " + "key"); + return (struct crypto_private_key *) + crypto_rsa_import_private_key(key, len); +} + + +struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf, + size_t len) +{ + /* No X.509 support in crypto_internal.c */ + return NULL; +} + + +int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + return pkcs1_encrypt(2, (struct crypto_rsa_key *) key, + 0, in, inlen, out, outlen); +} + + +int crypto_private_key_decrypt_pkcs1_v15(struct crypto_private_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + return pkcs1_v15_private_key_decrypt((struct crypto_rsa_key *) key, + in, inlen, out, outlen); +} + + +int crypto_private_key_sign_pkcs1(struct crypto_private_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + return pkcs1_encrypt(1, (struct crypto_rsa_key *) key, + 1, in, inlen, out, outlen); +} + + +void crypto_public_key_free(struct crypto_public_key *key) +{ + crypto_rsa_free((struct crypto_rsa_key *) key); +} + + +void crypto_private_key_free(struct crypto_private_key *key) +{ + crypto_rsa_free((struct crypto_rsa_key *) key); +} + + +int crypto_public_key_decrypt_pkcs1(struct crypto_public_key *key, + const u8 *crypt, size_t crypt_len, + u8 *plain, size_t *plain_len) +{ + return pkcs1_decrypt_public_key((struct crypto_rsa_key *) key, + crypt, crypt_len, plain, plain_len); +} diff --git a/components/wpa_supplicant/src/crypto/crypto_internal.c b/components/wpa_supplicant/src/crypto/crypto_internal.c new file mode 100644 index 00000000..5eba12ec --- /dev/null +++ b/components/wpa_supplicant/src/crypto/crypto_internal.c @@ -0,0 +1,282 @@ +/* + * Crypto wrapper for internal crypto implementation + * Copyright (c) 2006-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "crypto/includes.h" +#include "crypto/common.h" +#include "crypto/crypto.h" +#include "crypto/sha1_i.h" +#include "crypto/md5_i.h" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + + +struct crypto_hash { + enum crypto_hash_alg alg; + union { + struct MD5Context md5; +#ifndef CONFIG_ESP_SHA + struct SHA1Context sha1; +#else + SHA1_CTX sha1; +#endif +#ifdef CONFIG_SHA256 + struct sha256_state sha256; +#endif /* CONFIG_SHA256 */ + } u; + u8 key[64]; + size_t key_len; +}; + + +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len) +{ + struct crypto_hash *ctx; + u8 k_pad[64]; + u8 tk[32]; + size_t i; + + ctx = (struct crypto_hash *)os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + ctx->alg = alg; + + switch (alg) { + case CRYPTO_HASH_ALG_MD5: + MD5Init(&ctx->u.md5); + break; + case CRYPTO_HASH_ALG_SHA1: + SHA1Init(&ctx->u.sha1); + break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_SHA256: + sha256_init(&ctx->u.sha256); + break; +#endif /* CONFIG_SHA256 */ + case CRYPTO_HASH_ALG_HMAC_MD5: + if (key_len > sizeof(k_pad)) { + MD5Init(&ctx->u.md5); + MD5Update(&ctx->u.md5, key, key_len); + MD5Final(tk, &ctx->u.md5); + key = tk; + key_len = 16; + } + os_memcpy(ctx->key, key, key_len); + ctx->key_len = key_len; + + os_memcpy(k_pad, key, key_len); + if (key_len < sizeof(k_pad)) + os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x36; + MD5Init(&ctx->u.md5); + MD5Update(&ctx->u.md5, k_pad, sizeof(k_pad)); + break; + case CRYPTO_HASH_ALG_HMAC_SHA1: + if (key_len > sizeof(k_pad)) { + SHA1Init(&ctx->u.sha1); + SHA1Update(&ctx->u.sha1, key, key_len); + SHA1Final(tk, &ctx->u.sha1); + key = tk; + key_len = 20; + } + os_memcpy(ctx->key, key, key_len); + ctx->key_len = key_len; + + os_memcpy(k_pad, key, key_len); + if (key_len < sizeof(k_pad)) + os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x36; + SHA1Init(&ctx->u.sha1); + SHA1Update(&ctx->u.sha1, k_pad, sizeof(k_pad)); + break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_HMAC_SHA256: + if (key_len > sizeof(k_pad)) { + sha256_init(&ctx->u.sha256); + sha256_process(&ctx->u.sha256, key, key_len); + sha256_done(&ctx->u.sha256, tk); + key = tk; + key_len = 32; + } + os_memcpy(ctx->key, key, key_len); + ctx->key_len = key_len; + + os_memcpy(k_pad, key, key_len); + if (key_len < sizeof(k_pad)) + os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x36; + sha256_init(&ctx->u.sha256); + sha256_process(&ctx->u.sha256, k_pad, sizeof(k_pad)); + break; +#endif /* CONFIG_SHA256 */ + default: + os_free(ctx); + return NULL; + } + + return ctx; +} + + +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) +{ + if (ctx == NULL) + return; + + switch (ctx->alg) { + case CRYPTO_HASH_ALG_MD5: + case CRYPTO_HASH_ALG_HMAC_MD5: + MD5Update(&ctx->u.md5, data, len); + break; + case CRYPTO_HASH_ALG_SHA1: + case CRYPTO_HASH_ALG_HMAC_SHA1: + SHA1Update(&ctx->u.sha1, data, len); + break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_SHA256: + case CRYPTO_HASH_ALG_HMAC_SHA256: + sha256_process(&ctx->u.sha256, data, len); + break; +#endif /* CONFIG_SHA256 */ + default: + break; + } +} + + +int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) +{ + u8 k_pad[64]; + size_t i; + + if (ctx == NULL) + return -2; + + if (mac == NULL || len == NULL) { + os_free(ctx); + return 0; + } + + switch (ctx->alg) { + case CRYPTO_HASH_ALG_MD5: + if (*len < 16) { + *len = 16; + os_free(ctx); + return -1; + } + *len = 16; + MD5Final(mac, &ctx->u.md5); + break; + case CRYPTO_HASH_ALG_SHA1: + if (*len < 20) { + *len = 20; + os_free(ctx); + return -1; + } + *len = 20; + SHA1Final(mac, &ctx->u.sha1); + break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_SHA256: + if (*len < 32) { + *len = 32; + os_free(ctx); + return -1; + } + *len = 32; + sha256_done(&ctx->u.sha256, mac); + break; +#endif /* CONFIG_SHA256 */ + case CRYPTO_HASH_ALG_HMAC_MD5: + if (*len < 16) { + *len = 16; + os_free(ctx); + return -1; + } + *len = 16; + + MD5Final(mac, &ctx->u.md5); + + os_memcpy(k_pad, ctx->key, ctx->key_len); + os_memset(k_pad + ctx->key_len, 0, + sizeof(k_pad) - ctx->key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x5c; + MD5Init(&ctx->u.md5); + MD5Update(&ctx->u.md5, k_pad, sizeof(k_pad)); + MD5Update(&ctx->u.md5, mac, 16); + MD5Final(mac, &ctx->u.md5); + break; + case CRYPTO_HASH_ALG_HMAC_SHA1: + if (*len < 20) { + *len = 20; + os_free(ctx); + return -1; + } + *len = 20; + + SHA1Final(mac, &ctx->u.sha1); + + os_memcpy(k_pad, ctx->key, ctx->key_len); + os_memset(k_pad + ctx->key_len, 0, + sizeof(k_pad) - ctx->key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x5c; + SHA1Init(&ctx->u.sha1); + SHA1Update(&ctx->u.sha1, k_pad, sizeof(k_pad)); + SHA1Update(&ctx->u.sha1, mac, 20); + SHA1Final(mac, &ctx->u.sha1); + break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_HMAC_SHA256: + if (*len < 32) { + *len = 32; + os_free(ctx); + return -1; + } + *len = 32; + + sha256_done(&ctx->u.sha256, mac); + + os_memcpy(k_pad, ctx->key, ctx->key_len); + os_memset(k_pad + ctx->key_len, 0, + sizeof(k_pad) - ctx->key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x5c; + sha256_init(&ctx->u.sha256); + sha256_process(&ctx->u.sha256, k_pad, sizeof(k_pad)); + sha256_process(&ctx->u.sha256, mac, 32); + sha256_done(&ctx->u.sha256, mac); + break; +#endif /* CONFIG_SHA256 */ + default: + os_free(ctx); + return -1; + } + + os_free(ctx); + + return 0; +} + + +int crypto_global_init(void) +{ + return 0; +} + + +void crypto_global_deinit(void) +{ +} diff --git a/components/wpa_supplicant/src/crypto/des-internal.c b/components/wpa_supplicant/src/crypto/des-internal.c new file mode 100644 index 00000000..20355629 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/des-internal.c @@ -0,0 +1,493 @@ +/* + * DES and 3DES-EDE ciphers + * + * Modifications to LibTomCrypt implementation: + * Copyright (c) 2006-2009, Jouni Malinen + * + * 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 "des_i.h" + +/* + * This implementation is based on a DES implementation included in + * LibTomCrypt. The version here is modified to fit in wpa_supplicant/hostapd + * coding style. + */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtomcrypt.com + */ + +/** + DES code submitted by Dobes Vandermeer +*/ + +#define ROLc(x, y) \ + ((((unsigned long) (x) << (unsigned long) ((y) & 31)) | \ + (((unsigned long) (x) & 0xFFFFFFFFUL) >> \ + (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL) +#define RORc(x, y) \ + (((((unsigned long) (x) & 0xFFFFFFFFUL) >> \ + (unsigned long) ((y) & 31)) | \ + ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & \ + 0xFFFFFFFFUL) + + +static const u32 bytebit[8] = +{ + 0200, 0100, 040, 020, 010, 04, 02, 01 +}; + +static const u32 bigbyte[24] = +{ + 0x800000UL, 0x400000UL, 0x200000UL, 0x100000UL, + 0x80000UL, 0x40000UL, 0x20000UL, 0x10000UL, + 0x8000UL, 0x4000UL, 0x2000UL, 0x1000UL, + 0x800UL, 0x400UL, 0x200UL, 0x100UL, + 0x80UL, 0x40UL, 0x20UL, 0x10UL, + 0x8UL, 0x4UL, 0x2UL, 0x1L +}; + +/* Use the key schedule specific in the standard (ANSI X3.92-1981) */ + +static const u8 pc1[56] = { + 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, + 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, + 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, + 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 +}; + +static const u8 totrot[16] = { + 1, 2, 4, 6, + 8, 10, 12, 14, + 15, 17, 19, 21, + 23, 25, 27, 28 +}; + +static const u8 pc2[48] = { + 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, + 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, + 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, + 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 +}; + + +static const u32 SP1[64] = +{ + 0x01010400UL, 0x00000000UL, 0x00010000UL, 0x01010404UL, + 0x01010004UL, 0x00010404UL, 0x00000004UL, 0x00010000UL, + 0x00000400UL, 0x01010400UL, 0x01010404UL, 0x00000400UL, + 0x01000404UL, 0x01010004UL, 0x01000000UL, 0x00000004UL, + 0x00000404UL, 0x01000400UL, 0x01000400UL, 0x00010400UL, + 0x00010400UL, 0x01010000UL, 0x01010000UL, 0x01000404UL, + 0x00010004UL, 0x01000004UL, 0x01000004UL, 0x00010004UL, + 0x00000000UL, 0x00000404UL, 0x00010404UL, 0x01000000UL, + 0x00010000UL, 0x01010404UL, 0x00000004UL, 0x01010000UL, + 0x01010400UL, 0x01000000UL, 0x01000000UL, 0x00000400UL, + 0x01010004UL, 0x00010000UL, 0x00010400UL, 0x01000004UL, + 0x00000400UL, 0x00000004UL, 0x01000404UL, 0x00010404UL, + 0x01010404UL, 0x00010004UL, 0x01010000UL, 0x01000404UL, + 0x01000004UL, 0x00000404UL, 0x00010404UL, 0x01010400UL, + 0x00000404UL, 0x01000400UL, 0x01000400UL, 0x00000000UL, + 0x00010004UL, 0x00010400UL, 0x00000000UL, 0x01010004UL +}; + +static const u32 SP2[64] = +{ + 0x80108020UL, 0x80008000UL, 0x00008000UL, 0x00108020UL, + 0x00100000UL, 0x00000020UL, 0x80100020UL, 0x80008020UL, + 0x80000020UL, 0x80108020UL, 0x80108000UL, 0x80000000UL, + 0x80008000UL, 0x00100000UL, 0x00000020UL, 0x80100020UL, + 0x00108000UL, 0x00100020UL, 0x80008020UL, 0x00000000UL, + 0x80000000UL, 0x00008000UL, 0x00108020UL, 0x80100000UL, + 0x00100020UL, 0x80000020UL, 0x00000000UL, 0x00108000UL, + 0x00008020UL, 0x80108000UL, 0x80100000UL, 0x00008020UL, + 0x00000000UL, 0x00108020UL, 0x80100020UL, 0x00100000UL, + 0x80008020UL, 0x80100000UL, 0x80108000UL, 0x00008000UL, + 0x80100000UL, 0x80008000UL, 0x00000020UL, 0x80108020UL, + 0x00108020UL, 0x00000020UL, 0x00008000UL, 0x80000000UL, + 0x00008020UL, 0x80108000UL, 0x00100000UL, 0x80000020UL, + 0x00100020UL, 0x80008020UL, 0x80000020UL, 0x00100020UL, + 0x00108000UL, 0x00000000UL, 0x80008000UL, 0x00008020UL, + 0x80000000UL, 0x80100020UL, 0x80108020UL, 0x00108000UL +}; + +static const u32 SP3[64] = +{ + 0x00000208UL, 0x08020200UL, 0x00000000UL, 0x08020008UL, + 0x08000200UL, 0x00000000UL, 0x00020208UL, 0x08000200UL, + 0x00020008UL, 0x08000008UL, 0x08000008UL, 0x00020000UL, + 0x08020208UL, 0x00020008UL, 0x08020000UL, 0x00000208UL, + 0x08000000UL, 0x00000008UL, 0x08020200UL, 0x00000200UL, + 0x00020200UL, 0x08020000UL, 0x08020008UL, 0x00020208UL, + 0x08000208UL, 0x00020200UL, 0x00020000UL, 0x08000208UL, + 0x00000008UL, 0x08020208UL, 0x00000200UL, 0x08000000UL, + 0x08020200UL, 0x08000000UL, 0x00020008UL, 0x00000208UL, + 0x00020000UL, 0x08020200UL, 0x08000200UL, 0x00000000UL, + 0x00000200UL, 0x00020008UL, 0x08020208UL, 0x08000200UL, + 0x08000008UL, 0x00000200UL, 0x00000000UL, 0x08020008UL, + 0x08000208UL, 0x00020000UL, 0x08000000UL, 0x08020208UL, + 0x00000008UL, 0x00020208UL, 0x00020200UL, 0x08000008UL, + 0x08020000UL, 0x08000208UL, 0x00000208UL, 0x08020000UL, + 0x00020208UL, 0x00000008UL, 0x08020008UL, 0x00020200UL +}; + +static const u32 SP4[64] = +{ + 0x00802001UL, 0x00002081UL, 0x00002081UL, 0x00000080UL, + 0x00802080UL, 0x00800081UL, 0x00800001UL, 0x00002001UL, + 0x00000000UL, 0x00802000UL, 0x00802000UL, 0x00802081UL, + 0x00000081UL, 0x00000000UL, 0x00800080UL, 0x00800001UL, + 0x00000001UL, 0x00002000UL, 0x00800000UL, 0x00802001UL, + 0x00000080UL, 0x00800000UL, 0x00002001UL, 0x00002080UL, + 0x00800081UL, 0x00000001UL, 0x00002080UL, 0x00800080UL, + 0x00002000UL, 0x00802080UL, 0x00802081UL, 0x00000081UL, + 0x00800080UL, 0x00800001UL, 0x00802000UL, 0x00802081UL, + 0x00000081UL, 0x00000000UL, 0x00000000UL, 0x00802000UL, + 0x00002080UL, 0x00800080UL, 0x00800081UL, 0x00000001UL, + 0x00802001UL, 0x00002081UL, 0x00002081UL, 0x00000080UL, + 0x00802081UL, 0x00000081UL, 0x00000001UL, 0x00002000UL, + 0x00800001UL, 0x00002001UL, 0x00802080UL, 0x00800081UL, + 0x00002001UL, 0x00002080UL, 0x00800000UL, 0x00802001UL, + 0x00000080UL, 0x00800000UL, 0x00002000UL, 0x00802080UL +}; + +static const u32 SP5[64] = +{ + 0x00000100UL, 0x02080100UL, 0x02080000UL, 0x42000100UL, + 0x00080000UL, 0x00000100UL, 0x40000000UL, 0x02080000UL, + 0x40080100UL, 0x00080000UL, 0x02000100UL, 0x40080100UL, + 0x42000100UL, 0x42080000UL, 0x00080100UL, 0x40000000UL, + 0x02000000UL, 0x40080000UL, 0x40080000UL, 0x00000000UL, + 0x40000100UL, 0x42080100UL, 0x42080100UL, 0x02000100UL, + 0x42080000UL, 0x40000100UL, 0x00000000UL, 0x42000000UL, + 0x02080100UL, 0x02000000UL, 0x42000000UL, 0x00080100UL, + 0x00080000UL, 0x42000100UL, 0x00000100UL, 0x02000000UL, + 0x40000000UL, 0x02080000UL, 0x42000100UL, 0x40080100UL, + 0x02000100UL, 0x40000000UL, 0x42080000UL, 0x02080100UL, + 0x40080100UL, 0x00000100UL, 0x02000000UL, 0x42080000UL, + 0x42080100UL, 0x00080100UL, 0x42000000UL, 0x42080100UL, + 0x02080000UL, 0x00000000UL, 0x40080000UL, 0x42000000UL, + 0x00080100UL, 0x02000100UL, 0x40000100UL, 0x00080000UL, + 0x00000000UL, 0x40080000UL, 0x02080100UL, 0x40000100UL +}; + +static const u32 SP6[64] = +{ + 0x20000010UL, 0x20400000UL, 0x00004000UL, 0x20404010UL, + 0x20400000UL, 0x00000010UL, 0x20404010UL, 0x00400000UL, + 0x20004000UL, 0x00404010UL, 0x00400000UL, 0x20000010UL, + 0x00400010UL, 0x20004000UL, 0x20000000UL, 0x00004010UL, + 0x00000000UL, 0x00400010UL, 0x20004010UL, 0x00004000UL, + 0x00404000UL, 0x20004010UL, 0x00000010UL, 0x20400010UL, + 0x20400010UL, 0x00000000UL, 0x00404010UL, 0x20404000UL, + 0x00004010UL, 0x00404000UL, 0x20404000UL, 0x20000000UL, + 0x20004000UL, 0x00000010UL, 0x20400010UL, 0x00404000UL, + 0x20404010UL, 0x00400000UL, 0x00004010UL, 0x20000010UL, + 0x00400000UL, 0x20004000UL, 0x20000000UL, 0x00004010UL, + 0x20000010UL, 0x20404010UL, 0x00404000UL, 0x20400000UL, + 0x00404010UL, 0x20404000UL, 0x00000000UL, 0x20400010UL, + 0x00000010UL, 0x00004000UL, 0x20400000UL, 0x00404010UL, + 0x00004000UL, 0x00400010UL, 0x20004010UL, 0x00000000UL, + 0x20404000UL, 0x20000000UL, 0x00400010UL, 0x20004010UL +}; + +static const u32 SP7[64] = +{ + 0x00200000UL, 0x04200002UL, 0x04000802UL, 0x00000000UL, + 0x00000800UL, 0x04000802UL, 0x00200802UL, 0x04200800UL, + 0x04200802UL, 0x00200000UL, 0x00000000UL, 0x04000002UL, + 0x00000002UL, 0x04000000UL, 0x04200002UL, 0x00000802UL, + 0x04000800UL, 0x00200802UL, 0x00200002UL, 0x04000800UL, + 0x04000002UL, 0x04200000UL, 0x04200800UL, 0x00200002UL, + 0x04200000UL, 0x00000800UL, 0x00000802UL, 0x04200802UL, + 0x00200800UL, 0x00000002UL, 0x04000000UL, 0x00200800UL, + 0x04000000UL, 0x00200800UL, 0x00200000UL, 0x04000802UL, + 0x04000802UL, 0x04200002UL, 0x04200002UL, 0x00000002UL, + 0x00200002UL, 0x04000000UL, 0x04000800UL, 0x00200000UL, + 0x04200800UL, 0x00000802UL, 0x00200802UL, 0x04200800UL, + 0x00000802UL, 0x04000002UL, 0x04200802UL, 0x04200000UL, + 0x00200800UL, 0x00000000UL, 0x00000002UL, 0x04200802UL, + 0x00000000UL, 0x00200802UL, 0x04200000UL, 0x00000800UL, + 0x04000002UL, 0x04000800UL, 0x00000800UL, 0x00200002UL +}; + +static const u32 SP8[64] = +{ + 0x10001040UL, 0x00001000UL, 0x00040000UL, 0x10041040UL, + 0x10000000UL, 0x10001040UL, 0x00000040UL, 0x10000000UL, + 0x00040040UL, 0x10040000UL, 0x10041040UL, 0x00041000UL, + 0x10041000UL, 0x00041040UL, 0x00001000UL, 0x00000040UL, + 0x10040000UL, 0x10000040UL, 0x10001000UL, 0x00001040UL, + 0x00041000UL, 0x00040040UL, 0x10040040UL, 0x10041000UL, + 0x00001040UL, 0x00000000UL, 0x00000000UL, 0x10040040UL, + 0x10000040UL, 0x10001000UL, 0x00041040UL, 0x00040000UL, + 0x00041040UL, 0x00040000UL, 0x10041000UL, 0x00001000UL, + 0x00000040UL, 0x10040040UL, 0x00001000UL, 0x00041040UL, + 0x10001000UL, 0x00000040UL, 0x10000040UL, 0x10040000UL, + 0x10040040UL, 0x10000000UL, 0x00040000UL, 0x10001040UL, + 0x00000000UL, 0x10041040UL, 0x00040040UL, 0x10000040UL, + 0x10040000UL, 0x10001000UL, 0x10001040UL, 0x00000000UL, + 0x10041040UL, 0x00041000UL, 0x00041000UL, 0x00001040UL, + 0x00001040UL, 0x00040040UL, 0x10000000UL, 0x10041000UL +}; + +static void cookey(const u32 *raw1, u32 *keyout) +{ + u32 *cook; + const u32 *raw0; + u32 dough[32]; + int i; + + cook = dough; + for (i = 0; i < 16; i++, raw1++) { + raw0 = raw1++; + *cook = (*raw0 & 0x00fc0000L) << 6; + *cook |= (*raw0 & 0x00000fc0L) << 10; + *cook |= (*raw1 & 0x00fc0000L) >> 10; + *cook++ |= (*raw1 & 0x00000fc0L) >> 6; + *cook = (*raw0 & 0x0003f000L) << 12; + *cook |= (*raw0 & 0x0000003fL) << 16; + *cook |= (*raw1 & 0x0003f000L) >> 4; + *cook++ |= (*raw1 & 0x0000003fL); + } + + os_memcpy(keyout, dough, sizeof(dough)); +} + + +static void deskey(const u8 *key, int decrypt, u32 *keyout) +{ + u32 i, j, l, m, n, kn[32]; + u8 pc1m[56], pcr[56]; + + for (j = 0; j < 56; j++) { + l = (u32) pc1[j]; + m = l & 7; + pc1m[j] = (u8) + ((key[l >> 3U] & bytebit[m]) == bytebit[m] ? 1 : 0); + } + + for (i = 0; i < 16; i++) { + if (decrypt) + m = (15 - i) << 1; + else + m = i << 1; + n = m + 1; + kn[m] = kn[n] = 0L; + for (j = 0; j < 28; j++) { + l = j + (u32) totrot[i]; + if (l < 28) + pcr[j] = pc1m[l]; + else + pcr[j] = pc1m[l - 28]; + } + for (/* j = 28 */; j < 56; j++) { + l = j + (u32) totrot[i]; + if (l < 56) + pcr[j] = pc1m[l]; + else + pcr[j] = pc1m[l - 28]; + } + for (j = 0; j < 24; j++) { + if ((int) pcr[(int) pc2[j]] != 0) + kn[m] |= bigbyte[j]; + if ((int) pcr[(int) pc2[j + 24]] != 0) + kn[n] |= bigbyte[j]; + } + } + + cookey(kn, keyout); +} + + +static void desfunc(u32 *block, const u32 *keys) +{ + u32 work, right, leftt; + int cur_round; + + leftt = block[0]; + right = block[1]; + + work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL; + right ^= work; + leftt ^= (work << 4); + + work = ((leftt >> 16) ^ right) & 0x0000ffffL; + right ^= work; + leftt ^= (work << 16); + + work = ((right >> 2) ^ leftt) & 0x33333333L; + leftt ^= work; + right ^= (work << 2); + + work = ((right >> 8) ^ leftt) & 0x00ff00ffL; + leftt ^= work; + right ^= (work << 8); + + right = ROLc(right, 1); + work = (leftt ^ right) & 0xaaaaaaaaL; + + leftt ^= work; + right ^= work; + leftt = ROLc(leftt, 1); + + for (cur_round = 0; cur_round < 8; cur_round++) { + work = RORc(right, 4) ^ *keys++; + leftt ^= SP7[work & 0x3fL] + ^ SP5[(work >> 8) & 0x3fL] + ^ SP3[(work >> 16) & 0x3fL] + ^ SP1[(work >> 24) & 0x3fL]; + work = right ^ *keys++; + leftt ^= SP8[ work & 0x3fL] + ^ SP6[(work >> 8) & 0x3fL] + ^ SP4[(work >> 16) & 0x3fL] + ^ SP2[(work >> 24) & 0x3fL]; + + work = RORc(leftt, 4) ^ *keys++; + right ^= SP7[ work & 0x3fL] + ^ SP5[(work >> 8) & 0x3fL] + ^ SP3[(work >> 16) & 0x3fL] + ^ SP1[(work >> 24) & 0x3fL]; + work = leftt ^ *keys++; + right ^= SP8[ work & 0x3fL] + ^ SP6[(work >> 8) & 0x3fL] + ^ SP4[(work >> 16) & 0x3fL] + ^ SP2[(work >> 24) & 0x3fL]; + } + + right = RORc(right, 1); + work = (leftt ^ right) & 0xaaaaaaaaL; + leftt ^= work; + right ^= work; + leftt = RORc(leftt, 1); + work = ((leftt >> 8) ^ right) & 0x00ff00ffL; + right ^= work; + leftt ^= (work << 8); + /* -- */ + work = ((leftt >> 2) ^ right) & 0x33333333L; + right ^= work; + leftt ^= (work << 2); + work = ((right >> 16) ^ leftt) & 0x0000ffffL; + leftt ^= work; + right ^= (work << 16); + work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL; + leftt ^= work; + right ^= (work << 4); + + block[0] = right; + block[1] = leftt; +} + + +/* wpa_supplicant/hostapd specific wrapper */ + +void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ + u8 pkey[8], next, tmp; + int i; + u32 ek[32], work[2]; + + /* Add parity bits to the key */ + next = 0; + for (i = 0; i < 7; i++) { + tmp = key[i]; + pkey[i] = (tmp >> i) | next | 1; + next = tmp << (7 - i); + } + pkey[i] = next | 1; + + deskey(pkey, 0, ek); + + work[0] = WPA_GET_BE32(clear); + work[1] = WPA_GET_BE32(clear + 4); + desfunc(work, ek); + WPA_PUT_BE32(cypher, work[0]); + WPA_PUT_BE32(cypher + 4, work[1]); + + os_memset(pkey, 0, sizeof(pkey)); + os_memset(ek, 0, sizeof(ek)); +} + +/* +void des_key_setup(const u8 *key, u32 *ek, u32 *dk) +{ + deskey(key, 0, ek); + deskey(key, 1, dk); +} + + +void des_block_encrypt(const u8 *plain, const u32 *ek, u8 *crypt) +{ + u32 work[2]; + work[0] = WPA_GET_BE32(plain); + work[1] = WPA_GET_BE32(plain + 4); + desfunc(work, ek); + WPA_PUT_BE32(crypt, work[0]); + WPA_PUT_BE32(crypt + 4, work[1]); +} + + +void des_block_decrypt(const u8 *crypt, const u32 *dk, u8 *plain) +{ + u32 work[2]; + work[0] = WPA_GET_BE32(crypt); + work[1] = WPA_GET_BE32(crypt + 4); + desfunc(work, dk); + WPA_PUT_BE32(plain, work[0]); + WPA_PUT_BE32(plain + 4, work[1]); +} + + +void des3_key_setup(const u8 *key, struct des3_key_s *dkey) +{ + deskey(key, 0, dkey->ek[0]); + deskey(key + 8, 1, dkey->ek[1]); + deskey(key + 16, 0, dkey->ek[2]); + + deskey(key, 1, dkey->dk[2]); + deskey(key + 8, 0, dkey->dk[1]); + deskey(key + 16, 1, dkey->dk[0]); +} + + +void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt) +{ + u32 work[2]; + + work[0] = WPA_GET_BE32(plain); + work[1] = WPA_GET_BE32(plain + 4); + desfunc(work, key->ek[0]); + desfunc(work, key->ek[1]); + desfunc(work, key->ek[2]); + WPA_PUT_BE32(crypt, work[0]); + WPA_PUT_BE32(crypt + 4, work[1]); +} + + +void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain) +{ + u32 work[2]; + + work[0] = WPA_GET_BE32(crypt); + work[1] = WPA_GET_BE32(crypt + 4); + desfunc(work, key->dk[0]); + desfunc(work, key->dk[1]); + desfunc(work, key->dk[2]); + WPA_PUT_BE32(plain, work[0]); + WPA_PUT_BE32(plain + 4, work[1]); +}*/ + diff --git a/components/wpa_supplicant/src/crypto/dh_groups.c b/components/wpa_supplicant/src/crypto/dh_groups.c index c2e167b7..105b903e 100644 --- a/components/wpa_supplicant/src/crypto/dh_groups.c +++ b/components/wpa_supplicant/src/crypto/dh_groups.c @@ -13,13 +13,14 @@ */ #include "crypto/includes.h" - #include "crypto/common.h" #include "crypto/crypto.h" #include "crypto/random.h" #include "crypto/dh_groups.h" + #include "wpa/wpabuf.h" #include "wpa/wpa_debug.h" + #include "esp_wifi_crypto_types.h" extern wps_crypto_funcs_t wps_crypto_funcs; diff --git a/components/wpa_supplicant/src/crypto/md4-internal.c b/components/wpa_supplicant/src/crypto/md4-internal.c new file mode 100644 index 00000000..a3ad5812 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/md4-internal.c @@ -0,0 +1,201 @@ +/* + * MD4 hash implementation + * + * This software may be distributed under the terms of BSD license. + */ + +#include "crypto/includes.h" +#include "crypto/common.h" +#include "crypto/crypto.h" + +#define MD4_BLOCK_LENGTH 64 +#define MD4_DIGEST_LENGTH 16 + +typedef struct MD4Context { + u32 state[4]; + u64 count; + u8 buffer[MD4_BLOCK_LENGTH]; +} MD4_CTX; + +static void MD4Init(MD4_CTX *ctx); +static void MD4Update(MD4_CTX *ctx, const unsigned char *input, size_t len); +static void MD4Final(unsigned char digest[MD4_DIGEST_LENGTH], MD4_CTX *ctx); + +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + MD4_CTX ctx; + size_t i; + + MD4Init(&ctx); + for (i = 0; i < num_elem; i++) + MD4Update(&ctx, addr[i], len[i]); + MD4Final(mac, &ctx); + return 0; +} + +#define MD4_DIGEST_STRING_LENGTH (MD4_DIGEST_LENGTH * 2 + 1) + +static void MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH]); + +#define PUT_64BIT_LE(cp, value) do { \ + (cp)[7] = (value) >> 56; \ + (cp)[6] = (value) >> 48; \ + (cp)[5] = (value) >> 40; \ + (cp)[4] = (value) >> 32; \ + (cp)[3] = (value) >> 24; \ + (cp)[2] = (value) >> 16; \ + (cp)[1] = (value) >> 8; \ + (cp)[0] = (value); } while (0) + +#define PUT_32BIT_LE(cp, value) do { \ + (cp)[3] = (value) >> 24; \ + (cp)[2] = (value) >> 16; \ + (cp)[1] = (value) >> 8; \ + (cp)[0] = (value); } while (0) + +static u8 PADDING[MD4_BLOCK_LENGTH] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static void MD4Init(MD4_CTX *ctx) +{ + ctx->count = 0; + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xefcdab89; + ctx->state[2] = 0x98badcfe; + ctx->state[3] = 0x10325476; +} + +static void MD4Update(MD4_CTX *ctx, const unsigned char *input, size_t len) +{ + size_t have, need; + + have = (size_t)((ctx->count >> 3) & (MD4_BLOCK_LENGTH - 1)); + need = MD4_BLOCK_LENGTH - have; + + ctx->count += (u64)len << 3; + + if (len >= need) { + if (have != 0) { + os_memcpy(ctx->buffer + have, input, need); + MD4Transform(ctx->state, ctx->buffer); + input += need; + len -= need; + have = 0; + } + + while (len >= MD4_BLOCK_LENGTH) { + MD4Transform(ctx->state, input); + input += MD4_BLOCK_LENGTH; + len -= MD4_BLOCK_LENGTH; + } + } + + if (len != 0) + os_memcpy(ctx->buffer + have, input, len); +} + +static void MD4Pad(MD4_CTX *ctx) +{ + u8 count[8]; + size_t padlen; + + PUT_64BIT_LE(count, ctx->count); + + padlen = MD4_BLOCK_LENGTH - + ((ctx->count >> 3) & (MD4_BLOCK_LENGTH - 1)); + if (padlen < 1 + 8) + padlen += MD4_BLOCK_LENGTH; + MD4Update(ctx, PADDING, padlen - 8); + MD4Update(ctx, count, 8); +} + +static void MD4Final(unsigned char digest[MD4_DIGEST_LENGTH], MD4_CTX *ctx) +{ + int i; + + MD4Pad(ctx); + if (digest != NULL) { + for (i = 0; i < 4; i ++) + PUT_32BIT_LE(digest + i * 4, ctx->state[i]); + os_memset(ctx, 0, sizeof(*ctx)); + } +} + +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) ((x & y) | (x & z) | (y & z)) +#define F3(x, y, z) (x ^ y ^ z) + +#define MD4SETP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s) ) + +static void MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH]) +{ + u32 a, b, c, d, in[MD4_BLOCK_LENGTH / 4]; + + os_memcpy(in, block, sizeof(in)); + + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + + MD4SETP(F1, a, b, c, d, in[ 0], 3); + MD4SETP(F1, d, a, b, c, in[ 1], 7); + MD4SETP(F1, c, d, a, b, in[ 2], 11); + MD4SETP(F1, b, c, d, a, in[ 3], 19); + MD4SETP(F1, a, b, c, d, in[ 4], 3); + MD4SETP(F1, d, a, b, c, in[ 5], 7); + MD4SETP(F1, c, d, a, b, in[ 6], 11); + MD4SETP(F1, b, c, d, a, in[ 7], 19); + MD4SETP(F1, a, b, c, d, in[ 8], 3); + MD4SETP(F1, d, a, b, c, in[ 9], 7); + MD4SETP(F1, c, d, a, b, in[10], 11); + MD4SETP(F1, b, c, d, a, in[11], 19); + MD4SETP(F1, a, b, c, d, in[12], 3); + MD4SETP(F1, d, a, b, c, in[13], 7); + MD4SETP(F1, c, d, a, b, in[14], 11); + MD4SETP(F1, b, c, d, a, in[15], 19); + + MD4SETP(F2, a, b, c, d, in[ 0] + 0x5a827999, 3); + MD4SETP(F2, d, a, b, c, in[ 4] + 0x5a827999, 5); + MD4SETP(F2, c, d, a, b, in[ 8] + 0x5a827999, 9); + MD4SETP(F2, b, c, d, a, in[12] + 0x5a827999, 13); + MD4SETP(F2, a, b, c, d, in[ 1] + 0x5a827999, 3); + MD4SETP(F2, d, a, b, c, in[ 5] + 0x5a827999, 5); + MD4SETP(F2, c, d, a, b, in[ 9] + 0x5a827999, 9); + MD4SETP(F2, b, c, d, a, in[13] + 0x5a827999, 13); + MD4SETP(F2, a, b, c, d, in[ 2] + 0x5a827999, 3); + MD4SETP(F2, d, a, b, c, in[ 6] + 0x5a827999, 5); + MD4SETP(F2, c, d, a, b, in[10] + 0x5a827999, 9); + MD4SETP(F2, b, c, d, a, in[14] + 0x5a827999, 13); + MD4SETP(F2, a, b, c, d, in[ 3] + 0x5a827999, 3); + MD4SETP(F2, d, a, b, c, in[ 7] + 0x5a827999, 5); + MD4SETP(F2, c, d, a, b, in[11] + 0x5a827999, 9); + MD4SETP(F2, b, c, d, a, in[15] + 0x5a827999, 13); + + MD4SETP(F3, a, b, c, d, in[ 0] + 0x6ed9eba1, 3); + MD4SETP(F3, d, a, b, c, in[ 8] + 0x6ed9eba1, 9); + MD4SETP(F3, c, d, a, b, in[ 4] + 0x6ed9eba1, 11); + MD4SETP(F3, b, c, d, a, in[12] + 0x6ed9eba1, 15); + MD4SETP(F3, a, b, c, d, in[ 2] + 0x6ed9eba1, 3); + MD4SETP(F3, d, a, b, c, in[10] + 0x6ed9eba1, 9); + MD4SETP(F3, c, d, a, b, in[ 6] + 0x6ed9eba1, 11); + MD4SETP(F3, b, c, d, a, in[14] + 0x6ed9eba1, 15); + MD4SETP(F3, a, b, c, d, in[ 1] + 0x6ed9eba1, 3); + MD4SETP(F3, d, a, b, c, in[ 9] + 0x6ed9eba1, 9); + MD4SETP(F3, c, d, a, b, in[ 5] + 0x6ed9eba1, 11); + MD4SETP(F3, b, c, d, a, in[13] + 0x6ed9eba1, 15); + MD4SETP(F3, a, b, c, d, in[ 3] + 0x6ed9eba1, 3); + MD4SETP(F3, d, a, b, c, in[11] + 0x6ed9eba1, 9); + MD4SETP(F3, c, d, a, b, in[ 7] + 0x6ed9eba1, 11); + MD4SETP(F3, b, c, d, a, in[15] + 0x6ed9eba1, 15); + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; +} diff --git a/components/wpa_supplicant/src/crypto/ms_funcs.c b/components/wpa_supplicant/src/crypto/ms_funcs.c new file mode 100644 index 00000000..cb2488fc --- /dev/null +++ b/components/wpa_supplicant/src/crypto/ms_funcs.c @@ -0,0 +1,528 @@ +/* + * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 + * Copyright (c) 2004-2012, Jouni Malinen + * + * 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/ms_funcs.h" +#include "crypto/crypto.h" + +/** + * utf8_to_ucs2 - Convert UTF-8 string to UCS-2 encoding + * @utf8_string: UTF-8 string (IN) + * @utf8_string_len: Length of utf8_string (IN) + * @ucs2_buffer: UCS-2 buffer (OUT) + * @ucs2_buffer_size: Length of UCS-2 buffer (IN) + * @ucs2_string_size: Number of 2-byte words in the resulting UCS-2 string + * Returns: 0 on success, -1 on failure + */ +static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len, + u8 *ucs2_buffer, size_t ucs2_buffer_size, + size_t *ucs2_string_size) +{ + size_t i, j; + + for (i = 0, j = 0; i < utf8_string_len; i++) { + u8 c = utf8_string[i]; + if (j >= ucs2_buffer_size) { + /* input too long */ + return -1; + } + if (c <= 0x7F) { + WPA_PUT_LE16(ucs2_buffer + j, c); + j += 2; + } else if (i == utf8_string_len - 1 || + j >= ucs2_buffer_size - 1) { + /* incomplete surrogate */ + return -1; + } else { + u8 c2 = utf8_string[++i]; + if ((c & 0xE0) == 0xC0) { + /* two-byte encoding */ + WPA_PUT_LE16(ucs2_buffer + j, + ((c & 0x1F) << 6) | (c2 & 0x3F)); + j += 2; + } else if (i == utf8_string_len || + j >= ucs2_buffer_size - 1) { + /* incomplete surrogate */ + return -1; + } else { + /* three-byte encoding */ + u8 c3 = utf8_string[++i]; + WPA_PUT_LE16(ucs2_buffer + j, + ((c & 0xF) << 12) | + ((c2 & 0x3F) << 6) | (c3 & 0x3F)); + } + } + } + + if (ucs2_string_size) + *ucs2_string_size = j / 2; + return 0; +} + + +/** + * challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2 + * @peer_challenge: 16-octet PeerChallenge (IN) + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @challenge: 8-octet Challenge (OUT) + * Returns: 0 on success, -1 on failure + */ +static int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, + const u8 *username, size_t username_len, + u8 *challenge) +{ + u8 hash[SHA1_MAC_LEN]; + const unsigned char *addr[3]; + size_t len[3]; + + addr[0] = peer_challenge; + len[0] = 16; + addr[1] = auth_challenge; + len[1] = 16; + addr[2] = username; + len[2] = username_len; + + if (sha1_vector(3, addr, len, hash)) + return -1; + os_memcpy(challenge, hash, 8); + return 0; +} + + +/** + * nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3 + * @password: 0-to-256-unicode-char Password (IN; UTF-8) + * @password_len: Length of password + * @password_hash: 16-octet PasswordHash (OUT) + * Returns: 0 on success, -1 on failure + */ +int nt_password_hash(const u8 *password, size_t password_len, + u8 *password_hash) +{ + u8 buf[512], *pos; + size_t len, max_len; + + max_len = sizeof(buf); + if (utf8_to_ucs2(password, password_len, buf, max_len, &len) < 0) + return -1; + + len *= 2; + pos = buf; + return md4_vector(1, (const u8 **) &pos, &len, password_hash); +} + + +/** + * hash_nt_password_hash - HashNtPasswordHash() - RFC 2759, Sect. 8.4 + * @password_hash: 16-octet PasswordHash (IN) + * @password_hash_hash: 16-octet PasswordHashHash (OUT) + * Returns: 0 on success, -1 on failure + */ +int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash) +{ + size_t len = 16; + return md4_vector(1, &password_hash, &len, password_hash_hash); +} + + +/** + * challenge_response - ChallengeResponse() - RFC 2759, Sect. 8.5 + * @challenge: 8-octet Challenge (IN) + * @password_hash: 16-octet PasswordHash (IN) + * @response: 24-octet Response (OUT) + */ +void challenge_response(const u8 *challenge, const u8 *password_hash, + u8 *response) +{ + u8 zpwd[7]; + des_encrypt(challenge, password_hash, response); + des_encrypt(challenge, password_hash + 7, response + 8); + zpwd[0] = password_hash[14]; + zpwd[1] = password_hash[15]; + os_memset(zpwd + 2, 0, 5); + des_encrypt(challenge, zpwd, response + 16); +} + + +/** + * generate_nt_response - GenerateNTResponse() - RFC 2759, Sect. 8.1 + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) + * @peer_challenge: 16-octet PeerChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @password: 0-to-256-unicode-char Password (IN; UTF-8) + * @password_len: Length of password + * @response: 24-octet Response (OUT) + * Returns: 0 on success, -1 on failure + */ +int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, + const u8 *username, size_t username_len, + const u8 *password, size_t password_len, + u8 *response) +{ + u8 challenge[8]; + u8 password_hash[16]; + + if (challenge_hash(peer_challenge, auth_challenge, username, + username_len, challenge)) + return -1; + if (nt_password_hash(password, password_len, password_hash)) + return -1; + challenge_response(challenge, password_hash, response); + return 0; +} + + +/** + * generate_nt_response_pwhash - GenerateNTResponse() - RFC 2759, Sect. 8.1 + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) + * @peer_challenge: 16-octet PeerChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @password_hash: 16-octet PasswordHash (IN) + * @response: 24-octet Response (OUT) + * Returns: 0 on success, -1 on failure + */ +int generate_nt_response_pwhash(const u8 *auth_challenge, + const u8 *peer_challenge, + const u8 *username, size_t username_len, + const u8 *password_hash, + u8 *response) +{ + u8 challenge[8]; + + if (challenge_hash(peer_challenge, auth_challenge, + username, username_len, + challenge)) + return -1; + challenge_response(challenge, password_hash, response); + return 0; +} + + +/** + * generate_authenticator_response_pwhash - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7 + * @password_hash: 16-octet PasswordHash (IN) + * @nt_response: 24-octet NT-Response (IN) + * @peer_challenge: 16-octet PeerChallenge (IN) + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually + * encoded as a 42-octet ASCII string (S=hexdump_of_response) + * Returns: 0 on success, -1 on failure + */ +int generate_authenticator_response_pwhash( + const u8 *password_hash, + const u8 *peer_challenge, const u8 *auth_challenge, + const u8 *username, size_t username_len, + const u8 *nt_response, u8 *response) +{ + static const u8 magic1[39] = { + 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65, + 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, + 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 + }; + static const u8 magic2[41] = { + 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B, + 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F, + 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E, + 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, + 0x6E + }; + + u8 password_hash_hash[16], challenge[8]; + const unsigned char *addr1[3]; + const size_t len1[3] = { 16, 24, sizeof(magic1) }; + const unsigned char *addr2[3]; + const size_t len2[3] = { SHA1_MAC_LEN, 8, sizeof(magic2) }; + + addr1[0] = password_hash_hash; + addr1[1] = nt_response; + addr1[2] = magic1; + + addr2[0] = response; + addr2[1] = challenge; + addr2[2] = magic2; + + if (hash_nt_password_hash(password_hash, password_hash_hash)) + return -1; + if (sha1_vector(3, addr1, len1, response)) + return -1; + + if (challenge_hash(peer_challenge, auth_challenge, username, + username_len, challenge)) + return -1; + return sha1_vector(3, addr2, len2, response); +} + + +/** + * generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7 + * @password: 0-to-256-unicode-char Password (IN; UTF-8) + * @password_len: Length of password + * @nt_response: 24-octet NT-Response (IN) + * @peer_challenge: 16-octet PeerChallenge (IN) + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually + * encoded as a 42-octet ASCII string (S=hexdump_of_response) + * Returns: 0 on success, -1 on failure + */ +int generate_authenticator_response(const u8 *password, size_t password_len, + const u8 *peer_challenge, + const u8 *auth_challenge, + const u8 *username, size_t username_len, + const u8 *nt_response, u8 *response) +{ + u8 password_hash[16]; + if (nt_password_hash(password, password_len, password_hash)) + return -1; + return generate_authenticator_response_pwhash( + password_hash, peer_challenge, auth_challenge, + username, username_len, nt_response, response); +} + + +/** + * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5 + * @challenge: 8-octet Challenge (IN) + * @password: 0-to-256-unicode-char Password (IN; UTF-8) + * @password_len: Length of password + * @response: 24-octet Response (OUT) + * Returns: 0 on success, -1 on failure + */ +int nt_challenge_response(const u8 *challenge, const u8 *password, + size_t password_len, u8 *response) +{ + u8 password_hash[16]; + if (nt_password_hash(password, password_len, password_hash)) + return -1; + challenge_response(challenge, password_hash, response); + return 0; +} + + +/** + * get_master_key - GetMasterKey() - RFC 3079, Sect. 3.4 + * @password_hash_hash: 16-octet PasswordHashHash (IN) + * @nt_response: 24-octet NTResponse (IN) + * @master_key: 16-octet MasterKey (OUT) + * Returns: 0 on success, -1 on failure + */ +int get_master_key(const u8 *password_hash_hash, const u8 *nt_response, + u8 *master_key) +{ + static const u8 magic1[27] = { + 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d, + 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 + }; + const unsigned char *addr[3]; + const size_t len[3] = { 16, 24, sizeof(magic1) }; + u8 hash[SHA1_MAC_LEN]; + + addr[0] = password_hash_hash; + addr[1] = nt_response; + addr[2] = magic1; + + if (sha1_vector(3, addr, len, hash)) + return -1; + os_memcpy(master_key, hash, 16); + return 0; +} + + +/** + * get_asymetric_start_key - GetAsymetricStartKey() - RFC 3079, Sect. 3.4 + * @master_key: 16-octet MasterKey (IN) + * @session_key: 8-to-16 octet SessionKey (OUT) + * @session_key_len: SessionKeyLength (Length of session_key) (IN) + * @is_send: IsSend (IN, BOOLEAN) + * @is_server: IsServer (IN, BOOLEAN) + * Returns: 0 on success, -1 on failure + */ +int get_asymetric_start_key(const u8 *master_key, u8 *session_key, + size_t session_key_len, int is_send, + int is_server) +{ + static const u8 magic2[84] = { + 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79, + 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, + 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x2e + }; + static const u8 magic3[84] = { + 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, + 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, + 0x6b, 0x65, 0x79, 0x2e + }; + static const u8 shs_pad1[40] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static const u8 shs_pad2[40] = { + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 + }; + u8 digest[SHA1_MAC_LEN]; + const unsigned char *addr[4]; + const size_t len[4] = { 16, 40, 84, 40 }; + + addr[0] = master_key; + addr[1] = shs_pad1; + if (is_send) { + addr[2] = is_server ? magic3 : magic2; + } else { + addr[2] = is_server ? magic2 : magic3; + } + addr[3] = shs_pad2; + + if (sha1_vector(4, addr, len, digest)) + return -1; + + if (session_key_len > SHA1_MAC_LEN) + session_key_len = SHA1_MAC_LEN; + os_memcpy(session_key, digest, session_key_len); + return 0; +} + + +#define PWBLOCK_LEN 516 + +/** + * encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10 + * @password: 0-to-256-unicode-char Password (IN; UTF-8) + * @password_len: Length of password + * @password_hash: 16-octet PasswordHash (IN) + * @pw_block: 516-byte PwBlock (OUT) + * Returns: 0 on success, -1 on failure + */ +int encrypt_pw_block_with_password_hash( + const u8 *password, size_t password_len, + const u8 *password_hash, u8 *pw_block) +{ + size_t ucs2_len, offset; + u8 *pos; + + os_memset(pw_block, 0, PWBLOCK_LEN); + + if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0) + return -1; + + if (ucs2_len > 256) + return -1; + + offset = (256 - ucs2_len) * 2; + if (offset != 0) { + os_memmove(pw_block + offset, pw_block, ucs2_len * 2); + if (os_get_random(pw_block, offset) < 0) + return -1; + } + /* + * PasswordLength is 4 octets, but since the maximum password length is + * 256, only first two (in little endian byte order) can be non-zero. + */ + pos = &pw_block[2 * 256]; + WPA_PUT_LE16(pos, password_len * 2); + rc4_skip(password_hash, 16, 0, pw_block, PWBLOCK_LEN); + return 0; +} + + +/** + * new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9 + * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8) + * @new_password_len: Length of new_password + * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8) + * @old_password_len: Length of old_password + * @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT) + * Returns: 0 on success, -1 on failure + */ +int new_password_encrypted_with_old_nt_password_hash( + const u8 *new_password, size_t new_password_len, + const u8 *old_password, size_t old_password_len, + u8 *encrypted_pw_block) +{ + u8 password_hash[16]; + + if (nt_password_hash(old_password, old_password_len, password_hash)) + return -1; + if (encrypt_pw_block_with_password_hash(new_password, new_password_len, + password_hash, + encrypted_pw_block)) + return -1; + return 0; +} + + +/** + * nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13 + * @password_hash: 16-octer PasswordHash (IN) + * @block: 16-octet Block (IN) + * @cypher: 16-octer Cypher (OUT) + */ +void nt_password_hash_encrypted_with_block(const u8 *password_hash, + const u8 *block, u8 *cypher) +{ + des_encrypt(password_hash, block, cypher); + des_encrypt(password_hash + 8, block + 7, cypher + 8); +} + + +/** + * old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12 + * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8) + * @new_password_len: Length of new_password + * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8) + * @old_password_len: Length of old_password + * @encrypted_password_hash: 16-octet EncryptedPasswordHash (OUT) + * Returns: 0 on success, -1 on failure + */ +int old_nt_password_hash_encrypted_with_new_nt_password_hash( + const u8 *new_password, size_t new_password_len, + const u8 *old_password, size_t old_password_len, + u8 *encrypted_password_hash) +{ + u8 old_password_hash[16], new_password_hash[16]; + + if (nt_password_hash(old_password, old_password_len, + old_password_hash) || + nt_password_hash(new_password, new_password_len, + new_password_hash)) + return -1; + nt_password_hash_encrypted_with_block(old_password_hash, + new_password_hash, + encrypted_password_hash); + return 0; +} + diff --git a/components/wpa_supplicant/src/fast_crypto_ops.c b/components/wpa_supplicant/src/fast_crypto_ops.c index 0152df89..0e5cd01a 100644 --- a/components/wpa_supplicant/src/fast_crypto_ops.c +++ b/components/wpa_supplicant/src/fast_crypto_ops.c @@ -25,9 +25,11 @@ #include "wps/wps.h" #include "wps/wps_i.h" -#include "eap/eap_defs.h" -#include "eap/eap_common.h" - +#include "wpa2/eap_peer/eap.h" +#include "wpa2/tls/tls.h" +#include "wpa2/eap_peer/eap_methods.h" +#include "wpa2/eap_peer/eap_i.h" +#include "wpa2/eap_peer/eap_common.h" #include "esp_wifi_crypto_types.h" #if CONFIG_SSL_USING_MBEDTLS @@ -85,6 +87,39 @@ const wps_crypto_funcs_t g_wifi_default_wps_crypto_funcs = { .eap_msg_alloc = (esp_eap_msg_alloc_t)eap_msg_alloc }; +/* + * What should notice is that the cyrpto hash type function and crypto cipher type function can not register + * as different, i.e, if you use fast_crypto_hash_init, you should use fast_crypto_hash_update and + * fast_crypto_hash_finish for finish hash calculate, rather than call crypto_hash_update and + * crypto_hash_finish, so do crypto_cipher. + */ +const wpa2_crypto_funcs_t g_wifi_default_wpa2_crypto_funcs = { + .size = sizeof(wpa2_crypto_funcs_t), + .version = ESP_WIFI_CRYPTO_VERSION, + .crypto_hash_init = (esp_crypto_hash_init_t)fast_crypto_hash_init, + .crypto_hash_update = (esp_crypto_hash_update_t)fast_crypto_hash_update, + .crypto_hash_finish = (esp_crypto_hash_finish_t)fast_crypto_hash_finish, + .crypto_cipher_init = (esp_crypto_cipher_init_t)fast_crypto_cipher_init, + .crypto_cipher_encrypt = (esp_crypto_cipher_encrypt_t)fast_crypto_cipher_encrypt, + .crypto_cipher_decrypt = (esp_crypto_cipher_decrypt_t)fast_crypto_cipher_decrypt, + .crypto_cipher_deinit = (esp_crypto_cipher_deinit_t)fast_crypto_cipher_deinit, + .crypto_mod_exp = (esp_crypto_mod_exp_t)crypto_mod_exp, + .sha256_vector = (esp_sha256_vector_t)fast_sha256_vector, + .tls_init = (esp_tls_init_t)tls_init, + .tls_deinit = (esp_tls_deinit_t)tls_deinit, + .eap_peer_blob_init = (esp_eap_peer_blob_init_t)eap_peer_blob_init, + .eap_peer_blob_deinit = (esp_eap_peer_blob_deinit_t)eap_peer_blob_deinit, + .eap_peer_config_init = (esp_eap_peer_config_init_t)eap_peer_config_init, + .eap_peer_config_deinit = (esp_eap_peer_config_deinit_t)eap_peer_config_deinit, + .eap_peer_register_methods = (esp_eap_peer_register_methods_t)eap_peer_register_methods, + .eap_peer_unregister_methods = (esp_eap_peer_unregister_methods_t)eap_peer_unregister_methods, + .eap_deinit_prev_method = (esp_eap_deinit_prev_method_t)eap_deinit_prev_method, + .eap_peer_get_eap_method = (esp_eap_peer_get_eap_method_t)eap_peer_get_eap_method, + .eap_sm_abort = (esp_eap_sm_abort_t)eap_sm_abort, + .eap_sm_build_nak = (esp_eap_sm_build_nak_t)eap_sm_build_nak, + .eap_sm_build_identity_resp = (esp_eap_sm_build_identity_resp_t)eap_sm_build_identity_resp, + .eap_msg_alloc = (esp_eap_msg_alloc_t)eap_msg_alloc +}; #else /* @@ -140,4 +175,38 @@ const wps_crypto_funcs_t g_wifi_default_wps_crypto_funcs = { .wps_is_selected_pbc_registrar = (esp_wps_is_selected_pbc_registrar_t)wps_is_selected_pbc_registrar, .eap_msg_alloc = (esp_eap_msg_alloc_t)eap_msg_alloc }; + +/* + * What should notice is that the cyrpto hash type function and crypto cipher type function can not register + * as different, i.e, if you use fast_crypto_hash_init, you should use fast_crypto_hash_update and + * fast_crypto_hash_finish for finish hash calculate, rather than call crypto_hash_update and + * crypto_hash_finish, so do crypto_cipher. + */ +const wpa2_crypto_funcs_t g_wifi_default_wpa2_crypto_funcs = { + .size = sizeof(wpa2_crypto_funcs_t), + .version = ESP_WIFI_CRYPTO_VERSION, + .crypto_hash_init = (esp_crypto_hash_init_t)crypto_hash_init, + .crypto_hash_update = (esp_crypto_hash_update_t)crypto_hash_update, + .crypto_hash_finish = (esp_crypto_hash_finish_t)crypto_hash_finish, + .crypto_cipher_init = (esp_crypto_cipher_init_t)crypto_cipher_init, + .crypto_cipher_encrypt = (esp_crypto_cipher_encrypt_t)crypto_cipher_encrypt, + .crypto_cipher_decrypt = (esp_crypto_cipher_decrypt_t)crypto_cipher_decrypt, + .crypto_cipher_deinit = (esp_crypto_cipher_deinit_t)crypto_cipher_deinit, + .crypto_mod_exp = (esp_crypto_mod_exp_t)crypto_mod_exp, + .sha256_vector = (esp_sha256_vector_t)sha256_vector, + .tls_init = (esp_tls_init_t)tls_init, + .tls_deinit = (esp_tls_deinit_t)tls_deinit, + .eap_peer_blob_init = (esp_eap_peer_blob_init_t)eap_peer_blob_init, + .eap_peer_blob_deinit = (esp_eap_peer_blob_deinit_t)eap_peer_blob_deinit, + .eap_peer_config_init = (esp_eap_peer_config_init_t)eap_peer_config_init, + .eap_peer_config_deinit = (esp_eap_peer_config_deinit_t)eap_peer_config_deinit, + .eap_peer_register_methods = (esp_eap_peer_register_methods_t)eap_peer_register_methods, + .eap_peer_unregister_methods = (esp_eap_peer_unregister_methods_t)eap_peer_unregister_methods, + .eap_deinit_prev_method = (esp_eap_deinit_prev_method_t)eap_deinit_prev_method, + .eap_peer_get_eap_method = (esp_eap_peer_get_eap_method_t)eap_peer_get_eap_method, + .eap_sm_abort = (esp_eap_sm_abort_t)eap_sm_abort, + .eap_sm_build_nak = (esp_eap_sm_build_nak_t)eap_sm_build_nak, + .eap_sm_build_identity_resp = (esp_eap_sm_build_identity_resp_t)eap_sm_build_identity_resp, + .eap_msg_alloc = (esp_eap_msg_alloc_t)eap_msg_alloc +}; #endif \ No newline at end of file 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 index 00000000..d182bb4d --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/eap_peer/chap.c @@ -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 index 00000000..7ba0e283 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/eap_peer/eap.c @@ -0,0 +1,871 @@ +/* + * EAP peer state machines (RFC 4137) + * Copyright (c) 2004-2012, Jouni Malinen + * + * 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 + +#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 + + +#if 1 +static bool gl_disable_time_check = true; +#endif + +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 wifi_unregister_wpa2_cb(void); + +#if 1//def 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) { + //do not propose insecure unencapsulated MSCHAPv2 as Phase 1 Method + if (m->vendor == EAP_VENDOR_IETF && m->method == EAP_TYPE_MSCHAPV2) { + continue; + } + + //do not propose EAP_TYPE_TLS if no client cert/key are configured + if (m->vendor == EAP_VENDOR_IETF && m->method == EAP_TYPE_TLS) { + struct eap_peer_config* config = eap_get_config(sm); + + if (config == NULL || config->private_key == 0 || config->client_cert == 0) { + continue; + } + } + + 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, m->method); + } + + 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 + 1); + 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 + 1); + 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 + 1); + 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; +} + +#if 1 +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) +{ + wifi_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; +} + +#endif \ No newline at end of file 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 100755 index 00000000..22054c84 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/eap_peer/eap_common.c @@ -0,0 +1,219 @@ +/* + * EAP common peer/server definitions + * Copyright (c) 2004-2012, Jouni Malinen + * + * 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\n"); + 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\n"); + 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 index 00000000..827be66f --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/eap_peer/eap_mschapv2.c @@ -0,0 +1,749 @@ +/* + * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt) + * Copyright (c) 2004-2008, 2012, Jouni Malinen + * + * 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" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +#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; + + //Do not init insecure unencapsulated MSCHAPv2 as Phase 1 method, only init if Phase 2 + if (!sm->init_phase2) { + return NULL; + } + + 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; + 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; + } + +#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 index 00000000..b9408207 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/eap_peer/eap_peap.c @@ -0,0 +1,1515 @@ +/* + * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) + * Copyright (c) 2004-2008, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ +#ifdef EAP_PEAP + +#include "wpa/includes.h" + +#include "wpa/common.h" +#include "crypto/sha1.h" +#include "wpa2/tls/tls.h" +#include "wpa2/eap_peer/eap_tlv_common.h" +#include "wpa2/eap_peer/eap_peap_common.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" +//#include "tncc.h" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +/* Maximum supported PEAP version + * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt + * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt + * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt + */ +#define EAP_PEAP_VERSION 1 + + +static void eap_peap_deinit(struct eap_sm* sm, void* priv); + + +struct eap_peap_data { + struct eap_ssl_data ssl; + + int peap_version, force_peap_version, force_new_label; + + const struct eap_method* phase2_method; + void* phase2_priv; + int phase2_success; + int phase2_eap_success; + int phase2_eap_started; + + struct eap_method_type phase2_type; + struct eap_method_type* phase2_types; + size_t num_phase2_types; + + int peap_outer_success; /* 0 = PEAP terminated on Phase 2 inner + * EAP-Success + * 1 = reply with tunneled EAP-Success to inner + * EAP-Success and expect AS to send outer + * (unencrypted) EAP-Success after this + * 2 = reply with PEAP/TLS ACK to inner + * EAP-Success and expect AS to send outer + * (unencrypted) EAP-Success after this */ + int resuming; /* starting a resumed session */ + int reauth; /* reauthentication */ + u8* key_data; + u8* session_id; + size_t id_len; + + struct wpabuf* pending_phase2_req; + enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding; + int crypto_binding_used; + u8 binding_nonce[32]; + u8 ipmk[40]; + u8 cmk[20]; + int soh; /* Whether IF-TNCCS-SOH (Statement of Health; Microsoft NAP) + * is enabled. */ +}; + + +static int +eap_peap_parse_phase1(struct eap_peap_data* data, + const char* phase1) +{ + const char* pos; + + pos = os_strstr(phase1, "peapver="); + + if (pos) { + data->force_peap_version = atoi(pos + 8); + data->peap_version = data->force_peap_version; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Forced PEAP version %d", + data->force_peap_version); + } + + if (os_strstr(phase1, "peaplabel=1")) { + data->force_new_label = 1; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for key " + "derivation"); + } + + if (os_strstr(phase1, "peap_outer_success=0")) { + data->peap_outer_success = 0; + wpa_printf(MSG_DEBUG, "EAP-PEAP: terminate authentication on " + "tunneled EAP-Success"); + } else if (os_strstr(phase1, "peap_outer_success=1")) { + data->peap_outer_success = 1; + wpa_printf(MSG_DEBUG, "EAP-PEAP: send tunneled EAP-Success " + "after receiving tunneled EAP-Success"); + } else if (os_strstr(phase1, "peap_outer_success=2")) { + data->peap_outer_success = 2; + wpa_printf(MSG_DEBUG, "EAP-PEAP: send PEAP/TLS ACK after " + "receiving tunneled EAP-Success"); + } + + if (os_strstr(phase1, "crypto_binding=0")) { + data->crypto_binding = NO_BINDING; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Do not use cryptobinding"); + } else if (os_strstr(phase1, "crypto_binding=1")) { + data->crypto_binding = OPTIONAL_BINDING; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Optional cryptobinding"); + } else if (os_strstr(phase1, "crypto_binding=2")) { + data->crypto_binding = REQUIRE_BINDING; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Require cryptobinding"); + } + +#ifdef EAP_TNC + + if (os_strstr(phase1, "tnc=soh2")) { + data->soh = 2; + wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled"); + } else if (os_strstr(phase1, "tnc=soh1")) { + data->soh = 1; + wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 1 enabled"); + } else if (os_strstr(phase1, "tnc=soh")) { + data->soh = 2; + wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled"); + } + +#endif /* EAP_TNC */ + + return 0; +} + + +static void* +eap_peap_init(struct eap_sm* sm) +{ + struct eap_peap_data* data; + struct eap_peer_config* config = eap_get_config(sm); + + data = (struct eap_peap_data*)os_zalloc(sizeof(*data)); + + if (data == NULL) { + return NULL; + } + + sm->peap_done = FALSE; + data->peap_version = EAP_PEAP_VERSION; + data->force_peap_version = -1; + data->peap_outer_success = 2; + data->crypto_binding = OPTIONAL_BINDING; + + if (config && config->phase1 && + eap_peap_parse_phase1(data, config->phase1) < 0) { + eap_peap_deinit(sm, data); + return NULL; + } + + if (eap_peer_select_phase2_methods(config, "auth=", + &data->phase2_types, + &data->num_phase2_types) < 0) { + eap_peap_deinit(sm, data); + return NULL; + } + + data->phase2_type.vendor = EAP_VENDOR_IETF; + data->phase2_type.method = EAP_TYPE_NONE; + + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_PEAP)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); + eap_peap_deinit(sm, data); + return NULL; + } + + return data; +} + + +static void +eap_peap_deinit(struct eap_sm* sm, void* priv) +{ + struct eap_peap_data* data = priv; + + if (data == NULL) { + return; + } + + if (data->phase2_priv && data->phase2_method) { + data->phase2_method->deinit(sm, data->phase2_priv); + } + + os_free(data->phase2_types); + eap_peer_tls_ssl_deinit(sm, &data->ssl); + os_free(data->key_data); + os_free(data->session_id); + wpabuf_free(data->pending_phase2_req); + os_free(data); +} + + +/** + * eap_tlv_build_nak - Build EAP-TLV NAK message + * @id: EAP identifier for the header + * @nak_type: TLV type (EAP_TLV_*) + * Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure + * + * This function builds an EAP-TLV NAK message. The caller is responsible for + * freeing the returned buffer. + */ +static struct wpabuf* +eap_tlv_build_nak(int id, u16 nak_type) +{ + struct wpabuf* msg; + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, 10, + EAP_CODE_RESPONSE, id); + + if (msg == NULL) { + return NULL; + } + + wpabuf_put_u8(msg, 0x80); /* Mandatory */ + wpabuf_put_u8(msg, EAP_TLV_NAK_TLV); + wpabuf_put_be16(msg, 6); /* Length */ + wpabuf_put_be32(msg, 0); /* Vendor-Id */ + wpabuf_put_be16(msg, nak_type); /* NAK-Type */ + + return msg; +} + + +static int +eap_peap_get_isk(struct eap_sm* sm, struct eap_peap_data* data, + u8* isk, size_t isk_len) +{ + u8* key; + size_t key_len; + + os_memset(isk, 0, isk_len); + + if (data->phase2_method == NULL || data->phase2_priv == NULL || + data->phase2_method->isKeyAvailable == NULL || + data->phase2_method->getKey == NULL) { + return 0; + } + + if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) || + (key = data->phase2_method->getKey(sm, data->phase2_priv, + &key_len)) == NULL) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not get key material " + "from Phase 2"); + return -1; + } + + if (key_len > isk_len) { + key_len = isk_len; + } + + os_memcpy(isk, key, key_len); + os_free(key); + + return 0; +} + + +static int +eap_peap_derive_cmk(struct eap_sm* sm, struct eap_peap_data* data) +{ + u8* tk; + u8 isk[32], imck[60]; + + /* + * Tunnel key (TK) is the first 60 octets of the key generated by + * phase 1 of PEAP (based on TLS). + */ + tk = data->key_data; + + if (tk == NULL) { + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60); + + if (data->reauth && + tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) { + /* Fast-connect: IPMK|CMK = TK */ + os_memcpy(data->ipmk, tk, 40); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK", + data->ipmk, 40); + os_memcpy(data->cmk, tk + 40, 20); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK from TK", + data->cmk, 20); + return 0; + } + + if (eap_peap_get_isk(sm, data, isk, sizeof(isk)) < 0) { + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk)); + + /* + * IPMK Seed = "Inner Methods Compound Keys" | ISK + * TempKey = First 40 octets of TK + * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60) + * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space + * in the end of the label just before ISK; is that just a typo?) + */ + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); + + if (peap_prfplus(data->peap_version, tk, 40, + "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)) < 0) { + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)", + imck, sizeof(imck)); + + os_memcpy(data->ipmk, imck, 40); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); + os_memcpy(data->cmk, imck + 40, 20); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20); + + return 0; +} + + +static int +eap_tlv_add_cryptobinding(struct eap_sm* sm, + struct eap_peap_data* data, + struct wpabuf* buf) +{ + u8* mac; + u8 eap_type = EAP_TYPE_PEAP; + const u8* addr[2]; + size_t len[2]; + u16 tlv_type; + + /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ + addr[0] = wpabuf_put(buf, 0); + len[0] = 60; + addr[1] = &eap_type; + len[1] = 1; + + tlv_type = EAP_TLV_CRYPTO_BINDING_TLV; + + if (data->peap_version >= 2) { + tlv_type |= EAP_TLV_TYPE_MANDATORY; + } + + wpabuf_put_be16(buf, tlv_type); + wpabuf_put_be16(buf, 56); + + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_u8(buf, data->peap_version); /* Version */ + wpabuf_put_u8(buf, data->peap_version); /* RecvVersion */ + wpabuf_put_u8(buf, 1); /* SubType: 0 = Request, 1 = Response */ + wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */ + mac = wpabuf_put(buf, 20); /* Compound_MAC */ + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", data->cmk, 20); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1", + addr[0], len[0]); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2", + addr[1], len[1]); + hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN); + data->crypto_binding_used = 1; + + return 0; +} + + +/** + * eap_tlv_build_result - Build EAP-TLV Result message + * @id: EAP identifier for the header + * @status: Status (EAP_TLV_RESULT_SUCCESS or EAP_TLV_RESULT_FAILURE) + * Returns: Buffer to the allocated EAP-TLV Result message or %NULL on failure + * + * This function builds an EAP-TLV Result message. The caller is responsible + * for freeing the returned buffer. + */ +static struct wpabuf* +eap_tlv_build_result(struct eap_sm* sm, + struct eap_peap_data* data, + int crypto_tlv_used, + int id, u16 status) +{ + struct wpabuf* msg; + size_t len; + + if (data->crypto_binding == NO_BINDING) { + crypto_tlv_used = 0; + } + + len = 6; + + if (crypto_tlv_used) { + len += 60; /* Cryptobinding TLV */ + } + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len, + EAP_CODE_RESPONSE, id); + + if (msg == NULL) { + return NULL; + } + + wpabuf_put_u8(msg, 0x80); /* Mandatory */ + wpabuf_put_u8(msg, EAP_TLV_RESULT_TLV); + wpabuf_put_be16(msg, 2); /* Length */ + wpabuf_put_be16(msg, status); /* Status */ + + if (crypto_tlv_used && eap_tlv_add_cryptobinding(sm, data, msg)) { + wpabuf_free(msg); + return NULL; + } + + return msg; +} + + +static int +eap_tlv_validate_cryptobinding(struct eap_sm* sm, + struct eap_peap_data* data, + const u8* crypto_tlv, + size_t crypto_tlv_len) +{ + u8 buf[61], mac[SHA1_MAC_LEN]; + const u8* pos; + + if (eap_peap_derive_cmk(sm, data) < 0) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not derive CMK"); + return -1; + } + + if (crypto_tlv_len != 4 + 56) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV " + "length %d", (int) crypto_tlv_len); + return -1; + } + + pos = crypto_tlv; + pos += 4; /* TLV header */ + + if (pos[1] != data->peap_version) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version " + "mismatch (was %d; expected %d)", + pos[1], data->peap_version); + return -1; + } + + if (pos[3] != 0) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV " + "SubType %d", pos[3]); + return -1; + } + + pos += 4; + os_memcpy(data->binding_nonce, pos, 32); + pos += 32; /* Nonce */ + + /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ + os_memcpy(buf, crypto_tlv, 60); + os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */ + buf[60] = EAP_TYPE_PEAP; + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Compound_MAC data", + buf, sizeof(buf)); + hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac); + + if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in " + "cryptobinding TLV"); + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received MAC", + pos, SHA1_MAC_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Expected MAC", + mac, SHA1_MAC_LEN); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received"); + + return 0; +} + + +/** + * eap_tlv_process - Process a received EAP-TLV message and generate a response + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @ret: Return values from EAP request validation and processing + * @req: EAP-TLV request to be processed. The caller must have validated that + * the buffer is large enough to contain full request (hdr->length bytes) and + * that the EAP type is EAP_TYPE_TLV. + * @resp: Buffer to return a pointer to the allocated response message. This + * field should be initialized to %NULL before the call. The value will be + * updated if a response message is generated. The caller is responsible for + * freeing the allocated message. + * @force_failure: Force negotiation to fail + * Returns: 0 on success, -1 on failure + */ +static int +eap_tlv_process(struct eap_sm* sm, struct eap_peap_data* data, + struct eap_method_ret* ret, + const struct wpabuf* req, struct wpabuf** resp, + int force_failure) +{ + size_t left, tlv_len; + const u8* pos; + const u8* result_tlv = NULL, *crypto_tlv = NULL; + size_t result_tlv_len = 0, crypto_tlv_len = 0; + int tlv_type, mandatory; + + /* Parse TLVs */ + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, req, &left); + + if (pos == NULL) { + return -1; + } + + wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left); + + while (left >= 4) { + mandatory = !!(pos[0] & 0x80); + tlv_type = WPA_GET_BE16(pos) & 0x3fff; + pos += 2; + tlv_len = WPA_GET_BE16(pos); + pos += 2; + left -= 4; + + if (tlv_len > left) { + wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun " + "(tlv_len=%lu left=%lu)", + (unsigned long) tlv_len, + (unsigned long) left); + return -1; + } + + switch (tlv_type) { + case EAP_TLV_RESULT_TLV: + result_tlv = pos; + result_tlv_len = tlv_len; + break; + + case EAP_TLV_CRYPTO_BINDING_TLV: + crypto_tlv = pos; + crypto_tlv_len = tlv_len; + break; + + default: + wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type " + "%d%s", tlv_type, + mandatory ? " (mandatory)" : ""); + + if (mandatory) { + /* NAK TLV and ignore all TLVs in this packet. + */ + *resp = eap_tlv_build_nak(eap_get_id(req), + tlv_type); + return *resp == NULL ? -1 : 0; + } + + /* Ignore this TLV, but process other TLVs */ + break; + } + + pos += tlv_len; + left -= tlv_len; + } + + if (left) { + wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in " + "Request (left=%lu)", (unsigned long) left); + return -1; + } + + /* Process supported TLVs */ + if (crypto_tlv && data->crypto_binding != NO_BINDING) { + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV", + crypto_tlv, crypto_tlv_len); + + if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4, + crypto_tlv_len + 4) < 0) { + if (result_tlv == NULL) { + return -1; + } + + force_failure = 1; + crypto_tlv = NULL; /* do not include Cryptobinding TLV + * in response, if the received + * cryptobinding was invalid. */ + } + } else if (!crypto_tlv && data->crypto_binding == REQUIRE_BINDING) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV"); + return -1; + } + + if (result_tlv) { + int status, resp_status; + wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV", + result_tlv, result_tlv_len); + + if (result_tlv_len < 2) { + wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV " + "(len=%lu)", + (unsigned long) result_tlv_len); + return -1; + } + + status = WPA_GET_BE16(result_tlv); + + if (status == EAP_TLV_RESULT_SUCCESS) { + wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success " + "- EAP-TLV/Phase2 Completed"); + + if (force_failure) { + wpa_printf(MSG_INFO, "EAP-TLV: Earlier failure" + " - force failed Phase 2"); + resp_status = EAP_TLV_RESULT_FAILURE; + ret->decision = DECISION_FAIL; + } else { + resp_status = EAP_TLV_RESULT_SUCCESS; + ret->decision = DECISION_UNCOND_SUCC; + } + } else if (status == EAP_TLV_RESULT_FAILURE) { + wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure"); + resp_status = EAP_TLV_RESULT_FAILURE; + ret->decision = DECISION_FAIL; + } else { + wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result " + "Status %d", status); + resp_status = EAP_TLV_RESULT_FAILURE; + ret->decision = DECISION_FAIL; + } + + ret->methodState = METHOD_DONE; + + *resp = eap_tlv_build_result(sm, data, crypto_tlv != NULL, + eap_get_id(req), resp_status); + } + + return 0; +} + + +static struct wpabuf* +eap_peapv2_tlv_eap_payload(struct wpabuf* buf) +{ + struct wpabuf* e; + struct eap_tlv_hdr* tlv; + + if (buf == NULL) { + return NULL; + } + + /* Encapsulate EAP packet in EAP-Payload TLV */ + wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV"); + e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf)); + + if (e == NULL) { + wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory " + "for TLV encapsulation"); + wpabuf_free(buf); + return NULL; + } + + tlv = wpabuf_put(e, sizeof(*tlv)); + tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + EAP_TLV_EAP_PAYLOAD_TLV); + tlv->length = host_to_be16(wpabuf_len(buf)); + wpabuf_put_buf(e, buf); + wpabuf_free(buf); + return e; +} + + +static int +eap_peap_phase2_request(struct eap_sm* sm, + struct eap_peap_data* data, + struct eap_method_ret* ret, + struct wpabuf* req, + struct wpabuf** resp) +{ + struct eap_hdr* hdr = wpabuf_mhead(req); + size_t len = be_to_host16(hdr->length); + u8* pos; + struct eap_method_ret iret; + //struct eap_peer_config *config = eap_get_config(sm); + + if (len <= sizeof(struct eap_hdr)) { + wpa_printf(MSG_INFO, "EAP-PEAP: too short " + "Phase 2 request (len=%lu)", (unsigned long) len); + return -1; + } + + pos = (u8*)(hdr + 1); + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: type=%d\n", *pos); + + switch (*pos) { + case EAP_TYPE_IDENTITY: + *resp = eap_sm_build_identity_resp(sm, hdr->identifier, 1); + break; + + case EAP_TYPE_TLV: + os_memset(&iret, 0, sizeof(iret)); + + if (eap_tlv_process(sm, data, &iret, req, resp, + data->phase2_eap_started && + !data->phase2_eap_success)) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + + if (iret.methodState == METHOD_DONE || + iret.methodState == METHOD_MAY_CONT) { + ret->methodState = iret.methodState; + ret->decision = iret.decision; + data->phase2_success = 1; + } + + break; + + case EAP_TYPE_EXPANDED: +#ifdef EAP_TNC + if (data->soh) { + const u8* epos; + size_t eleft; + + epos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21, + req, &eleft); + + if (epos) { + struct wpabuf* buf; + wpa_printf(MSG_DEBUG, + "EAP-PEAP: SoH EAP Extensions"); + buf = tncc_process_soh_request(data->soh, + epos, eleft); + + if (buf) { + *resp = eap_msg_alloc( + EAP_VENDOR_MICROSOFT, 0x21, + wpabuf_len(buf), + EAP_CODE_RESPONSE, + hdr->identifier); + + if (*resp == NULL) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + + wpabuf_put_buf(*resp, buf); + wpabuf_free(buf); + break; + } + } + } + +#endif /* EAP_TNC */ + + /* fall through */ + default: + if (data->phase2_type.vendor == EAP_VENDOR_IETF && + data->phase2_type.method == EAP_TYPE_NONE) { + size_t i; + + for (i = 0; i < data->num_phase2_types; i++) { + if (data->phase2_types[i].vendor != + EAP_VENDOR_IETF || + data->phase2_types[i].method != *pos) { + continue; + } + + data->phase2_type.vendor = + data->phase2_types[i].vendor; + data->phase2_type.method = + data->phase2_types[i].method; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected " + "Phase 2 EAP vendor %d method %d\n", + data->phase2_type.vendor, + data->phase2_type.method); + break; + } + } + + if (*pos != data->phase2_type.method || + *pos == EAP_TYPE_NONE) { + if (eap_peer_tls_phase2_nak(data->phase2_types, + data->num_phase2_types, + hdr, resp)) { + return -1; + } + + return 0; + } + + if (data->phase2_priv == NULL) { + data->phase2_method = eap_peer_get_eap_method( + data->phase2_type.vendor, + data->phase2_type.method); + + if (data->phase2_method) { + sm->init_phase2 = 1; + data->phase2_priv = + data->phase2_method->init(sm); + sm->init_phase2 = 0; + } + } + + if (data->phase2_priv == NULL || data->phase2_method == NULL) { + wpa_printf(MSG_ERROR, "EAP-PEAP: failed to initialize " + "Phase 2 EAP method %d\n", *pos); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + + data->phase2_eap_started = 1; + os_memset(&iret, 0, sizeof(iret)); + *resp = data->phase2_method->process(sm, data->phase2_priv, + &iret, req); + + if ((iret.methodState == METHOD_DONE || + iret.methodState == METHOD_MAY_CONT) && + (iret.decision == DECISION_UNCOND_SUCC || + iret.decision == DECISION_COND_SUCC)) { + data->phase2_eap_success = 1; + data->phase2_success = 1; + } + + break; + } + + if (*resp == NULL) { + wpa_printf(MSG_ERROR, "phase 2 response failure\n"); + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = wpabuf_alloc_copy(hdr, len); + } + + /* if (*resp == NULL && + (config->pending_req_identity || config->pending_req_password || + config->pending_req_otp || config->pending_req_new_password)) { + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = wpabuf_alloc_copy(hdr, len); + }*/ + + return 0; +} + + +static int +eap_peap_decrypt(struct eap_sm* sm, struct eap_peap_data* data, + struct eap_method_ret* ret, + const struct eap_hdr* req, + const struct wpabuf* in_data, + struct wpabuf** out_data) +{ + struct wpabuf* in_decrypted = NULL; + int res, skip_change = 0; + struct eap_hdr* hdr, *rhdr; + struct wpabuf* resp = NULL; + size_t len; + + wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for" + " Phase 2\n", (unsigned long) wpabuf_len(in_data)); + + if (data->pending_phase2_req) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - " + "skip decryption and use old data"); + /* Clear TLS reassembly state. */ + eap_peer_tls_reset_input(&data->ssl); + in_decrypted = data->pending_phase2_req; + data->pending_phase2_req = NULL; + skip_change = 1; + goto continue_req; + } + + if (wpabuf_len(in_data) == 0 && sm->workaround && + data->phase2_success) { + /* + * Cisco ACS seems to be using TLS ACK to terminate + * EAP-PEAPv0/GTC. Try to reply with TLS ACK. + */ + wpa_printf(MSG_DEBUG, "EAP-PEAP: Received TLS ACK, but " + "expected data - acknowledge with TLS ACK since " + "Phase 2 has been completed"); + ret->decision = DECISION_COND_SUCC; + ret->methodState = METHOD_DONE; + return 1; + } else if (wpabuf_len(in_data) == 0) { + /* Received TLS ACK - requesting more fragments */ + return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP, + data->peap_version, + req->identifier, NULL, out_data); + } + + res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); + + if (res) { + return res; + } + +continue_req: + wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", + in_decrypted); + + hdr = wpabuf_mhead(in_decrypted); + + if (wpabuf_len(in_decrypted) == 5 && hdr->code == EAP_CODE_REQUEST && + be_to_host16(hdr->length) == 5 && + eap_get_type(in_decrypted) == EAP_TYPE_IDENTITY) { + /* At least FreeRADIUS seems to send full EAP header with + * EAP Request Identity */ + skip_change = 1; + } + + if (wpabuf_len(in_decrypted) >= 5 && hdr->code == EAP_CODE_REQUEST && + eap_get_type(in_decrypted) == EAP_TYPE_TLV) { + skip_change = 1; + } + + if (data->peap_version == 0 && !skip_change) { + struct eap_hdr* nhdr; + struct wpabuf* nmsg = wpabuf_alloc(sizeof(struct eap_hdr) + + wpabuf_len(in_decrypted)); + + if (nmsg == NULL) { + wpabuf_free(in_decrypted); + return 0; + } + + nhdr = wpabuf_put(nmsg, sizeof(*nhdr)); + wpabuf_put_buf(nmsg, in_decrypted); + nhdr->code = req->code; + nhdr->identifier = req->identifier; + nhdr->length = host_to_be16(sizeof(struct eap_hdr) + + wpabuf_len(in_decrypted)); + + wpabuf_free(in_decrypted); + in_decrypted = nmsg; + } + + if (data->peap_version >= 2) { + struct eap_tlv_hdr* tlv; + struct wpabuf* nmsg; + + if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) { + wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 " + "EAP TLV"); + wpabuf_free(in_decrypted); + return 0; + } + + tlv = wpabuf_mhead(in_decrypted); + + if ((be_to_host16(tlv->tlv_type) & 0x3fff) != + EAP_TLV_EAP_PAYLOAD_TLV) { + wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV"); + wpabuf_free(in_decrypted); + return 0; + } + + if (sizeof(*tlv) + be_to_host16(tlv->length) > + wpabuf_len(in_decrypted)) { + wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV " + "length"); + wpabuf_free(in_decrypted); + return 0; + } + + hdr = (struct eap_hdr*)(tlv + 1); + + if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) { + wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full " + "EAP packet in EAP TLV"); + wpabuf_free(in_decrypted); + return 0; + } + + nmsg = wpabuf_alloc(be_to_host16(hdr->length)); + + if (nmsg == NULL) { + wpabuf_free(in_decrypted); + return 0; + } + + wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length)); + wpabuf_free(in_decrypted); + in_decrypted = nmsg; + } + + hdr = wpabuf_mhead(in_decrypted); + + if (wpabuf_len(in_decrypted) < sizeof(*hdr)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 " + "EAP frame (len=%lu)", + (unsigned long) wpabuf_len(in_decrypted)); + wpabuf_free(in_decrypted); + return 0; + } + + len = be_to_host16(hdr->length); + + if (len > wpabuf_len(in_decrypted)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in " + "Phase 2 EAP frame (len=%lu hdr->length=%lu)", + (unsigned long) wpabuf_len(in_decrypted), + (unsigned long) len); + wpabuf_free(in_decrypted); + return 0; + } + + if (len < wpabuf_len(in_decrypted)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Odd.. Phase 2 EAP header has " + "shorter length than full decrypted data " + "(%lu < %lu)", + (unsigned long) len, + (unsigned long) wpabuf_len(in_decrypted)); + } + + wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d " + "identifier=%d length=%lu\n", hdr->code, hdr->identifier, + (unsigned long) len); + + switch (hdr->code) { + case EAP_CODE_REQUEST: + if (eap_peap_phase2_request(sm, data, ret, in_decrypted, + &resp)) { + wpabuf_free(in_decrypted); + wpa_printf(MSG_ERROR, "EAP-PEAP: Phase2 Request " + "processing failed\n"); + return 0; + } + + break; + + case EAP_CODE_SUCCESS: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success\n"); + + if (data->peap_version == 1) { + /* EAP-Success within TLS tunnel is used to indicate + * shutdown of the TLS channel. The authentication has + * been completed. */ + if (data->phase2_eap_started && + !data->phase2_eap_success) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 " + "Success used to indicate success, " + "but Phase 2 EAP was not yet " + "completed successfully"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + wpabuf_free(in_decrypted); + return 0; + } + + wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - " + "EAP-Success within TLS tunnel - " + "authentication completed"); + ret->decision = DECISION_UNCOND_SUCC; + ret->methodState = METHOD_DONE; + data->phase2_success = 1; + + if (data->peap_outer_success == 2) { + wpabuf_free(in_decrypted); + wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK " + "to finish authentication"); + return 1; + } else if (data->peap_outer_success == 1) { + /* Reply with EAP-Success within the TLS + * channel to complete the authentication. */ + resp = wpabuf_alloc(sizeof(struct eap_hdr)); + + if (resp) { + rhdr = wpabuf_put(resp, sizeof(*rhdr)); + rhdr->code = EAP_CODE_SUCCESS; + rhdr->identifier = hdr->identifier; + rhdr->length = + host_to_be16(sizeof(*rhdr)); + } + } else { + /* No EAP-Success expected for Phase 1 (outer, + * unencrypted auth), so force EAP state + * machine to SUCCESS state. */ + sm->peap_done = TRUE; + } + } else { + /* FIX: ? */ + } + + break; + + case EAP_CODE_FAILURE: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure\n"); + ret->decision = DECISION_FAIL; + ret->methodState = METHOD_MAY_CONT; + ret->allowNotifications = FALSE; + /* Reply with EAP-Failure within the TLS channel to complete + * failure reporting. */ + resp = wpabuf_alloc(sizeof(struct eap_hdr)); + + if (resp) { + rhdr = wpabuf_put(resp, sizeof(*rhdr)); + rhdr->code = EAP_CODE_FAILURE; + rhdr->identifier = hdr->identifier; + rhdr->length = host_to_be16(sizeof(*rhdr)); + } + + break; + + default: + wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in " + "Phase 2 EAP header", hdr->code); + break; + } + + wpabuf_free(in_decrypted); + + if (resp) { + int skip_change2 = 0; + struct wpabuf* rmsg, buf; + + wpa_hexdump_buf_key(MSG_DEBUG, + "EAP-PEAP: Encrypting Phase 2 data", resp); + + /* PEAP version changes */ + if (data->peap_version >= 2) { + resp = eap_peapv2_tlv_eap_payload(resp); + + if (resp == NULL) { + return -1; + } + } + + if (wpabuf_len(resp) >= 5 && + wpabuf_head_u8(resp)[0] == EAP_CODE_RESPONSE && + eap_get_type(resp) == EAP_TYPE_TLV) { + skip_change2 = 1; + } + + rmsg = resp; + + if (data->peap_version == 0 && !skip_change2) { + wpabuf_set(&buf, wpabuf_head_u8(resp) + + sizeof(struct eap_hdr), + wpabuf_len(resp) - sizeof(struct eap_hdr)); + rmsg = &buf; + } + + if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP, + data->peap_version, req->identifier, + rmsg, out_data)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt " + "a Phase 2 frame"); + } + + wpabuf_free(resp); + } + + return 0; +} + + +static struct wpabuf* +eap_peap_process(struct eap_sm* sm, void* priv, + struct eap_method_ret* ret, + const struct wpabuf* reqData) +{ + const struct eap_hdr* req; + size_t left; + int res; + u8 flags, id; + struct wpabuf* resp; + const u8* pos; + struct eap_peap_data* data = priv; + + pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret, + reqData, &left, &flags); + + if (pos == NULL) { + return NULL; + } + + req = wpabuf_head(reqData); + id = req->identifier; + + if (flags & EAP_TLS_FLAGS_START) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own " + "ver=%d)", flags & EAP_TLS_VERSION_MASK, + data->peap_version); + + if ((flags & EAP_TLS_VERSION_MASK) < data->peap_version) { + data->peap_version = flags & EAP_TLS_VERSION_MASK; + } + + if (data->force_peap_version >= 0 && + data->force_peap_version != data->peap_version) { + wpa_printf(MSG_WARNING, "EAP-PEAP: Failed to select " + "forced PEAP version %d", + data->force_peap_version); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-PEAP: Using PEAP version %d", + data->peap_version); + left = 0; /* make sure that this frame is empty, even though it + * should always be, anyway */ + } + + resp = NULL; + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + !data->resuming) { + struct wpabuf msg; + wpabuf_set(&msg, pos, left); + res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp); + } else { + res = eap_peer_tls_process_helper(sm, &data->ssl, + EAP_TYPE_PEAP, + data->peap_version, id, pos, + left, &resp); + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + char* label; + wpa_printf(MSG_DEBUG, + "EAP-PEAP: TLS done, proceed to Phase 2"); + os_free(data->key_data); + + /* draft-josefsson-ppext-eap-tls-eap-05.txt + * specifies that PEAPv1 would use "client PEAP + * encryption" as the label. However, most existing + * PEAPv1 implementations seem to be using the old + * label, "client EAP encryption", instead. Use the old + * label by default, but allow it to be configured with + * phase1 parameter peaplabel=1. */ + if (data->peap_version > 1 || data->force_new_label) { + label = "client PEAP encryption"; + } else { + label = "client EAP encryption"; + } + + wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in " + "key derivation", label); + data->key_data = + eap_peer_tls_derive_key(sm, &data->ssl, label, + EAP_TLS_KEY_LEN); + + if (data->key_data) { + wpa_hexdump_key(MSG_DEBUG, + "EAP-PEAP: Derived key", + data->key_data, + EAP_TLS_KEY_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to " + "derive key"); + } + + os_free(data->session_id); + data->session_id = + eap_peer_tls_derive_session_id(sm, &data->ssl, + EAP_TYPE_PEAP, + &data->id_len); + + if (data->session_id) { + wpa_hexdump(MSG_DEBUG, + "EAP-PEAP: Derived Session-Id", + data->session_id, data->id_len); + } else { + wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to " + "derive Session-Id"); + } + + if (sm->workaround && data->resuming) { + /* + * At least few RADIUS servers (Aegis v1.1.6; + * but not v1.1.4; and Cisco ACS) seem to be + * terminating PEAPv1 (Aegis) or PEAPv0 (Cisco + * ACS) session resumption with outer + * EAP-Success. This does not seem to follow + * draft-josefsson-pppext-eap-tls-eap-05.txt + * section 4.2, so only allow this if EAP + * workarounds are enabled. + */ + wpa_printf(MSG_DEBUG, "EAP-PEAP: Workaround - " + "allow outer EAP-Success to " + "terminate PEAP resumption"); + ret->decision = DECISION_COND_SUCC; + data->phase2_success = 1; + } + + data->resuming = 0; + } + + if (res == 2) { + struct wpabuf msg; + /* + * Application data included in the handshake message. + */ + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = resp; + resp = NULL; + wpabuf_set(&msg, pos, left); + res = eap_peap_decrypt(sm, data, ret, req, &msg, + &resp); + } + } + + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + } + + if (res == 1) { + wpabuf_free(resp); + return eap_peer_tls_build_ack(id, EAP_TYPE_PEAP, + data->peap_version); + } + + return resp; +} + + +static bool +eap_peap_has_reauth_data(struct eap_sm* sm, void* priv) +{ + struct eap_peap_data* data = priv; + return tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + data->phase2_success; +} + + +static void +eap_peap_deinit_for_reauth(struct eap_sm* sm, void* priv) +{ + struct eap_peap_data* data = priv; + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = NULL; + data->crypto_binding_used = 0; +} + + +static void* +eap_peap_init_for_reauth(struct eap_sm* sm, void* priv) +{ + struct eap_peap_data* data = priv; + os_free(data->key_data); + data->key_data = NULL; + os_free(data->session_id); + data->session_id = NULL; + + if (eap_peer_tls_reauth_init(sm, &data->ssl)) { + os_free(data); + return NULL; + } + + if (data->phase2_priv && data->phase2_method && + data->phase2_method->init_for_reauth) { + data->phase2_method->init_for_reauth(sm, data->phase2_priv); + } + + data->phase2_success = 0; + data->phase2_eap_success = 0; + data->phase2_eap_started = 0; + data->resuming = 1; + data->reauth = 1; + sm->peap_done = FALSE; + return priv; +} + + +static int +eap_peap_get_status(struct eap_sm* sm, void* priv, char* buf, + size_t buflen, int verbose) +{ + struct eap_peap_data* data = priv; + int len, ret; + + len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); + + if (data->phase2_method) { + ret = snprintf(buf + len, buflen - len, + "EAP-PEAPv%d Phase2 method=%d\n", + data->peap_version, + data->phase2_method->method); + + if (ret < 0 || (size_t) ret >= buflen - len) { + return len; + } + + len += ret; + } + + return len; +} + + +static bool +eap_peap_isKeyAvailable(struct eap_sm* sm, void* priv) +{ + struct eap_peap_data* data = priv; + return data->key_data != NULL && data->phase2_success; +} + + +static u8* +eap_peap_getKey(struct eap_sm* sm, void* priv, size_t* len) +{ + struct eap_peap_data* data = priv; + u8* key; + + if (data->key_data == NULL || !data->phase2_success) { + return NULL; + } + + key = os_malloc(EAP_TLS_KEY_LEN); + + if (key == NULL) { + return NULL; + } + + *len = EAP_TLS_KEY_LEN; + + if (data->crypto_binding_used) { + u8 csk[128]; + + /* + * Note: It looks like Microsoft implementation requires null + * termination for this label while the one used for deriving + * IPMK|CMK did not use null termination. + */ + if (peap_prfplus(data->peap_version, data->ipmk, 40, + "Session Key Generating Function", + (u8*) "\00", 1, csk, sizeof(csk)) < 0) { + os_free(key); + return NULL; + } + + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk)); + os_memcpy(key, csk, EAP_TLS_KEY_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", + key, EAP_TLS_KEY_LEN); + } else { + os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); + } + + return key; +} + + +static u8* +eap_peap_get_session_id(struct eap_sm* sm, void* priv, size_t* len) +{ + struct eap_peap_data* data = priv; + u8* id; + + if (data->session_id == NULL || !data->phase2_success) { + return NULL; + } + + id = os_malloc(data->id_len); + + if (id == NULL) { + return NULL; + } + + *len = data->id_len; + os_memcpy(id, data->session_id, data->id_len); + + return id; +} + + +int +eap_peer_peap_register(void) +{ + struct eap_method* eap; + int ret; + + eap = eap_peer_method_alloc(EAP_VENDOR_IETF, EAP_TYPE_PEAP, + "PEAP"); + + if (eap == NULL) { + return -1; + } + + eap->init = eap_peap_init; + eap->deinit = eap_peap_deinit; + eap->process = eap_peap_process; + eap->isKeyAvailable = eap_peap_isKeyAvailable; + eap->getKey = eap_peap_getKey; + eap->get_status = eap_peap_get_status; + eap->has_reauth_data = eap_peap_has_reauth_data; + eap->deinit_for_reauth = eap_peap_deinit_for_reauth; + eap->init_for_reauth = eap_peap_init_for_reauth; + eap->getSessionId = eap_peap_get_session_id; + + ret = eap_peer_method_register(eap); + + if (ret) { + eap_peer_method_free(eap); + } + + return ret; +} + +#endif /* EAP_PEAP */ \ No newline at end of file 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 index 00000000..e4633982 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/eap_peer/eap_peap_common.c @@ -0,0 +1,95 @@ +/* + * EAP-PEAP common routines + * Copyright (c) 2008-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifdef EAP_PEAP + +#include "wpa/includes.h" + +#include "wpa/common.h" +#include "crypto/sha1.h" +#include "wpa2/eap_peer/eap_peap_common.h" + +int +peap_prfplus(int version, const u8* key, size_t key_len, + const char* label, const u8* seed, size_t seed_len, + u8* buf, size_t buf_len) +{ + unsigned char counter = 0; + size_t pos, plen; + u8 hash[SHA1_MAC_LEN]; + size_t label_len = os_strlen(label); + u8 extra[2]; + const unsigned char* addr[5]; + size_t len[5]; + + addr[0] = hash; + len[0] = 0; + addr[1] = (unsigned char*) label; + len[1] = label_len; + addr[2] = seed; + len[2] = seed_len; + + if (version == 0) { + /* + * PRF+(K, S, LEN) = T1 | T2 | ... | Tn + * T1 = HMAC-SHA1(K, S | 0x01 | 0x00 | 0x00) + * T2 = HMAC-SHA1(K, T1 | S | 0x02 | 0x00 | 0x00) + * ... + * Tn = HMAC-SHA1(K, Tn-1 | S | n | 0x00 | 0x00) + */ + + extra[0] = 0; + extra[1] = 0; + + addr[3] = &counter; + len[3] = 1; + addr[4] = extra; + len[4] = 2; + } else { + /* + * PRF (K,S,LEN) = T1 | T2 | T3 | T4 | ... where: + * T1 = HMAC-SHA1(K, S | LEN | 0x01) + * T2 = HMAC-SHA1 (K, T1 | S | LEN | 0x02) + * T3 = HMAC-SHA1 (K, T2 | S | LEN | 0x03) + * T4 = HMAC-SHA1 (K, T3 | S | LEN | 0x04) + * ... + */ + + extra[0] = buf_len & 0xff; + + addr[3] = extra; + len[3] = 1; + addr[4] = &counter; + len[4] = 1; + } + + pos = 0; + + while (pos < buf_len) { + counter++; + plen = buf_len - pos; + + if (hmac_sha1_vector(key, key_len, 5, addr, len, hash) < 0) { + return -1; + } + + if (plen >= SHA1_MAC_LEN) { + os_memcpy(&buf[pos], hash, SHA1_MAC_LEN); + pos += SHA1_MAC_LEN; + } else { + os_memcpy(&buf[pos], hash, plen); + break; + } + + len[0] = SHA1_MAC_LEN; + } + + return 0; +} + +#endif /* EAP_PEAP */ 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 100755 index 00000000..f4d95f7f --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/eap_peer/eap_tls.c @@ -0,0 +1,265 @@ +/* + * EAP peer method: EAP-TLS (RFC 2716) + * Copyright (c) 2004-2008, 2012, Jouni Malinen + * + * 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" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +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 100755 index 00000000..cc238163 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/eap_peer/eap_tls_common.c @@ -0,0 +1,1158 @@ +/* + * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions + * Copyright (c) 2004-2013, Jouni Malinen + * + * 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, ¶ms->ca_cert, ¶ms->ca_cert_blob, + ¶ms->ca_cert_blob_len) || + eap_tls_check_blob(sm, ¶ms->client_cert, + ¶ms->client_cert_blob, + ¶ms->client_cert_blob_len) || + eap_tls_check_blob(sm, ¶ms->private_key, + ¶ms->private_key_blob, + ¶ms->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, ¶ms, config) < 0) { /* no phase2 */ + return -1; + } + + if (eap_tls_init_connection(sm, data, config, ¶ms) < 0) { + return -1; + } + + data->tls_out_limit = config->fragment_size; + + if (config->phase1 && + os_strstr(config->phase1, "include_tls_length=1")) { + wpa_printf(MSG_DEBUG, "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_DEBUG, "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) { + 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)", + (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 = 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", *(u8*)(hdr + 1)); + 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 index 00000000..f43131a4 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/eap_peer/eap_ttls.c @@ -0,0 +1,1809 @@ +/* + * EAP peer method: EAP-TTLS (RFC 5281) + * Copyright (c) 2004-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifdef EAP_TTLS + +#include "wpa/includes.h" + +#include "wpa/common.h" +#include "crypto/ms_funcs.h" +#include "crypto/sha1.h" +#include "wpa2/tls/tls.h" +//#include "eap_common/chap.h" +#include "wpa2/eap_peer/eap.h" +#include "wpa2/eap_peer/eap_ttls.h" +#include "wpa2/eap_peer/mschapv2.h" +//#include "wpa2/eap_peer/chap.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" + + +#define EAP_TTLS_VERSION 0 + +static void eap_ttls_deinit(struct eap_sm* sm, void* priv); + +struct eap_ttls_data { + struct eap_ssl_data ssl; + + int ttls_version; + + const struct eap_method* phase2_method; + void* phase2_priv; + int phase2_success; + int phase2_start; + + enum phase2_types { + EAP_TTLS_PHASE2_EAP, + EAP_TTLS_PHASE2_MSCHAPV2, + EAP_TTLS_PHASE2_MSCHAP, + EAP_TTLS_PHASE2_PAP, + EAP_TTLS_PHASE2_CHAP + } phase2_type; + struct eap_method_type phase2_eap_type; + struct eap_method_type* phase2_eap_types; + size_t num_phase2_eap_types; + + u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN]; + int auth_response_valid; + u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; /* MSCHAPv2 master key */ + u8 ident; + int resuming; /* starting a resumed session */ + int reauth; /* reauthentication */ + u8* key_data; + u8* session_id; + size_t id_len; + + struct wpabuf* pending_phase2_req; + +#ifdef EAP_TNC + int ready_for_tnc; + int tnc_started; +#endif /* EAP_TNC */ +}; + + +static void* eap_ttls_init(struct eap_sm* sm) +{ + struct eap_ttls_data* data; + struct eap_peer_config* config = eap_get_config(sm); + //char *selected; + + data = (struct eap_ttls_data*)os_zalloc(sizeof(*data)); + + if (data == NULL) { + return NULL; + } + + data->ttls_version = EAP_TTLS_VERSION; + data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2; + + /* + selected = "MSCHAPV2"; + //TODO: Now only support EAP-TTLS/MSCHAPV2 + if (config && config->phase2) { + if (os_strstr(config->phase2, "autheap=")) { + selected = "EAP"; + data->phase2_type = EAP_TTLS_PHASE2_EAP; + } else if (os_strstr(config->phase2, "auth=MSCHAPV2")) { + selected = "MSCHAPV2"; + data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2; + } else if (os_strstr(config->phase2, "auth=MSCHAP")) { + selected = "MSCHAP"; + data->phase2_type = EAP_TTLS_PHASE2_MSCHAP; + } else if (os_strstr(config->phase2, "auth=PAP")) { + selected = "PAP"; + data->phase2_type = EAP_TTLS_PHASE2_PAP; + } else if (os_strstr(config->phase2, "auth=CHAP")) { + selected = "CHAP"; + data->phase2_type = EAP_TTLS_PHASE2_CHAP; + } + } + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected); + + if (data->phase2_type == EAP_TTLS_PHASE2_EAP) { + if (eap_peer_select_phase2_methods(config, "autheap=", + &data->phase2_eap_types, + &data->num_phase2_eap_types) + < 0) { + eap_ttls_deinit(sm, data); + return NULL; + } + + data->phase2_eap_type.vendor = EAP_VENDOR_IETF; + data->phase2_eap_type.method = EAP_TYPE_NONE; + } + */ + + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TTLS)) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to initialize SSL.\n"); + eap_ttls_deinit(sm, data); + return NULL; + } + + return data; +} + +static void eap_ttls_phase2_eap_deinit(struct eap_sm* sm, + struct eap_ttls_data* data) +{ + if (data->phase2_priv && data->phase2_method) { + data->phase2_method->deinit(sm, data->phase2_priv); + data->phase2_method = NULL; + data->phase2_priv = NULL; + } +} + +static void eap_ttls_deinit(struct eap_sm* sm, void* priv) +{ + struct eap_ttls_data* data = priv; + + if (data == NULL) { + return; + } + + eap_ttls_phase2_eap_deinit(sm, data); + os_free(data->phase2_eap_types); + eap_peer_tls_ssl_deinit(sm, &data->ssl); + os_free(data->key_data); + os_free(data->session_id); + wpabuf_free(data->pending_phase2_req); + os_free(data); +} + +static u8* eap_ttls_avp_hdr(u8* avphdr, u32 avp_code, u32 vendor_id, + int mandatory, size_t len) +{ + struct ttls_avp_vendor* avp; + u8 flags; + size_t hdrlen; + + avp = (struct ttls_avp_vendor*) avphdr; + flags = mandatory ? AVP_FLAGS_MANDATORY : 0; + + if (vendor_id) { + flags |= AVP_FLAGS_VENDOR; + hdrlen = sizeof(*avp); + avp->vendor_id = host_to_be32(vendor_id); + } else { + hdrlen = sizeof(struct ttls_avp); + } + + avp->avp_code = host_to_be32(avp_code); + avp->avp_length = host_to_be32((flags << 24) | (u32)(hdrlen + len)); + + return avphdr + hdrlen; +} + + +static u8* eap_ttls_avp_add(u8* start, u8* avphdr, u32 avp_code, + u32 vendor_id, int mandatory, + const u8* data, size_t len) +{ + u8* pos; + pos = eap_ttls_avp_hdr(avphdr, avp_code, vendor_id, mandatory, len); + os_memcpy(pos, data, len); + pos += len; + AVP_PAD(start, pos); + return pos; +} + +#if 0 +static int eap_ttls_avp_encapsulate(struct wpabuf** resp, u32 avp_code, + int mandatory) +{ + struct wpabuf* msg; + u8* avp, *pos; + + msg = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(*resp) + 4); + + if (msg == NULL) { + wpabuf_free(*resp); + *resp = NULL; + return -1; + } + + avp = wpabuf_mhead(msg); + pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, wpabuf_len(*resp)); + os_memcpy(pos, wpabuf_head(*resp), wpabuf_len(*resp)); + pos += wpabuf_len(*resp); + AVP_PAD(avp, pos); + wpabuf_free(*resp); + wpabuf_put(msg, pos - avp); + *resp = msg; + return 0; +} +#endif + +static int eap_ttls_v0_derive_key(struct eap_sm* sm, + struct eap_ttls_data* data) +{ + os_free(data->key_data); + data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, + "ttls keying material", + EAP_TLS_KEY_LEN); + + if (!data->key_data) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to derive key\n"); + return -1; + } + + os_free(data->session_id); + data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl, + EAP_TYPE_TTLS, + &data->id_len); + + if (data->session_id) { + } else { + wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to derive Session-Id\n"); + } + + return 0; +} + + +static u8* eap_ttls_implicit_challenge(struct eap_sm* sm, + struct eap_ttls_data* data, size_t len) +{ + return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len); +} + +#if 0 +static void eap_ttls_phase2_select_eap_method(struct eap_ttls_data* data, + u8 method) +{ + size_t i; + + for (i = 0; i < data->num_phase2_eap_types; i++) { + if (data->phase2_eap_types[i].vendor != EAP_VENDOR_IETF || + data->phase2_eap_types[i].method != method) { + continue; + } + + data->phase2_eap_type.vendor = + data->phase2_eap_types[i].vendor; + data->phase2_eap_type.method = + data->phase2_eap_types[i].method; + wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected " + "Phase 2 EAP vendor %d method %d\n", + data->phase2_eap_type.vendor, + data->phase2_eap_type.method); + break; + } +} + +static int eap_ttls_phase2_eap_process(struct eap_sm* sm, + struct eap_ttls_data* data, + struct eap_method_ret* ret, + struct eap_hdr* hdr, size_t len, + struct wpabuf** resp) +{ + struct wpabuf msg; + struct eap_method_ret iret; + + os_memset(&iret, 0, sizeof(iret)); + wpabuf_set(&msg, hdr, len); + *resp = data->phase2_method->process(sm, data->phase2_priv, &iret, + &msg); + + if ((iret.methodState == METHOD_DONE || + iret.methodState == METHOD_MAY_CONT) && + (iret.decision == DECISION_UNCOND_SUCC || + iret.decision == DECISION_COND_SUCC || + iret.decision == DECISION_FAIL)) { + ret->methodState = iret.methodState; + ret->decision = iret.decision; + } + + return 0; +} + + +static int eap_ttls_phase2_request_eap_method(struct eap_sm* sm, + struct eap_ttls_data* data, + struct eap_method_ret* ret, + struct eap_hdr* hdr, size_t len, + u8 method, struct wpabuf** resp) +{ +#ifdef EAP_TNC + + if (data->tnc_started && data->phase2_method && + data->phase2_priv && method == EAP_TYPE_TNC && + data->phase2_eap_type.method == EAP_TYPE_TNC) + return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, + resp); + + if (data->ready_for_tnc && !data->tnc_started && + method == EAP_TYPE_TNC) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed " + "EAP method\n"); + data->tnc_started = 1; + } + + if (data->tnc_started) { + if (data->phase2_eap_type.vendor != EAP_VENDOR_IETF || + data->phase2_eap_type.method == EAP_TYPE_TNC) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Unexpected EAP " + "type %d for TNC\n", method); + return -1; + } + + data->phase2_eap_type.vendor = EAP_VENDOR_IETF; + data->phase2_eap_type.method = method; + wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected " + "Phase 2 EAP vendor %d method %d (TNC)\n", + data->phase2_eap_type.vendor, + data->phase2_eap_type.method); + + if (data->phase2_type == EAP_TTLS_PHASE2_EAP) { + eap_ttls_phase2_eap_deinit(sm, data); + } + } + +#endif /* EAP_TNC */ + + if (data->phase2_eap_type.vendor == EAP_VENDOR_IETF && + data->phase2_eap_type.method == EAP_TYPE_NONE) { + eap_ttls_phase2_select_eap_method(data, method); + } + + if (method != data->phase2_eap_type.method || method == EAP_TYPE_NONE) { + return -1; + + if (eap_peer_tls_phase2_nak(data->phase2_eap_types, + data->num_phase2_eap_types, + hdr, resp)) { + return -1; + } + + return 0; + + } + + if (data->phase2_priv == NULL) { + data->phase2_method = eap_peer_get_eap_method( + EAP_VENDOR_IETF, method); + + if (data->phase2_method) { + sm->init_phase2 = 1; + data->phase2_priv = data->phase2_method->init(sm); + sm->init_phase2 = 0; + } + } + + if (data->phase2_priv == NULL || data->phase2_method == NULL) { + wpa_printf(MSG_ERROR, "EAP-TTLS: failed to initialize " + "Phase 2 EAP method %d\n", method); + return -1; + } + + return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, resp); +} + +#if 0 +static int eap_ttls_phase2_request_eap(struct eap_sm* sm, + struct eap_ttls_data* data, + struct eap_method_ret* ret, + struct eap_hdr* hdr, + struct wpabuf** resp) +{ + size_t len = be_to_host16(hdr->length); + u8* pos; + struct eap_peer_config* config = eap_get_config(sm); + + if (len <= sizeof(struct eap_hdr)) { + wpa_printf(MSG_ERROR, "EAP-TTLS: too short " + "Phase 2 request (len=%lu)\n", (unsigned long) len); + return -1; + } + + pos = (u8*)(hdr + 1); + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP Request: type=%d\n", *pos); + + switch (*pos) { + case EAP_TYPE_IDENTITY: + *resp = eap_sm_build_identity_resp(sm, hdr->identifier, 1); + break; + + default: + if (eap_ttls_phase2_request_eap_method(sm, data, ret, hdr, len, + *pos, resp) < 0) { + return -1; + } + + break; + } + + if (*resp == NULL && + (config->pending_req_identity || config->pending_req_password || + config->pending_req_otp)) { + return 0; + } + + if (*resp == NULL) { + return -1; + } + + return eap_ttls_avp_encapsulate(resp, RADIUS_ATTR_EAP_MESSAGE, 1); +} +#endif +#endif + +static int eap_ttls_phase2_request_mschapv2(struct eap_sm* sm, + struct eap_ttls_data* data, + struct eap_method_ret* ret, + struct wpabuf** resp) +{ +#ifdef EAP_MSCHAPv2 + struct wpabuf* msg; + u8* buf, *pos, *challenge, *peer_challenge; + const u8* identity, *password; + size_t identity_len, password_len; + int pwhash; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 Request\n"); + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password2(sm, &password_len, &pwhash); + + if (identity == NULL || password == NULL) { + return -1; + } + + msg = wpabuf_alloc(identity_len + 1000); + + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to allocate memory\n"); + return -1; + } + + pos = buf = wpabuf_mhead(msg); + + /* User-Name */ + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, + identity, identity_len); + + /* MS-CHAP-Challenge */ + challenge = eap_ttls_implicit_challenge( + sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1); + + if (challenge == NULL) { + wpabuf_free(msg); + wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " + "implicit challenge\n"); + return -1; + } + + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, + RADIUS_VENDOR_ID_MICROSOFT, 1, + challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); + + /* MS-CHAP2-Response */ + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_RESPONSE, + RADIUS_VENDOR_ID_MICROSOFT, 1, + EAP_TTLS_MSCHAPV2_RESPONSE_LEN); + data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]; + *pos++ = data->ident; + *pos++ = 0; /* Flags */ + + if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) { + os_free(challenge); + wpabuf_free(msg); + wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get " + "random data for peer challenge\n"); + return -1; + } + + peer_challenge = pos; + pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN; + os_memset(pos, 0, 8); /* Reserved, must be zero */ + pos += 8; + + if (mschapv2_derive_response(identity, identity_len, password, + password_len, pwhash, challenge, + peer_challenge, pos, data->auth_response, + data->master_key)) { + os_free(challenge); + wpabuf_free(msg); + wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " + "response\n"); + return -1; + } + + data->auth_response_valid = 1; + + pos += 24; + os_free(challenge); + AVP_PAD(buf, pos); + + wpabuf_put(msg, pos - buf); + *resp = msg; + + if (sm->workaround) { + /* At least FreeRADIUS seems to be terminating + * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success + * packet. */ + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: EAP workaround - " + "allow success without tunneled response\n"); + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_COND_SUCC; + } + + return 0; +#else /* EAP_MSCHAPv2 */ + printf("[Debug] Set EEEEE \n"); + wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build\n"); + return -1; +#endif /* EAP_MSCHAPv2 */ +} + +#if 0 +//only support MSCHAPv2 +static int eap_ttls_phase2_request_mschap(struct eap_sm* sm, + struct eap_ttls_data* data, + struct eap_method_ret* ret, + struct wpabuf** resp) +{ + struct wpabuf* msg; + u8* buf, *pos, *challenge; + const u8* identity, *password; + size_t identity_len, password_len; + int pwhash; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAP Request\n"); + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password2(sm, &password_len, &pwhash); + + if (identity == NULL || password == NULL) { + return -1; + } + + msg = wpabuf_alloc(identity_len + 1000); + + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to allocate memory\n"); + return -1; + } + + pos = buf = wpabuf_mhead(msg); + + // User-Name + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, + identity, identity_len); + + // MS-CHAP-Challenge + challenge = eap_ttls_implicit_challenge( + sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1); + + if (challenge == NULL) { + wpabuf_free(msg); + wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive " + "implicit challenge\n"); + return -1; + } + + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, + RADIUS_VENDOR_ID_MICROSOFT, 1, + challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN); + + // MS-CHAP-Response + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_RESPONSE, + RADIUS_VENDOR_ID_MICROSOFT, 1, + EAP_TTLS_MSCHAP_RESPONSE_LEN); + data->ident = challenge[EAP_TTLS_MSCHAP_CHALLENGE_LEN]; + *pos++ = data->ident; + *pos++ = 1; // Flags: Use NT style passwords + os_memset(pos, 0, 24); // LM-Response + pos += 24; + + if (pwhash) { + challenge_response(challenge, password, pos); // NT-Response + } else { + nt_challenge_response(challenge, password, password_len, + pos); // NT-Response + } + + pos += 24; + os_free(challenge); + AVP_PAD(buf, pos); + + wpabuf_put(msg, pos - buf); + *resp = msg; + + // EAP-TTLS/MSCHAP does not provide tunneled success + // notification, so assume that Phase2 succeeds. + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + + return 0; +} + + +static int eap_ttls_phase2_request_pap(struct eap_sm* sm, + struct eap_ttls_data* data, + struct eap_method_ret* ret, + struct wpabuf** resp) +{ + struct wpabuf* msg; + u8* buf, *pos; + size_t pad; + const u8* identity, *password; + size_t identity_len, password_len; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 PAP Request\n"); + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password(sm, &password_len); + + if (identity == NULL || password == NULL) { + return -1; + } + + msg = wpabuf_alloc(identity_len + password_len + 100); + + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-TTLS/PAP: Failed to allocate memory\n"); + return -1; + } + + pos = buf = wpabuf_mhead(msg); + + // User-Name + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, + identity, identity_len); + + // User-Password; in RADIUS, this is encrypted, but EAP-TTLS encrypts + // the data, so no separate encryption is used in the AVP itself. + // However, the password is padded to obfuscate its length. + pad = password_len == 0 ? 16 : (16 - (password_len & 15)) & 15; + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_USER_PASSWORD, 0, 1, + password_len + pad); + os_memcpy(pos, password, password_len); + pos += password_len; + os_memset(pos, 0, pad); + pos += pad; + AVP_PAD(buf, pos); + + wpabuf_put(msg, pos - buf); + *resp = msg; + + // EAP-TTLS/PAP does not provide tunneled success notification, + // so assume that Phase2 succeeds. + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + + return 0; +} + + +static int eap_ttls_phase2_request_chap(struct eap_sm* sm, + struct eap_ttls_data* data, + struct eap_method_ret* ret, + struct wpabuf** resp) +{ + struct wpabuf* msg; + u8* buf, *pos, *challenge; + const u8* identity, *password; + size_t identity_len, password_len; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 CHAP Request\n"); + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password(sm, &password_len); + + if (identity == NULL || password == NULL) { + return -1; + } + + msg = wpabuf_alloc(identity_len + 1000); + + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to allocate memory\n"); + return -1; + } + + pos = buf = wpabuf_mhead(msg); + + // User-Name + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, + identity, identity_len); + + // CHAP-Challenge + challenge = eap_ttls_implicit_challenge( + sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1); + + if (challenge == NULL) { + wpabuf_free(msg); + wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive " + "implicit challenge\n"); + return -1; + } + + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_CHAP_CHALLENGE, 0, 1, + challenge, EAP_TTLS_CHAP_CHALLENGE_LEN); + + // CHAP-Password + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_CHAP_PASSWORD, 0, 1, + 1 + EAP_TTLS_CHAP_PASSWORD_LEN); + data->ident = challenge[EAP_TTLS_CHAP_CHALLENGE_LEN]; + *pos++ = data->ident; + + // MD5(Ident + Password + Challenge) + chap_md5(data->ident, password, password_len, challenge, + EAP_TTLS_CHAP_CHALLENGE_LEN, pos); + + pos += EAP_TTLS_CHAP_PASSWORD_LEN; + os_free(challenge); + AVP_PAD(buf, pos); + + wpabuf_put(msg, pos - buf); + *resp = msg; + + // EAP-TTLS/CHAP does not provide tunneled success + // notification, so assume that Phase2 succeeds. + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + + return 0; +} +#endif + +static int eap_ttls_phase2_request(struct eap_sm* sm, + struct eap_ttls_data* data, + struct eap_method_ret* ret, + struct eap_hdr* hdr, + struct wpabuf** resp) +{ + int res = 0; + size_t len; + enum phase2_types phase2_type = data->phase2_type; + +#ifdef EAP_TNC + + if (data->tnc_started) { + printf("[debug] set phase2_type \n"); + wpa_printf(MSG_DEBUG, "EAP-TTLS: Processing TNC\n"); + phase2_type = EAP_TTLS_PHASE2_EAP; + } + +#endif /* EAP_TNC */ + + if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2) { + if (eap_get_config_identity(sm, &len) == NULL) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Identity not configured\n"); + printf("[Debug] Return because no identity EAP_TTLS_PHASE2_MSCHAPV2\n"); + return 0; + } + + if (eap_get_config_password(sm, &len) == NULL) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Password not configured\n"); + printf("[Debug] Return because no password EAP_TTLS_PHASE2_MSCHAPV2\n"); + return 0; + } + + res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp); + } else { + wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown type %d\n", phase2_type); + res = -1; + } + + /* if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2 || + phase2_type == EAP_TTLS_PHASE2_MSCHAP || + phase2_type == EAP_TTLS_PHASE2_PAP || + phase2_type == EAP_TTLS_PHASE2_CHAP) { + if (eap_get_config_identity(sm, &len) == NULL) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Identity not configured\n"); + //eap_sm_request_identity(sm); + if (eap_get_config_password(sm, &len) == NULL); + // eap_sm_request_password(sm); + printf("[Debug] Return because no identity EAP_TTLS_PHASE2_MSCHAPV2 EAP_TTLS_PHASE2_MSCHAP\n"); + return 0; + } + + if (eap_get_config_password(sm, &len) == NULL) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Password not configured\n"); + printf("[Debug] Return because no password EAP_TTLS_PHASE2_MSCHAPV2 EAP_TTLS_PHASE2_MSCHAP\n"); + //eap_sm_request_password(sm); + return 0; + } + } + + switch (phase2_type) { + case EAP_TTLS_PHASE2_EAP: + printf("[Debug] EAP_TTLS_PHASE2_EAP typed \n"); + res = eap_ttls_phase2_request_eap(sm, data, ret, hdr, resp); + break; + case EAP_TTLS_PHASE2_MSCHAPV2: + printf("[Debug] EAP_TTLS_PHASE2_MSCHAPV2 typed \n"); + res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp); + break; + case EAP_TTLS_PHASE2_MSCHAP: + printf("[Debug] EAP_TTLS_PHASE2_MSCHAP typed \n"); + res = eap_ttls_phase2_request_mschap(sm, data, ret, resp); + break; + case EAP_TTLS_PHASE2_PAP: + printf("[Debug] EAP_TTLS_PHASE2_PAP typed \n"); + res = eap_ttls_phase2_request_pap(sm, data, ret, resp); + break; + case EAP_TTLS_PHASE2_CHAP: + printf("[Debug] EAP_TTLS_PHASE2_CHAP typed \n"); + res = eap_ttls_phase2_request_chap(sm, data, ret, resp); + break; + default: + printf("[Debug] Default typed \n"); + wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown\n"); + res = -1; + break; + }*/ + + if (res < 0) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + } + + return res; +} + + +struct ttls_parse_avp { + u8* mschapv2; + u8* eapdata; + size_t eap_len; + int mschapv2_error; +}; + + +static int eap_ttls_parse_attr_eap(const u8* dpos, size_t dlen, + struct ttls_parse_avp* parse) +{ + wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message\n"); + + if (parse->eapdata == NULL) { + parse->eapdata = os_malloc(dlen); + + if (parse->eapdata == NULL) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to allocate " + "memory for Phase 2 EAP data\n"); + return -1; + } + + os_memcpy(parse->eapdata, dpos, dlen); + parse->eap_len = dlen; + } else { + u8* neweap = (u8*)os_realloc(parse->eapdata, parse->eap_len + dlen); + + if (neweap == NULL) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to allocate " + "memory for Phase 2 EAP data\n"); + return -1; + } + + os_memcpy(neweap + parse->eap_len, dpos, dlen); + parse->eapdata = neweap; + parse->eap_len += dlen; + } + + return 0; +} + + +static int eap_ttls_parse_avp(u8* pos, size_t left, + struct ttls_parse_avp* parse) +{ + struct ttls_avp* avp; + u32 avp_code, avp_length, vendor_id = 0; + u8 avp_flags, *dpos; + size_t dlen; + + avp = (struct ttls_avp*) pos; + avp_code = be_to_host32(avp->avp_code); + avp_length = be_to_host32(avp->avp_length); + avp_flags = (avp_length >> 24) & 0xff; + avp_length &= 0xffffff; + wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x " + "length=%d\n", (int) avp_code, avp_flags, + (int) avp_length); + + if (avp_length > left) { + wpa_printf(MSG_ERROR, "EAP-TTLS: AVP overflow " + "(len=%d, left=%lu) - dropped\n", + (int) avp_length, (unsigned long) left); + return -1; + } + + if (avp_length < sizeof(*avp)) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Invalid AVP length %d\n", + avp_length); + return -1; + } + + dpos = (u8*)(avp + 1); + dlen = avp_length - sizeof(*avp); + + if (avp_flags & AVP_FLAGS_VENDOR) { + if (dlen < 4) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Vendor AVP underflow\n"); + return -1; + } + + vendor_id = WPA_GET_BE32(dpos); + wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d\n", + (int) vendor_id); + dpos += 4; + dlen -= 4; + } + + if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) { + if (eap_ttls_parse_attr_eap(dpos, dlen, parse) < 0) { + return -1; + } + } else if (vendor_id == 0 && avp_code == RADIUS_ATTR_REPLY_MESSAGE) { + /* This is an optional message that can be displayed to + * the user. */ + } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && + avp_code == RADIUS_ATTR_MS_CHAP2_SUCCESS) { + if (dlen != 43) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Unexpected " + "MS-CHAP2-Success length " + "(len=%lu, expected 43)\n", + (unsigned long) dlen); + return -1; + } + + parse->mschapv2 = dpos; + } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && + avp_code == RADIUS_ATTR_MS_CHAP_ERROR) { + parse->mschapv2_error = 1; + } else if (avp_flags & AVP_FLAGS_MANDATORY) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Unsupported mandatory AVP " + "code %d vendor_id %d - dropped\n", + (int) avp_code, (int) vendor_id); + return -1; + } else { + wpa_printf(MSG_INFO, "EAP-TTLS: Ignoring unsupported AVP " + "code %d vendor_id %d\n", + (int) avp_code, (int) vendor_id); + } + + return avp_length; +} + + +static int eap_ttls_parse_avps(struct wpabuf* in_decrypted, + struct ttls_parse_avp* parse) +{ + u8* pos; + size_t left, pad; + int avp_length; + + pos = wpabuf_mhead(in_decrypted); + left = wpabuf_len(in_decrypted); + + if (left < sizeof(struct ttls_avp)) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Too short Phase 2 AVP frame" + " len=%lu expected %lu or more - dropped\n", + (unsigned long) left, + (unsigned long) sizeof(struct ttls_avp)); + return -1; + } + + /* Parse AVPs */ + os_memset(parse, 0, sizeof(*parse)); + + while (left > 0) { + avp_length = eap_ttls_parse_avp(pos, left, parse); + + if (avp_length < 0) { + return -1; + } + + pad = (4 - (avp_length & 3)) & 3; + pos += avp_length + pad; + + if (left < avp_length + pad) { + left = 0; + } else { + left -= avp_length + pad; + } + } + + return 0; +} + + +static u8* eap_ttls_fake_identity_request(void) +{ + struct eap_hdr* hdr; + u8* buf; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: empty data in beginning of " + "Phase 2 - use fake EAP-Request Identity\n"); + buf = os_malloc(sizeof(*hdr) + 1); + + if (buf == NULL) { + wpa_printf(MSG_ERROR, "EAP-TTLS: failed to allocate " + "memory for fake EAP-Identity Request\n"); + return NULL; + } + + hdr = (struct eap_hdr*) buf; + hdr->code = EAP_CODE_REQUEST; + hdr->identifier = 0; + hdr->length = host_to_be16(sizeof(*hdr) + 1); + buf[sizeof(*hdr)] = EAP_TYPE_IDENTITY; + + return buf; +} + + +static int eap_ttls_encrypt_response(struct eap_sm* sm, + struct eap_ttls_data* data, + struct wpabuf* resp, u8 identifier, + struct wpabuf** out_data) +{ + if (resp == NULL) { + return 0; + } + + if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS, + data->ttls_version, identifier, + resp, out_data)) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to encrypt a Phase 2 frame\n"); + return -1; + } + + wpabuf_free(resp); + + return 0; +} + +#if 0 +static int eap_ttls_process_phase2_eap(struct eap_sm* sm, + struct eap_ttls_data* data, + struct eap_method_ret* ret, + struct ttls_parse_avp* parse, + struct wpabuf** resp) +{ + struct eap_hdr* hdr; + size_t len; + + if (parse->eapdata == NULL) { + wpa_printf(MSG_ERROR, "EAP-TTLS: No EAP Message in the " + "packet - dropped\n"); + return -1; + } + + hdr = (struct eap_hdr*) parse->eapdata; + + if (parse->eap_len < sizeof(*hdr)) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Too short Phase 2 EAP " + "frame (len=%lu, expected %lu or more) - dropped\n", + (unsigned long) parse->eap_len, + (unsigned long) sizeof(*hdr)); + return -1; + } + + len = be_to_host16(hdr->length); + + if (len > parse->eap_len) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Length mismatch in Phase 2 " + "EAP frame (EAP hdr len=%lu, EAP data len in " + "AVP=%lu)\n", + (unsigned long) len, + (unsigned long) parse->eap_len); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-TTLS: received Phase 2: code=%d " + "identifier=%d length=%lu\n", + hdr->code, hdr->identifier, (unsigned long) len); + + switch (hdr->code) { + case EAP_CODE_REQUEST: + if (eap_ttls_phase2_request(sm, data, ret, hdr, resp)) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Phase2 Request " + "processing failed\n"); + return -1; + } + + break; + + default: + wpa_printf(MSG_ERROR, "EAP-TTLS: Unexpected code=%d in " + "Phase 2 EAP header\n", hdr->code); + return -1; + } + + return 0; +} +#endif + +static int eap_ttls_process_phase2_mschapv2(struct eap_sm* sm, + struct eap_ttls_data* data, + struct eap_method_ret* ret, + struct ttls_parse_avp* parse) +{ +#ifdef EAP_MSCHAPv2 + + if (parse->mschapv2_error) { + wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Received " + "MS-CHAP-Error - failed\n"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + /* Reply with empty data to ACK error */ + return 1; + } + + if (parse->mschapv2 == NULL) { +#ifdef EAP_TNC + + if (data->phase2_success && parse->eapdata) { + /* + * Allow EAP-TNC to be started after successfully + * completed MSCHAPV2. + */ + return 1; + } + +#endif /* EAP_TNC */ + wpa_printf(MSG_ERROR, "EAP-TTLS: no MS-CHAP2-Success AVP " + "received for Phase2 MSCHAPV2\n"); + return -1; + } + + if (parse->mschapv2[0] != data->ident) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Ident mismatch for Phase 2 " + "MSCHAPV2 (received Ident 0x%02x, expected 0x%02x)\n", + parse->mschapv2[0], data->ident); + return -1; + } + + if (!data->auth_response_valid || + mschapv2_verify_auth_response(data->auth_response, + parse->mschapv2 + 1, 42)) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Invalid authenticator " + "response in Phase 2 MSCHAPV2 success request\n"); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 " + "authentication succeeded\n"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + data->phase2_success = 1; + + /* + * Reply with empty data; authentication server will reply + * with EAP-Success after this. + */ + return 1; +#else /* EAP_MSCHAPv2 */ + wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build\n"); + return -1; +#endif /* EAP_MSCHAPv2 */ +} + + +#ifdef EAP_TNC +static int eap_ttls_process_tnc_start(struct eap_sm* sm, + struct eap_ttls_data* data, + struct eap_method_ret* ret, + struct ttls_parse_avp* parse, + struct wpabuf** resp) +{ + /* TNC uses inner EAP method after non-EAP TTLS phase 2. */ + if (parse->eapdata == NULL) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 received " + "unexpected tunneled data (no EAP)\n"); + return -1; + } + + if (!data->ready_for_tnc) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 received " + "EAP after non-EAP, but not ready for TNC\n"); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed " + "non-EAP method\n"); + data->tnc_started = 1; + + if (eap_ttls_process_phase2_eap(sm, data, ret, parse, resp) < 0) { + return -1; + } + + return 0; +} +#endif /* EAP_TNC */ + + +static int eap_ttls_process_decrypted(struct eap_sm* sm, + struct eap_ttls_data* data, + struct eap_method_ret* ret, + u8 identifier, + struct ttls_parse_avp* parse, + struct wpabuf* in_decrypted, + struct wpabuf** out_data) +{ + struct wpabuf* resp = NULL; + //struct eap_peer_config *config = eap_get_config(sm); + int res; + enum phase2_types phase2_type = data->phase2_type; + +#ifdef EAP_TNC + + if (data->tnc_started) { + phase2_type = EAP_TTLS_PHASE2_EAP; + } + +#endif /* EAP_TNC */ + + switch (phase2_type) { + case EAP_TTLS_PHASE2_MSCHAPV2: + res = eap_ttls_process_phase2_mschapv2(sm, data, ret, parse); +#ifdef EAP_TNC + + if (res == 1 && parse->eapdata && data->phase2_success) { + /* + * TNC may be required as the next + * authentication method within the tunnel. + */ + ret->methodState = METHOD_MAY_CONT; + data->ready_for_tnc = 1; + + if (eap_ttls_process_tnc_start(sm, data, ret, parse, + &resp) == 0) { + break; + } + } + +#endif /* EAP_TNC */ + return res; + + /* case EAP_TTLS_PHASE2_EAP: + if (eap_ttls_process_phase2_eap(sm, data, ret, parse, &resp) < + 0) + return -1; + break; + case EAP_TTLS_PHASE2_MSCHAP: + case EAP_TTLS_PHASE2_PAP: + case EAP_TTLS_PHASE2_CHAP: + #ifdef EAP_TNC + if (eap_ttls_process_tnc_start(sm, data, ret, parse, &resp) < + 0) + return -1; + break; + #else // EAP_TNC + // EAP-TTLS/{MSCHAP,PAP,CHAP} should not send any TLS tunneled + // requests to the supplicant + wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 received unexpected " + "tunneled data\n"); + return -1; + #endif // EAP_TNC + */ + default: + return -1; + } + + if (resp) { + if (eap_ttls_encrypt_response(sm, data, resp, identifier, + out_data) < 0) { + return -1; + } + } else { + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = wpabuf_dup(in_decrypted); + }/* else if (config->pending_req_identity || + + config->pending_req_password || + config->pending_req_otp || + config->pending_req_new_password) { + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = wpabuf_dup(in_decrypted); + }*/ + + return 0; +} + + +static int eap_ttls_implicit_identity_request(struct eap_sm* sm, + struct eap_ttls_data* data, + struct eap_method_ret* ret, + u8 identifier, + struct wpabuf** out_data) +{ + int retval = 0; + struct eap_hdr* hdr; + struct wpabuf* resp; + + hdr = (struct eap_hdr*) eap_ttls_fake_identity_request(); + + if (hdr == NULL) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + + resp = NULL; + + if (eap_ttls_phase2_request(sm, data, ret, hdr, &resp)) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Phase2 Request " + "processing failed\n"); + retval = -1; + } else { + //struct eap_peer_config *config = eap_get_config(sm); + if (resp == NULL) { + /* && + (config->pending_req_identity || + config->pending_req_password || + config->pending_req_otp || + config->pending_req_new_password)) {*/ + /* + * Use empty buffer to force implicit request + * processing when EAP request is re-processed after + * user input. + */ + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = wpabuf_alloc(0); + } + + retval = eap_ttls_encrypt_response(sm, data, resp, identifier, + out_data); + } + + os_free(hdr); + + if (retval < 0) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + } + + return retval; +} + + +static int eap_ttls_phase2_start(struct eap_sm* sm, struct eap_ttls_data* data, + struct eap_method_ret* ret, u8 identifier, + struct wpabuf** out_data) +{ + data->phase2_start = 0; + + /* + * EAP-TTLS does not use Phase2 on fast re-auth; this must be done only + * if TLS part was indeed resuming a previous session. Most + * Authentication Servers terminate EAP-TTLS before reaching this + * point, but some do not. Make wpa_supplicant stop phase 2 here, if + * needed. + */ + if (data->reauth && + tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Session resumption - " + "skip phase 2\n"); + *out_data = eap_peer_tls_build_ack(identifier, EAP_TYPE_TTLS, + data->ttls_version); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + data->phase2_success = 1; + return 0; + } + + return eap_ttls_implicit_identity_request(sm, data, ret, identifier, + out_data); +} + + +static int eap_ttls_decrypt(struct eap_sm* sm, struct eap_ttls_data* data, + struct eap_method_ret* ret, u8 identifier, + const struct wpabuf* in_data, + struct wpabuf** out_data) +{ + struct wpabuf* in_decrypted = NULL; + int retval = 0; + struct ttls_parse_avp parse; + + os_memset(&parse, 0, sizeof(parse)); + + wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for" + " Phase 2\n", + in_data ? (unsigned long) wpabuf_len(in_data) : 0); + + if (data->pending_phase2_req) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 request - " + "skip decryption and use old data\n"); + /* Clear TLS reassembly state. */ + eap_peer_tls_reset_input(&data->ssl); + + in_decrypted = data->pending_phase2_req; + data->pending_phase2_req = NULL; + + if (wpabuf_len(in_decrypted) == 0) { + wpabuf_free(in_decrypted); + return eap_ttls_implicit_identity_request( + sm, data, ret, identifier, out_data); + } + + goto continue_req; + } + + if ((in_data == NULL || wpabuf_len(in_data) == 0) && + data->phase2_start) { + return eap_ttls_phase2_start(sm, data, ret, identifier, + out_data); + } + + if (in_data == NULL || wpabuf_len(in_data) == 0) { + /* Received TLS ACK - requesting more fragments */ + return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS, + data->ttls_version, + identifier, NULL, out_data); + } + + retval = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); + + if (retval) { + goto done; + } + +continue_req: + data->phase2_start = 0; + + if (eap_ttls_parse_avps(in_decrypted, &parse) < 0) { + retval = -1; + goto done; + } + + retval = eap_ttls_process_decrypted(sm, data, ret, identifier, + &parse, in_decrypted, out_data); + +done: + wpabuf_free(in_decrypted); + os_free(parse.eapdata); + + if (retval < 0) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + } + + return retval; +} + + +static int eap_ttls_process_handshake(struct eap_sm* sm, + struct eap_ttls_data* data, + struct eap_method_ret* ret, + u8 identifier, + const u8* in_data, size_t in_len, + struct wpabuf** out_data) +{ + int res; + + res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS, + data->ttls_version, identifier, + in_data, in_len, out_data); + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to " + "Phase 2\n"); + + if (data->resuming) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: fast reauth - may " + "skip Phase 2\n"); + ret->decision = DECISION_COND_SUCC; + ret->methodState = METHOD_MAY_CONT; + } + + data->phase2_start = 1; + eap_ttls_v0_derive_key(sm, data); + + if (*out_data == NULL || wpabuf_len(*out_data) == 0) { + if (eap_ttls_decrypt(sm, data, ret, identifier, + NULL, out_data)) { + wpa_printf(MSG_ERROR, "EAP-TTLS: " + "failed to process early " + "start for Phase 2\n"); + } + + res = 0; + } + + data->resuming = 0; + } + + if (res == 2) { + struct wpabuf msg; + /* + * Application data included in the handshake message. + */ + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = *out_data; + *out_data = NULL; + wpabuf_set(&msg, in_data, in_len); + res = eap_ttls_decrypt(sm, data, ret, identifier, &msg, + out_data); + } + + return res; +} + + +static void eap_ttls_check_auth_status(struct eap_sm* sm, + struct eap_ttls_data* data, + struct eap_method_ret* ret) +{ + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + + if (ret->decision == DECISION_UNCOND_SUCC || + ret->decision == DECISION_COND_SUCC) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " + "completed successfully"); + data->phase2_success = 1; +#ifdef EAP_TNC + + if (!data->ready_for_tnc && !data->tnc_started) { + /* + * TNC may be required as the next + * authentication method within the tunnel. + */ + ret->methodState = METHOD_MAY_CONT; + data->ready_for_tnc = 1; + } + +#endif /* EAP_TNC */ + } + } else if (ret->methodState == METHOD_MAY_CONT && + (ret->decision == DECISION_UNCOND_SUCC || + ret->decision == DECISION_COND_SUCC)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " + "completed successfully (MAY_CONT)\n"); + data->phase2_success = 1; + } +} + + +static struct wpabuf* eap_ttls_process(struct eap_sm* sm, void* priv, + struct eap_method_ret* ret, + const struct wpabuf* reqData) +{ + size_t left; + int res; + u8 flags, id; + struct wpabuf* resp; + const u8* pos; + struct eap_ttls_data* data = priv; + + pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, 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-TTLS: Start (server ver=%d, own " + "ver=%d)\n", flags & EAP_TLS_VERSION_MASK, + data->ttls_version); + + /* RFC 5281, Ch. 9.2: + * "This packet MAY contain additional information in the form + * of AVPs, which may provide useful hints to the client" + * For now, ignore any potential extra data. + */ + left = 0; + } + + resp = NULL; + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + !data->resuming) { + struct wpabuf msg; + wpabuf_set(&msg, pos, left); + res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp); + } else { + res = eap_ttls_process_handshake(sm, data, ret, id, + pos, left, &resp); + } + + eap_ttls_check_auth_status(sm, data, ret); + + /* FIX: what about res == -1? Could just move all error processing into + * the other functions and get rid of this res==1 case here. */ + if (res == 1) { + wpabuf_free(resp); + return eap_peer_tls_build_ack(id, EAP_TYPE_TTLS, + data->ttls_version); + } + + return resp; +} + + +static bool eap_ttls_has_reauth_data(struct eap_sm* sm, void* priv) +{ + struct eap_ttls_data* data = priv; + return tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + data->phase2_success; +} + + +static void eap_ttls_deinit_for_reauth(struct eap_sm* sm, void* priv) +{ + struct eap_ttls_data* data = priv; + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = NULL; +#ifdef EAP_TNC + data->ready_for_tnc = 0; + data->tnc_started = 0; +#endif /* EAP_TNC */ +} + + +static void* eap_ttls_init_for_reauth(struct eap_sm* sm, void* priv) +{ + struct eap_ttls_data* data = priv; + os_free(data->key_data); + data->key_data = NULL; + os_free(data->session_id); + data->session_id = NULL; + + if (eap_peer_tls_reauth_init(sm, &data->ssl)) { + os_free(data); + return NULL; + } + + if (data->phase2_priv && data->phase2_method && + data->phase2_method->init_for_reauth) { + data->phase2_method->init_for_reauth(sm, data->phase2_priv); + } + + data->phase2_start = 0; + data->phase2_success = 0; + data->resuming = 1; + data->reauth = 1; + return priv; +} + + +static int eap_ttls_get_status(struct eap_sm* sm, void* priv, char* buf, + size_t buflen, int verbose) +{ + struct eap_ttls_data* data = priv; + int len, ret; + + len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); + ret = snprintf(buf + len, buflen - len, + "EAP-TTLSv%d Phase2 method=", + data->ttls_version); + + if (ret < 0 || (size_t) ret >= buflen - len) { + return len; + } + + len += ret; + + switch (data->phase2_type) { + /*case EAP_TTLS_PHASE2_EAP: + ret = os_snprintf(buf + len, buflen - len, "EAP-%s\n", + data->phase2_method ? + data->phase2_method->name : "?"); + break;*/ + case EAP_TTLS_PHASE2_MSCHAPV2: + ret = snprintf(buf + len, buflen - len, "MSCHAPV2\n"); + break; + + /*case EAP_TTLS_PHASE2_MSCHAP: + ret = os_snprintf(buf + len, buflen - len, "MSCHAP\n"); + break; + case EAP_TTLS_PHASE2_PAP: + ret = os_snprintf(buf + len, buflen - len, "PAP\n"); + break; + case EAP_TTLS_PHASE2_CHAP: + ret = os_snprintf(buf + len, buflen - len, "CHAP\n"); + break;*/ + default: + ret = 0; + break; + } + + if (ret < 0 || (size_t) ret >= buflen - len) { + return len; + } + + len += ret; + + return len; +} + + +static bool eap_ttls_isKeyAvailable(struct eap_sm* sm, void* priv) +{ + struct eap_ttls_data* data = priv; + return data->key_data != NULL && data->phase2_success; +} + + +static u8* eap_ttls_getKey(struct eap_sm* sm, void* priv, size_t* len) +{ + struct eap_ttls_data* data = priv; + u8* key; + + if (data->key_data == NULL || !data->phase2_success) { + 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; +} + + +static u8* eap_ttls_get_session_id(struct eap_sm* sm, void* priv, size_t* len) +{ + struct eap_ttls_data* data = priv; + u8* id; + + if (data->session_id == NULL || !data->phase2_success) { + return NULL; + } + + id = os_malloc(data->id_len); + + if (id == NULL) { + return NULL; + } + + *len = data->id_len; + os_memcpy(id, data->session_id, data->id_len); + + return id; +} + +int eap_peer_ttls_register(void) +{ + struct eap_method* eap; + int ret; + + eap = eap_peer_method_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, + "TTLS"); + + if (eap == NULL) { + return -1; + } + + eap->init = eap_ttls_init; + eap->deinit = eap_ttls_deinit; + eap->process = eap_ttls_process; + eap->isKeyAvailable = eap_ttls_isKeyAvailable; + eap->getKey = eap_ttls_getKey; + eap->getSessionId = eap_ttls_get_session_id; + eap->get_status = eap_ttls_get_status; + eap->has_reauth_data = eap_ttls_has_reauth_data; + eap->deinit_for_reauth = eap_ttls_deinit_for_reauth; + eap->init_for_reauth = eap_ttls_init_for_reauth; + + ret = eap_peer_method_register(eap); + + if (ret) { + eap_peer_method_free(eap); + } + + return ret; +} + +#endif /* EAP_TTLS */ \ No newline at end of file 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 index 00000000..86a5d1c0 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/eap_peer/mschapv2.c @@ -0,0 +1,106 @@ +/* + * 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 */ \ No newline at end of file diff --git a/components/wpa_supplicant/src/wpa2/tls/asn1.c b/components/wpa_supplicant/src/wpa2/tls/asn1.c new file mode 100755 index 00000000..fc3b121d --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/tls/asn1.c @@ -0,0 +1,239 @@ +/* + * ASN.1 DER parsing + * Copyright (c) 2006, Jouni Malinen + * + * 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_MSGDUMP, "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 = 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 index 00000000..434355c3 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/tls/bignum.c @@ -0,0 +1,260 @@ +/* + * Big number math + * Copyright (c) 2006, Jouni Malinen + * + * 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 "lwip/mem.h" +#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 +#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 100755 index 00000000..34acb741 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/tls/pkcs1.c @@ -0,0 +1,221 @@ +/* + * PKCS #1 (RSA Encryption) + * Copyright (c) 2006-2009, Jouni Malinen + * + * 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 100755 index 00000000..a3236ff8 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/tls/pkcs5.c @@ -0,0 +1,254 @@ +/* + * PKCS #5 (Password-based Encryption) + * Copyright (c) 2009, Jouni Malinen + * + * 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" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +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); + + return crypto_cipher_init(CRYPTO_CIPHER_ALG_DES, hash + 8, hash, 8); +} + + +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; + u8* eb, pad; + struct pkcs5_params params; + unsigned int i; + + if (pkcs5_get_params(enc_alg, enc_alg_len, ¶ms) < 0) { + wpa_printf(MSG_DEBUG, "PKCS #5: Unsupported parameters"); + return NULL; + } + + ctx = pkcs5_crypto_init(¶ms, 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); + crypto_cipher_deinit(ctx); + return NULL; + } + + eb = os_malloc(enc_data_len); + + if (eb == NULL) { + crypto_cipher_deinit(ctx); + return NULL; + } + + if (crypto_cipher_decrypt(ctx, enc_data, eb, enc_data_len) < 0) { + wpa_printf(MSG_DEBUG, "PKCS #5: Failed to decrypt EB"); + crypto_cipher_deinit(ctx); + os_free(eb); + return NULL; + } + + crypto_cipher_deinit(ctx); + + 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 100755 index 00000000..f1834960 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/tls/pkcs8.c @@ -0,0 +1,200 @@ +/* + * PKCS #8 (Private-key information syntax) + * Copyright (c) 2006-2009, Jouni Malinen + * + * 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" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +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 100755 index 00000000..5ad7d169 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/tls/rsa.c @@ -0,0 +1,390 @@ +/* + * RSA + * Copyright (c) 2006, Jouni Malinen + * + * 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 "esp_system.h" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +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; + } + + esp_set_cpu_freq(ESP_CPU_FREQ_160M); + + 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: + esp_set_cpu_freq(ESP_CPU_FREQ_80M); + + 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 100755 index 00000000..51bc2906 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/tls/tls_internal.c @@ -0,0 +1,855 @@ +/* + * TLS interface functions and an internal TLS implementation + * Copyright (c) 2004-2011, Jouni Malinen + * + * 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 + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#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 100755 index 00000000..4eeb348b --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/tls/tlsv1_client.c @@ -0,0 +1,857 @@ +/* + * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) + * Copyright (c) 2006-2011, Jouni Malinen + * + * 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" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +/* 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) +{ + 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; +} + + +/** + * 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 100755 index 00000000..0a2f3424 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/tls/tlsv1_client_read.c @@ -0,0 +1,1117 @@ +/* + * TLSv1 client - read handshake message + * Copyright (c) 2006-2011, Jouni Malinen + * + * 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" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +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; + } + + 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 (conn->verify.sha256_server == NULL || + 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; + } + + 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)) { + 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 100755 index 00000000..50bc4791 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/tls/tlsv1_client_write.c @@ -0,0 +1,945 @@ +/* + * TLSv1 client - write handshake message + * Copyright (c) 2006-2011, Jouni Malinen + * + * 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" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +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 (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; + } + + 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 (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; + } + + 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 (conn->verify.sha256_cert == NULL || + 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; + } + + 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 (conn->verify.sha256_client == NULL || + 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; + } + + 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 100755 index 00000000..11a9a691 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/tls/tlsv1_common.c @@ -0,0 +1,414 @@ +/* + * TLSv1 common routines + * Copyright (c) 2006-2011, Jouni Malinen + * + * 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" + + +/* + * 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 + verify->sha256_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, + 0); + verify->sha256_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, + 0); + verify->sha256_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, + 0); + + 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 (verify->sha256_client) { + crypto_hash_update(verify->sha256_client, buf, len); + } + + if (verify->sha256_server) { + crypto_hash_update(verify->sha256_server, buf, len); + } + + if (verify->sha256_cert) { + crypto_hash_update(verify->sha256_cert, buf, len); + } + +#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 + crypto_hash_finish(verify->sha256_client, NULL, NULL); + crypto_hash_finish(verify->sha256_server, NULL, NULL); + crypto_hash_finish(verify->sha256_cert, NULL, NULL); + verify->sha256_client = NULL; + verify->sha256_server = NULL; + verify->sha256_cert = NULL; +#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 100755 index 00000000..72db6de6 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/tls/tlsv1_cred.c @@ -0,0 +1,571 @@ +/* + * TLSv1 credentials + * Copyright (c) 2006-2009, Jouni Malinen + * + * 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" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + + +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 = 0; + 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 = 0; + 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 = 0; + 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 100755 index 00000000..6ea5de6a --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/tls/tlsv1_record.c @@ -0,0 +1,521 @@ +/* + * TLSv1 Record Protocol + * Copyright (c) 2006-2011, Jouni Malinen + * + * 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" + + +/** + * 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) { + crypto_cipher_deinit(rl->write_cbc); + rl->write_cbc = NULL; + } + + if (rl->cipher_alg != CRYPTO_CIPHER_NULL) { + rl->write_cbc = crypto_cipher_init(rl->cipher_alg, + rl->write_iv, rl->write_key, + rl->key_material_len); + + 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", 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) { + crypto_cipher_deinit(rl->read_cbc); + rl->read_cbc = NULL; + } + + if (rl->cipher_alg != CRYPTO_CIPHER_NULL) { + rl->read_cbc = crypto_cipher_init(rl->cipher_alg, + rl->read_iv, rl->read_key, + rl->key_material_len); + + 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; + 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 + */ + hmac = crypto_hash_init(rl->hash_alg, rl->write_mac_secret, + rl->hash_size); + + if (hmac == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " + "to initialize HMAC"); + return -1; + } + + crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN); + /* type + version + length + fragment */ + crypto_hash_update(hmac, ct_start, TLS_RECORD_HEADER_LEN); + crypto_hash_update(hmac, payload, payload_len); + clen = buf + buf_size - pos; + + if (clen < rl->hash_size) { + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not " + "enough room for MAC"); + crypto_hash_finish(hmac, NULL, NULL); + return -1; + } + + if (crypto_hash_finish(hmac, pos, &clen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " + "to calculate HMAC"); + 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 (crypto_cipher_encrypt(rl->write_cbc, cpayload, + cpayload, pos - cpayload) < 0) { + 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; + 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 (crypto_cipher_decrypt(rl->read_cbc, in_data, + out_data, in_len) < 0) { + *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; + + hmac = crypto_hash_init(rl->hash_alg, rl->read_mac_secret, + rl->hash_size); + + if (hmac == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " + "to initialize HMAC"); + *alert = TLS_ALERT_INTERNAL_ERROR; + return -1; + } + + crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN); + /* type + version + length + fragment */ + crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3); + WPA_PUT_BE16(len, plen); + crypto_hash_update(hmac, len, 2); + crypto_hash_update(hmac, out_data, plen); + hlen = sizeof(hash); + + if (crypto_hash_finish(hmac, hash, &hlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " + "to calculate HMAC"); + *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 100755 index 00000000..64885624 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/tls/tlsv1_server.c @@ -0,0 +1,659 @@ +/* + * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) + * Copyright (c) 2006-2011, Jouni Malinen + * + * 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" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +/* 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) +{ + 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; +} + + +/** + * 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 100755 index 00000000..aff65f92 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/tls/tlsv1_server_read.c @@ -0,0 +1,1368 @@ +/* + * TLSv1 server - read handshake message + * Copyright (c) 2006-2011, Jouni Malinen + * + * 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" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +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 (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; + } + + 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 (conn->verify.sha256_cert == NULL || + 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; + } + + 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 (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; + } + + 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 100755 index 00000000..4abbb140 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/tls/tlsv1_server_write.c @@ -0,0 +1,857 @@ +/* + * TLSv1 server - write handshake message + * Copyright (c) 2006-2011, Jouni Malinen + * + * 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" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +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 (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; + } + + 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 (conn->verify.sha256_server == NULL || + 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; + } + + 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 100755 index 00000000..def3efc1 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/tls/x509v3.c @@ -0,0 +1,2173 @@ +/* + * X.509v3 certificate parsing and processing (RFC 3280 profile) + * Copyright (c) 2006-2011, Jouni Malinen + * + * 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 "wpa2/tls/asn1.h" +#include "wpa2/tls/x509v3.h" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +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) +{ + 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 "?"; +} + + +/** + * 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 = 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 = 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_MSGDUMP, "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_MSGDUMP, "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_MSGDUMP, "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_MSGDUMP, "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_MSGDUMP, "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_MSGDUMP, "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 */ + sha256_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, + hash); + 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 100755 index 00000000..b44ff652 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/utils/base64.c @@ -0,0 +1,189 @@ +/* + * Base64 encoding/decoding (RFC1341) + * Copyright (c) 2005-2011, Jouni Malinen + * + * 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" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + + +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 100755 index 00000000..37eb3688 --- /dev/null +++ b/components/wpa_supplicant/src/wpa2/utils/ext_password.c @@ -0,0 +1,132 @@ +/* + * External password backend + * Copyright (c) 2012, Jouni Malinen + * + * 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 MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +#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 */ \ No newline at end of file diff --git a/components/wpa_supplicant/src/wps/wps_attr_build.c b/components/wpa_supplicant/src/wps/wps_attr_build.c index 7700694b..4e877e4e 100644 --- a/components/wpa_supplicant/src/wps/wps_attr_build.c +++ b/components/wpa_supplicant/src/wps/wps_attr_build.c @@ -9,6 +9,7 @@ #include "wpa/includes.h" #include "wpa/common.h" #include "wpa/wpa_debug.h" +#include "wpa/ieee802_11_defs.h" #include "crypto/aes_wrap.h" #include "crypto/crypto.h" @@ -16,9 +17,7 @@ #include "crypto/sha256.h" #include "crypto/random.h" -#include "wpa/ieee802_11_defs.h" #include "wps/wps_i.h" - #ifdef CONFIG_IDF_TARGET_ESP8266 #include "wps/asm/irqflags.h" diff --git a/components/wpa_supplicant/src/wps/wps_registrar.c b/components/wpa_supplicant/src/wps/wps_registrar.c index 3063a092..98666e45 100644 --- a/components/wpa_supplicant/src/wps/wps_registrar.c +++ b/components/wpa_supplicant/src/wps/wps_registrar.c @@ -7,11 +7,10 @@ */ #include "wpa/includes.h" #include "wpa/common.h" +#include "wpa/list.h" +#include "wpa/ieee802_11_defs.h" #include "wps/utils/uuid.h" -#include "wpa/list.h" - -#include "wpa/ieee802_11_defs.h" #include "wps/wps_i.h" #include "wps/wps_dev_attr.h" diff --git a/examples/wifi/wpa2_enterprise/CMakeLists.txt b/examples/wifi/wpa2_enterprise/CMakeLists.txt new file mode 100644 index 00000000..d50e6df2 --- /dev/null +++ b/examples/wifi/wpa2_enterprise/CMakeLists.txt @@ -0,0 +1,9 @@ + +# (Automatically converted from project Makefile by convert_to_cmake.py.) + +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(wpa2-enterprise) diff --git a/examples/wifi/wpa2_enterprise/Makefile b/examples/wifi/wpa2_enterprise/Makefile new file mode 100644 index 00000000..ff23a939 --- /dev/null +++ b/examples/wifi/wpa2_enterprise/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := wpa2-enterprise + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/wifi/wpa2_enterprise/README.md b/examples/wifi/wpa2_enterprise/README.md new file mode 100644 index 00000000..976e0c86 --- /dev/null +++ b/examples/wifi/wpa2_enterprise/README.md @@ -0,0 +1,63 @@ +# WPA2 Enterprise Example + +This example shows how ESP8266 connects to AP with wpa2 enterprise encryption. Example does the following steps: + +1. Install CA certificate which is optional. +2. Install client certificate and client key which is required in TLS method and optional in PEAP and TTLS methods. +3. Set identity of phase 1 which is optional. +4. Set user name and password of phase 2 which is required in PEAP and TTLS methods. +5. Enable wpa2 enterprise. +6. Connect to AP. + +*Note:* 1. certificate currently is generated when compiling the example and then stored in flash. + 2. The expiration date of the certificates is 2027/06/05. + +## The file wpa2_ca.pem, wpa2_ca.key, wpa2_server.pem, wpa2_server.crt and wpa2_server.key can be used to configure AP with + wpa2 enterprise encryption. The steps how to generate new certificates and keys using openssl is as follows: + +1. wpa2_ca.pem wpa2_ca.key: + openssl req -new -x509 -keyout wpa2_ca.key -out wpa2_ca.pem +2. wpa2_server.key: + openssl req -new -key wpa2_server.key -out wpa2_server.csr +3. wpa2_csr: + openssl req -new -key server.key -out server.csr +4. wpa2_server.crt: + openssl ca -batch -keyfile wpa2_ca.key -cert wpa2_ca.pem -in wpa2_server.csr -key ca1234 -out wpa2_server.crt -extensions xpserver_ext -extfile xpextensions +5. wpa2_server.p12: + openssl pkcs12 -export -in wpa2_server.crt -inkey wpa2_server.key -out wpa2_server.p12 -passin pass:sv1234 -passout pass:sv1234 +6. wpa2_server.pem: + openssl pkcs12 -in wpa2_server.p12 -out wpa2_server.pem -passin pass:sv1234 -passout pass:sv1234 +7. wpa2_client.key: + openssl genrsa -out wpa2_client.key 1024 +8. wpa2_client.csr: + openssl req -new -key wpa2_client.key -out wpa2_client.csr +9. wpa2_client.crt: + openssl ca -batch -keyfile wpa2_ca.key -cert wpa2_ca.pem -in wpa2_client.csr -key ca1234 -out wpa2_client.crt -extensions xpclient_ext -extfile xpextensions +10. wpa2_client.p12: + openssl pkcs12 -export -in wpa2_client.crt -inkey wpa2_client.key -out wpa2_client.p12 +11. wpa2_client.pem: + openssl pkcs12 -in wpa2_client.p12 -out wpa2_client.pem + +### Example output + +Here is an example of wpa2 enterprise(PEAP method) console output. + +I (393) reset_reason: RTC reset 1 wakeup 0 store 0, reason is 1 +I (413) example: Setting WiFi configuration SSID wpa2_test... +I (4981) wifi: state: 0 -> 2 (b0) +I (4986) wifi: state: 2 -> 3 (0) +I (4992) wifi: state: 3 -> 5 (10) +I (11005) example: ~~~~~~~~~~~ +I (11007) example: IP:0.0.0.0 +I (11010) example: MASK:0.0.0.0 +I (11012) example: GW:0.0.0.0 +I (11015) example: ~~~~~~~~~~~ +I (11019) example: free heap:72588 + +I (13026) example: ~~~~~~~~~~~ +I (13032) example: IP:192.168.2.121 +I (13034) example: MASK:255.255.255.0 +I (13037) example: GW:192.168.2.1 +I (13040) example: ~~~~~~~~~~~ +I (13045) example: free heap:72028 + diff --git a/examples/wifi/wpa2_enterprise/main/CMakeLists.txt b/examples/wifi/wpa2_enterprise/main/CMakeLists.txt new file mode 100644 index 00000000..10640a32 --- /dev/null +++ b/examples/wifi/wpa2_enterprise/main/CMakeLists.txt @@ -0,0 +1,8 @@ +set(COMPONENT_SRCS "wpa2_enterprise_main.c") +set(COMPONENT_ADD_INCLUDEDIRS ".") + + +# Embed CA, certificate & key directly into binary +set(COMPONENT_EMBED_TXTFILES wpa2_ca.pem wpa2_client.crt wpa2_client.key) + +register_component() diff --git a/examples/wifi/wpa2_enterprise/main/Kconfig.projbuild b/examples/wifi/wpa2_enterprise/main/Kconfig.projbuild new file mode 100644 index 00000000..7cf61fbc --- /dev/null +++ b/examples/wifi/wpa2_enterprise/main/Kconfig.projbuild @@ -0,0 +1,34 @@ +menu "Example Configuration" + + config WIFI_SSID + string "WiFi SSID" + default "wpa2_test" + help + SSID (network name) for the example to connect to. + + config EAP_METHOD + int "EAP METHOD" + default 1 + help + EAP method (TLS, PEAP or TTLS) for the example to use. + TLS: 0, PEAP: 1, TTLS: 2 + + config EAP_ID + string "EAP ID" + default "example@espressif.com" + help + Identity in phase 1 of EAP procedure. + + config EAP_USERNAME + string "EAP USERNAME" + default "espressif" + help + Username for EAP method (PEAP and TTLS). + + config EAP_PASSWORD + string "EAP PASSWORD" + default "test11" + help + Password for EAP method (PEAP and TTLS). + +endmenu \ No newline at end of file diff --git a/examples/wifi/wpa2_enterprise/main/component.mk b/examples/wifi/wpa2_enterprise/main/component.mk new file mode 100644 index 00000000..aab8ff8f --- /dev/null +++ b/examples/wifi/wpa2_enterprise/main/component.mk @@ -0,0 +1,12 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + +# embed files from the "certs" directory as binary data symbols +# in the app +COMPONENT_EMBED_TXTFILES := wpa2_ca.pem +COMPONENT_EMBED_TXTFILES += wpa2_client.crt +COMPONENT_EMBED_TXTFILES += wpa2_client.key + + diff --git a/examples/wifi/wpa2_enterprise/main/wpa2_ca.pem b/examples/wifi/wpa2_enterprise/main/wpa2_ca.pem new file mode 100644 index 00000000..1bdf23d9 --- /dev/null +++ b/examples/wifi/wpa2_enterprise/main/wpa2_ca.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID3DCCA0WgAwIBAgIJAMnlgL1czsmjMA0GCSqGSIb3DQEBCwUAMIGTMQswCQYD +VQQGEwJGUjEPMA0GA1UECAwGUmFkaXVzMRIwEAYDVQQHDAlTb21ld2hlcmUxFTAT +BgNVBAoMDEV4YW1wbGUgSW5jLjEgMB4GCSqGSIb3DQEJARYRYWRtaW5AZXhhbXBs +ZS5jb20xJjAkBgNVBAMMHUV4YW1wbGUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4X +DTE3MDYwNzA4MDY0OVoXDTI3MDYwNTA4MDY0OVowgZMxCzAJBgNVBAYTAkZSMQ8w +DQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNvbWV3aGVyZTEVMBMGA1UECgwMRXhh +bXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxlLmNvbTEmMCQG +A1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwgZ8wDQYJKoZIhvcN +AQEBBQADgY0AMIGJAoGBALpWR23fn/TmHxsXsHdrydzPSd17fZkc71WsaicgQR66 +1tIVYb22UWGfj9KPM8THMsV74ew4ZkaQ39qvU0iuQIRrKARFHFok+vbaecgWMeWe +vGIqdnmyB9gJYaFOKgtSkfXsu2ddsqdvLYwcDbczrq8X9yEXpN6mnxXeCcPG4F0p +AgMBAAGjggE0MIIBMDAdBgNVHQ4EFgQUgigpdAUpONoDq0pQ3yfxrslCSpcwgcgG +A1UdIwSBwDCBvYAUgigpdAUpONoDq0pQ3yfxrslCSpehgZmkgZYwgZMxCzAJBgNV +BAYTAkZSMQ8wDQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNvbWV3aGVyZTEVMBMG +A1UECgwMRXhhbXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxl +LmNvbTEmMCQGA1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCCQDJ +5YC9XM7JozAMBgNVHRMEBTADAQH/MDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly93 +d3cuZXhhbXBsZS5jb20vZXhhbXBsZV9jYS5jcmwwDQYJKoZIhvcNAQELBQADgYEA +euxOBPInSJRKAIseMxPmAabtAqKNslZSmpG4He3lkKt+HM3jfznUt3psmD7j1hFW +S4l7KXzzajvaGYybDq5N9MqrDjhGn3VXZqOLMUNDL7OQq96TzgqsTBT1dmVSbNlt +PQgiAeKAk3tmH4lRRi9MTBSyJ6I92JYcS5H6Bs4ZwCc= +-----END CERTIFICATE----- diff --git a/examples/wifi/wpa2_enterprise/main/wpa2_client.crt b/examples/wifi/wpa2_enterprise/main/wpa2_client.crt new file mode 100644 index 00000000..0e03a1b7 --- /dev/null +++ b/examples/wifi/wpa2_enterprise/main/wpa2_client.crt @@ -0,0 +1,70 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 48 (0x30) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=FR, ST=Radius, L=Somewhere, O=Example Inc./emailAddress=admin@example.com, CN=Example Certificate Authority + Validity + Not Before: Jun 7 08:06:49 2017 GMT + Not After : Jun 5 08:06:49 2027 GMT + Subject: C=FR, ST=Radius, O=Example Inc., CN=user@example.com/emailAddress=user@example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:d2:f6:be:72:a5:ab:2e:56:0c:dd:f2:3b:2c:7c: + e0:5d:05:40:af:0c:8c:f3:82:0c:d0:18:34:b4:e3: + 7d:5f:8d:0a:3e:aa:79:02:f9:96:ad:10:00:ec:51: + e9:dc:3f:fb:ea:b0:57:eb:48:c7:ca:ef:e8:05:ab: + ee:3f:66:ba:5c:9e:7f:40:85:9f:25:a0:e0:e3:7c: + cf:b6:e6:31:f5:fd:24:03:c8:f4:fb:d8:a4:f3:92: + 29:05:aa:55:43:80:f7:3e:13:10:43:3a:89:24:be: + d8:01:86:d1:69:73:44:7d:f8:b9:46:2b:6b:51:d0: + 11:31:4b:06:ae:9f:45:fa:12:17:0c:ef:6a:fa:d0: + f7:36:46:eb:2e:db:4e:20:46:01:33:ac:b1:f7:4a: + e6:18:3d:53:22:dc:e8:4a:12:78:11:2f:e4:3b:92: + bd:d7:07:5a:c9:81:5d:48:58:c8:0f:9b:e9:a4:0f: + bb:89:b1:ad:38:07:6f:93:d0:a6:12:56:f9:07:48: + d2:23:2f:a3:a9:93:b0:11:0a:27:4c:48:0a:8d:70: + 41:68:76:7a:dd:bc:54:c3:42:33:b0:7b:f6:ae:1f: + e7:95:5e:11:ca:f2:b4:4b:5c:ba:47:64:f0:f3:d7: + 87:95:7f:93:06:a1:72:c9:81:12:a5:b7:8f:9d:7e: + d1:ef + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Extended Key Usage: + TLS Web Client Authentication + X509v3 CRL Distribution Points: + + Full Name: + URI:http://www.example.com/example_ca.crl + + Signature Algorithm: sha1WithRSAEncryption + 2d:02:bc:7b:88:b8:5c:e1:07:b8:bb:ba:b2:f3:98:14:8f:cb: + b0:21:13:b5:e5:6f:05:4f:92:fa:ac:c0:53:a7:b0:cd:7e:ba: + 87:36:85:25:d7:41:c5:29:84:22:74:af:bf:3e:34:36:d5:24: + 7a:81:e2:1b:54:52:85:6f:76:de:dc:63:98:45:fc:2c:31:fa: + 22:a4:72:3a:8d:d4:6a:2e:de:33:10:41:eb:94:1d:e3:59:cd: + b2:be:ab:f0:b6:20:86:9c:b8:46:ee:c5:64:ba:b6:6c:cc:53: + 44:7a:80:12:77:7c:e7:51:67:91:32:2f:88:9d:93:a8:ef:d6: + cd:de +-----BEGIN CERTIFICATE----- +MIIDTjCCAregAwIBAgIBMDANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UEBhMCRlIx +DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF +eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMSYw +JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNzA2MDcw +ODA2NDlaFw0yNzA2MDUwODA2NDlaMHExCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZS +YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEZMBcGA1UEAwwQdXNlckBleGFt +cGxlLmNvbTEfMB0GCSqGSIb3DQEJARYQdXNlckBleGFtcGxlLmNvbTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBANL2vnKlqy5WDN3yOyx84F0FQK8MjPOC +DNAYNLTjfV+NCj6qeQL5lq0QAOxR6dw/++qwV+tIx8rv6AWr7j9mulyef0CFnyWg +4ON8z7bmMfX9JAPI9PvYpPOSKQWqVUOA9z4TEEM6iSS+2AGG0WlzRH34uUYra1HQ +ETFLBq6fRfoSFwzvavrQ9zZG6y7bTiBGATOssfdK5hg9UyLc6EoSeBEv5DuSvdcH +WsmBXUhYyA+b6aQPu4mxrTgHb5PQphJW+QdI0iMvo6mTsBEKJ0xICo1wQWh2et28 +VMNCM7B79q4f55VeEcrytEtcukdk8PPXh5V/kwahcsmBEqW3j51+0e8CAwEAAaNP +ME0wEwYDVR0lBAwwCgYIKwYBBQUHAwIwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDov +L3d3dy5leGFtcGxlLmNvbS9leGFtcGxlX2NhLmNybDANBgkqhkiG9w0BAQUFAAOB +gQAtArx7iLhc4Qe4u7qy85gUj8uwIRO15W8FT5L6rMBTp7DNfrqHNoUl10HFKYQi +dK+/PjQ21SR6geIbVFKFb3be3GOYRfwsMfoipHI6jdRqLt4zEEHrlB3jWc2yvqvw +tiCGnLhG7sVkurZszFNEeoASd3znUWeRMi+InZOo79bN3g== +-----END CERTIFICATE----- diff --git a/examples/wifi/wpa2_enterprise/main/wpa2_client.key b/examples/wifi/wpa2_enterprise/main/wpa2_client.key new file mode 100644 index 00000000..99936e25 --- /dev/null +++ b/examples/wifi/wpa2_enterprise/main/wpa2_client.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA0va+cqWrLlYM3fI7LHzgXQVArwyM84IM0Bg0tON9X40KPqp5 +AvmWrRAA7FHp3D/76rBX60jHyu/oBavuP2a6XJ5/QIWfJaDg43zPtuYx9f0kA8j0 ++9ik85IpBapVQ4D3PhMQQzqJJL7YAYbRaXNEffi5RitrUdARMUsGrp9F+hIXDO9q ++tD3NkbrLttOIEYBM6yx90rmGD1TItzoShJ4ES/kO5K91wdayYFdSFjID5vppA+7 +ibGtOAdvk9CmElb5B0jSIy+jqZOwEQonTEgKjXBBaHZ63bxUw0IzsHv2rh/nlV4R +yvK0S1y6R2Tw89eHlX+TBqFyyYESpbePnX7R7wIDAQABAoIBAQC5PncO3tBIeMEF +pu007FZq9/DLhP7D2B9+HrMxX0y4uXUUf8aQyS74ukPFP0xV3U1M0BnzfU4KscyQ +Jl+nBoKAT6C3vF15wiGXQAJ4vPuD4Ate03fjKWH2ixJAakhCZR01QbIXBnBkdrvf +401BBjlPUDcIGZo8FbLzEMlGTo84vE9v3Qmkbi+PzPCh2YC+NDmsOcIW1zpmwyYC +ZYCpoWgl4++kqXXn0NGhuaOgB0JLsJOBpx/hOOjBU/wXCKaXZ1vchYqfbvvx2gf2 +WX4P0CiTH1z7MEAHanaZkcnNyxV/oF1EIMY5p0vDDzgrKtppvPOqspjydje03+CE +t0wKGPi5AoGBAPAG2Y4efgwLcoWdPjKZtsHLhDhLJnvxkqnNkzdPnLZojNi8pKkV +/Yu++pPemJZZa4YAp+OnqyEfhcha+HYqKMwRC8t3YrEVOlRQTfW/OoSrp059JIRV +jTvq/u7DdYGJRRgMLUJiEI+7xj1WbTc2EceJAgn0qfKvbvBtVJP0LH1TAoGBAOEA +xZB7SwyX+zDGRTugqMYg+sYobbQHJ7utLyoX+ckeG+sPEjEYLpQQfshET/gwF8ZK +4aILkACx/tna799xCjQdmyyc338NO9WULlY1xF+65WfeaxrtTAsqVikX3p19McRI +ijX8k7Msy3gYWJXev3MCtPT2+g68IgbL/W2wY+l1AoGAT7xGy0Jv5vpqid5pig+s +OYatHrJAT445hXUIQaiNy77Bg0JvhMgMWT8RKMwabl+4K2TOYP8TB0bcf2lQ/pgU +w22qOGYpf+AoZ1fh/hAPlYEcbCOAXQG6kDwJgjGmOGjsbgelhVbkX4smWLv8PgoV +L+7goYQIbNlAhlgbb6b+nIcCgYBB7Zr2Cdpkt0en9ACnRx0M6O7yDziNzqbqzAUM +3XeYYZUmnATlk8NaKTcs8S9JdrYQqTJR6/dm7MDTDt7IZvPpb19fhBvMu5DztPaa +1ihTMI01kStq+WsVvnL+mXrmRJ/HdsXgqcCReKep6eBTEbChP4LMYG3G0YNa4HzC +njO4XQKBgQDRnbqqg2CNTnS94BN2D3uzzELtwsIG6aVCtl09ZsLnGaBKVVDtP6BI +j2hGD7xw4g5JeSPIJU5J03nALTY3hz1JyI7AJCX7+JRtUTX2A8C4mlbeul7ilGaU +A7MFT8GqhjYYa84GzNcA1mK8ynlixpL8+yzTT/8lWInWRBa69SkktA== +-----END RSA PRIVATE KEY----- diff --git a/examples/wifi/wpa2_enterprise/main/wpa2_client.pem b/examples/wifi/wpa2_enterprise/main/wpa2_client.pem new file mode 100644 index 00000000..101f6cc3 --- /dev/null +++ b/examples/wifi/wpa2_enterprise/main/wpa2_client.pem @@ -0,0 +1,57 @@ +Bag Attributes + localKeyID: 40 1A 0E 8F 13 CA 96 2C 6D DB 18 E4 EF A8 83 84 0C 61 85 FE +subject=/C=FR/ST=Radius/O=Example Inc./CN=user@example.com/emailAddress=user@example.com +issuer=/C=FR/ST=Radius/L=Somewhere/O=Example Inc./emailAddress=admin@example.com/CN=Example Certificate Authority +-----BEGIN CERTIFICATE----- +MIIDTjCCAregAwIBAgIBMDANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UEBhMCRlIx +DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF +eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMSYw +JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNzA2MDcw +ODA2NDlaFw0yNzA2MDUwODA2NDlaMHExCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZS +YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEZMBcGA1UEAwwQdXNlckBleGFt +cGxlLmNvbTEfMB0GCSqGSIb3DQEJARYQdXNlckBleGFtcGxlLmNvbTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBANL2vnKlqy5WDN3yOyx84F0FQK8MjPOC +DNAYNLTjfV+NCj6qeQL5lq0QAOxR6dw/++qwV+tIx8rv6AWr7j9mulyef0CFnyWg +4ON8z7bmMfX9JAPI9PvYpPOSKQWqVUOA9z4TEEM6iSS+2AGG0WlzRH34uUYra1HQ +ETFLBq6fRfoSFwzvavrQ9zZG6y7bTiBGATOssfdK5hg9UyLc6EoSeBEv5DuSvdcH +WsmBXUhYyA+b6aQPu4mxrTgHb5PQphJW+QdI0iMvo6mTsBEKJ0xICo1wQWh2et28 +VMNCM7B79q4f55VeEcrytEtcukdk8PPXh5V/kwahcsmBEqW3j51+0e8CAwEAAaNP +ME0wEwYDVR0lBAwwCgYIKwYBBQUHAwIwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDov +L3d3dy5leGFtcGxlLmNvbS9leGFtcGxlX2NhLmNybDANBgkqhkiG9w0BAQUFAAOB +gQAtArx7iLhc4Qe4u7qy85gUj8uwIRO15W8FT5L6rMBTp7DNfrqHNoUl10HFKYQi +dK+/PjQ21SR6geIbVFKFb3be3GOYRfwsMfoipHI6jdRqLt4zEEHrlB3jWc2yvqvw +tiCGnLhG7sVkurZszFNEeoASd3znUWeRMi+InZOo79bN3g== +-----END CERTIFICATE----- +Bag Attributes + localKeyID: 40 1A 0E 8F 13 CA 96 2C 6D DB 18 E4 EF A8 83 84 0C 61 85 FE +Key Attributes: +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIvLOGpsoWmVMCAggA +MBQGCCqGSIb3DQMHBAhE0X+F6dzt+QSCBMjeI5lZaP89SD5MPX7wzByOKAEFrBMF +89epa3xAtxb6r42WRIoidg2MwVxsG5qjNt6+iD4AZuqDCP6J7UDg5LpWGicG0jXe +Wc947pIUUc4q29ZYoAMG4Kx6ccF864rdGPjwhkqCg3KQJsUp8uXNltFdhni7cQ5e +eHNGkL1YHpAYGJJQNchHjbyIMfhzSbc2nSPmCvMbN5PucJH183Cw4UzonfUHkvzA +VZmRrFkbd8F+mojie9x8MghWaNlozs2Rn+pAhk1YSmH+w++ZfSiTwAhN1LA6AdjH +FvCEUJHyrFxEPVPMJsfVTYM1t1PxCIfMykh6bby9esH78L3ItpDkMC4TJWq4q7zA +fxufhC8DCxdz5pLEYSrxBF+3YdG4vqIPF5fXj0IqtiYP8se01Smc5C5JuFtYQxyZ +E10hiSKJvDr0VCiBswyBBc4QPGI2y2vDpg1R5Ure2Mp1phDTcGIoDKw6bwlVfiTb +d0nKYwXDO5OigZ/W92FO4hAMIe+7E+q3If7Rp4fvyECoHlT2WYQ4Sq+xxSWxQSv6 +RHrTyyHTg9VIdMDxI6aLiLZBXhh1Q2fkRrh1utAfYH4hAhUscp8v1+0XQo93efbn +JJ7oPhbeapPIqzabcRyUG5dwuKzdzjN/k/R90h3DEzqFE372MeyYghfPB3VQolHd +GZ1bAEiAYkjgVQbezTZ5vON+hqz1f1gIanpCB+b1pW32ljS4ANEm3SjZfaUt41Wu +lO2Btpp4ugs4dt/HBJbzTGHwEHAqtYux+GYMeBZkXFabMozU1rY8yTsF6Oz1Taj6 +fZlm3NJ4gcnLx8lLAITSTJ63GwhPAxEmhmsA98yCJ4BIPgkBpCDT9wFyRLTD2qr5 +kQat2lS6qVLHTXTJhGaAddzc+ISv+tMskreLluIJo2Y8GKgpmYapnTU00Jeh846R +AUmHBayHdwjWBRHJgdxSuy3EkLuEbskzKeKOKt/2NsoCwgEpt77v87/yYZvqIzFm +wS48oQA/bVTEbRPPfjUfPbqkqg6Lsit1bBLuCSJZ5gSnS6a/q3/ArbZX12Uoyg21 +j4OXCILoqAs69rTZl0aDEWfOkCyAYxlDNcbE4x6JxTjfWuTvSfOGI87ktMuErvZV +IprOVhYF08xk1oGLPMjjqtCU05vrOf2d55vlx6WinZ4+5IMrFnixCYPtlb1V5HGL +w6ZLP5pP4cM0buFwrGaVOzy6dtP3ZX5JAXbV3lnV9SfzsN3t5hnMUsg9GkVW7FDQ +H6G8v2rIVP5VirQiNc2tJUk/46ybvsnrOvlrvTcXGMpWK3iCn27qi2swmwE0CSjW +hXydeKFi/tyxBDwOl/V13dYzpdU1sCVE7NZfVL0LA62jPZdRBAau0s3FMictyha3 +5BGr6mo41XTm2faOwnLoJTjlzGG01Bh8Di/B00+2Y1caDZAaHgpYO4Rs0c5W7NiG +zH0HrFJ4M+5Jw12n4LMYMZYvr+EmsLGlTkLqf26u2D98qH0vG7Hjht3/N66Eu5ct +hwRQaRuiFKx7tnte4K/iI3GjCqaGOACHSF3cQBjOe1jlEgMBXrb3C1SKaw8hnIgX +5T/wgCeVXHSQNlp3udTd9qIYYMi93Jb3ajeZva3bgz2KNbdnwVuk3cUi2vWHShzi +7sY= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/examples/wifi/wpa2_enterprise/main/wpa2_enterprise_main.c b/examples/wifi/wpa2_enterprise/main/wpa2_enterprise_main.c new file mode 100644 index 00000000..c24466cd --- /dev/null +++ b/examples/wifi/wpa2_enterprise/main/wpa2_enterprise_main.c @@ -0,0 +1,188 @@ +/* WiFi Connection Example using WPA2 Enterprise + * + * Original Copyright (C) 2006-2016, ARM Limited, All Rights Reserved, Apache 2.0 License. + * Additions Copyright (C) Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD, Apache 2.0 License. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_wifi.h" +#include "esp_wpa2.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "tcpip_adapter.h" +#include "esp_event_loop.h" + +/* The examples use simple WiFi configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" + + You can choose EAP method via 'make menuconfig' according to the + configuration of AP. +*/ +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_EAP_METHOD CONFIG_EAP_METHOD + +#define EXAMPLE_EAP_ID CONFIG_EAP_ID +#define EXAMPLE_EAP_USERNAME CONFIG_EAP_USERNAME +#define EXAMPLE_EAP_PASSWORD CONFIG_EAP_PASSWORD + +/* FreeRTOS event group to signal when we are connected & ready to make a request */ +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const int WIFI_CONNECTED_BIT = BIT0; + +/* Constants that aren't configurable in menuconfig */ +#define EAP_PEAP 1 +#define EAP_TTLS 2 + +static const char* TAG = "example"; + +/* CA cert, taken from wpa2_ca.pem + Client cert, taken from wpa2_client.crt + Client key, taken from wpa2_client.key + + The PEM, CRT and KEY file were provided by the person or organization + who configured the AP with wpa2 enterprise. + + To embed it in the app binary, the PEM, CRT and KEY file is named + in the component.mk COMPONENT_EMBED_TXTFILES variable. +*/ +extern uint8_t ca_pem_start[] asm("_binary_wpa2_ca_pem_start"); +extern uint8_t ca_pem_end[] asm("_binary_wpa2_ca_pem_end"); +extern uint8_t client_crt_start[] asm("_binary_wpa2_client_crt_start"); +extern uint8_t client_crt_end[] asm("_binary_wpa2_client_crt_end"); +extern uint8_t client_key_start[] asm("_binary_wpa2_client_key_start"); +extern uint8_t client_key_end[] asm("_binary_wpa2_client_key_end"); + +static esp_err_t event_handler(void* ctx, system_event_t* event) +{ + /* For accessing reason codes in case of disconnection */ + system_event_info_t* info = &event->event_info; + + switch (event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + + case SYSTEM_EVENT_STA_GOT_IP: + ESP_LOGI(TAG, "got ip:%s", + ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip)); + xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT); + break; + + case SYSTEM_EVENT_AP_STACONNECTED: + ESP_LOGI(TAG, "station:"MACSTR" join, AID=%d", + MAC2STR(event->event_info.sta_connected.mac), + event->event_info.sta_connected.aid); + break; + + case SYSTEM_EVENT_AP_STADISCONNECTED: + ESP_LOGI(TAG, "station:"MACSTR"leave, AID=%d", + MAC2STR(event->event_info.sta_disconnected.mac), + event->event_info.sta_disconnected.aid); + break; + + case SYSTEM_EVENT_STA_DISCONNECTED: + ESP_LOGE(TAG, "Disconnect reason : %d", info->disconnected.reason); + + if (info->disconnected.reason == WIFI_REASON_BASIC_RATE_NOT_SUPPORT) { + /*Switch to 802.11 bgn mode */ + esp_wifi_set_protocol(ESP_IF_WIFI_STA, WIFI_PROTOCAL_11B | WIFI_PROTOCAL_11G | WIFI_PROTOCAL_11N); + } + + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, WIFI_CONNECTED_BIT); + break; + + default: + break; + } + + return ESP_OK; +} + +static void initialise_wifi(void) +{ + unsigned int ca_pem_bytes = ca_pem_end - ca_pem_start; + unsigned int client_crt_bytes = client_crt_end - client_crt_start; + unsigned int client_key_bytes = client_key_end - client_key_start; + esp_wpa2_config_t config = WPA2_CONFIG_INIT_DEFAULT(); + + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + }, + }; + ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid); + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_set_ca_cert(ca_pem_start, ca_pem_bytes)); + ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_set_cert_key(client_crt_start, client_crt_bytes, + client_key_start, client_key_bytes, NULL, 0)); + ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_set_identity((uint8_t*)EXAMPLE_EAP_ID, strlen(EXAMPLE_EAP_ID))); + + if (EXAMPLE_EAP_METHOD == EAP_PEAP || EXAMPLE_EAP_METHOD == EAP_TTLS) { + ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_set_username((uint8_t*)EXAMPLE_EAP_USERNAME, strlen(EXAMPLE_EAP_USERNAME))); + ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_set_password((uint8_t*)EXAMPLE_EAP_PASSWORD, strlen(EXAMPLE_EAP_PASSWORD))); + } + + ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_enable(&config)); + ESP_ERROR_CHECK(esp_wifi_start()); +} + +static void wpa2_enterprise_example_task(void* pvParameters) +{ + tcpip_adapter_ip_info_t ip; + memset(&ip, 0, sizeof(tcpip_adapter_ip_info_t)); + vTaskDelay(2000 / portTICK_PERIOD_MS); + + while (1) { + vTaskDelay(2000 / portTICK_PERIOD_MS); + + if (tcpip_adapter_get_ip_info(ESP_IF_WIFI_STA, &ip) == 0) { + ESP_LOGI(TAG, "~~~~~~~~~~~"); + ESP_LOGI(TAG, "IP:"IPSTR, IP2STR(&ip.ip)); + ESP_LOGI(TAG, "MASK:"IPSTR, IP2STR(&ip.netmask)); + ESP_LOGI(TAG, "GW:"IPSTR, IP2STR(&ip.gw)); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + } + + ESP_LOGI(TAG, "free heap:%d\n", esp_get_free_heap_size()); + } +} + +void app_main() +{ + ESP_ERROR_CHECK(nvs_flash_init()); + initialise_wifi(); + xTaskCreate(&wpa2_enterprise_example_task, "wpa2_enterprise_example_task", 4096, NULL, 5, NULL); +} diff --git a/examples/wifi/wpa2_enterprise/main/wpa2_server.crt b/examples/wifi/wpa2_enterprise/main/wpa2_server.crt new file mode 100644 index 00000000..8f1dfcda --- /dev/null +++ b/examples/wifi/wpa2_enterprise/main/wpa2_server.crt @@ -0,0 +1,70 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 47 (0x2f) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=FR, ST=Radius, L=Somewhere, O=Example Inc./emailAddress=admin@example.com, CN=Example Certificate Authority + Validity + Not Before: Jun 7 08:06:49 2017 GMT + Not After : Jun 5 08:06:49 2027 GMT + Subject: C=FR, ST=Radius, O=Example Inc., CN=Example Server Certificate/emailAddress=admin@example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c9:d8:e2:e0:75:91:83:87:d8:c8:80:c6:20:4d: + e9:14:24:30:98:33:53:fa:56:0e:ec:9a:43:7f:87: + a9:22:94:26:06:c7:ac:b5:d9:ec:55:06:81:b7:0d: + c9:24:51:49:fa:47:fb:4b:4e:fc:ed:75:8a:e1:28: + 32:bc:c5:e0:4c:45:c4:58:60:15:67:1e:6b:40:19: + 3f:f0:ab:92:61:92:2d:71:10:2e:f2:eb:bc:81:2f: + 5a:3b:74:ca:5f:fd:e0:ee:d1:d9:07:6a:6c:20:c0: + 07:88:b4:8b:0f:ad:1e:c9:4f:7c:11:98:37:89:15: + de:24:b1:11:1a:7c:97:4a:cf:f3:c8:cb:79:9e:9c: + c3:71:da:a6:94:97:f5:95:fd:61:06:44:e2:3f:12: + 43:0b:1d:33:48:91:d2:ce:4f:97:a1:ed:6a:30:c7: + 5d:98:b5:6e:0a:b7:4f:d9:03:ec:80:76:09:b0:40: + a1:a1:af:ab:2a:59:c4:0f:56:22:bc:be:14:be:18: + df:10:7d:5d:22:bf:e5:04:77:7a:75:6b:3e:eb:6d: + 20:a1:a7:60:d4:f1:87:9d:9f:60:b9:d3:db:2c:25: + f4:91:4a:f1:d2:40:e5:a1:10:88:a0:41:5a:98:40: + ca:15:d7:e3:e6:3e:c0:6a:d5:46:b2:b4:90:b4:ae: + 3b:e3 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Extended Key Usage: + TLS Web Server Authentication + X509v3 CRL Distribution Points: + + Full Name: + URI:http://www.example.com/example_ca.crl + + Signature Algorithm: sha1WithRSAEncryption + a4:25:21:51:0b:22:6c:63:8d:a9:c1:4f:04:33:69:79:34:f0: + 36:dd:8f:6a:27:5f:07:a2:1d:ef:8b:f0:96:e6:e7:a3:b8:3b: + 85:5e:3f:26:43:8a:8e:95:58:9c:a6:db:9c:51:bf:ea:53:16: + 3e:c1:a8:11:1a:c6:cf:0e:a1:17:18:64:d2:05:f1:c0:9c:a6: + 2b:16:c4:29:54:03:d2:17:bd:15:74:d6:ad:8a:8f:2d:cc:27: + 3b:88:88:f2:ea:d0:a2:cb:e9:42:57:df:26:9f:8a:a2:02:2f: + 35:b6:19:1d:26:43:44:af:12:4b:bc:b9:84:50:02:fd:1d:fa: + 50:e8 +-----BEGIN CERTIFICATE----- +MIIDWTCCAsKgAwIBAgIBLzANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UEBhMCRlIx +DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF +eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMSYw +JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNzA2MDcw +ODA2NDlaFw0yNzA2MDUwODA2NDlaMHwxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZS +YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEjMCEGA1UEAwwaRXhhbXBsZSBT +ZXJ2ZXIgQ2VydGlmaWNhdGUxIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUu +Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAydji4HWRg4fYyIDG +IE3pFCQwmDNT+lYO7JpDf4epIpQmBsestdnsVQaBtw3JJFFJ+kf7S0787XWK4Sgy +vMXgTEXEWGAVZx5rQBk/8KuSYZItcRAu8uu8gS9aO3TKX/3g7tHZB2psIMAHiLSL +D60eyU98EZg3iRXeJLERGnyXSs/zyMt5npzDcdqmlJf1lf1hBkTiPxJDCx0zSJHS +zk+Xoe1qMMddmLVuCrdP2QPsgHYJsEChoa+rKlnED1YivL4UvhjfEH1dIr/lBHd6 +dWs+620goadg1PGHnZ9gudPbLCX0kUrx0kDloRCIoEFamEDKFdfj5j7AatVGsrSQ +tK474wIDAQABo08wTTATBgNVHSUEDDAKBggrBgEFBQcDATA2BgNVHR8ELzAtMCug +KaAnhiVodHRwOi8vd3d3LmV4YW1wbGUuY29tL2V4YW1wbGVfY2EuY3JsMA0GCSqG +SIb3DQEBBQUAA4GBAKQlIVELImxjjanBTwQzaXk08Dbdj2onXweiHe+L8Jbm56O4 +O4VePyZDio6VWJym25xRv+pTFj7BqBEaxs8OoRcYZNIF8cCcpisWxClUA9IXvRV0 +1q2Kjy3MJzuIiPLq0KLL6UJX3yafiqICLzW2GR0mQ0SvEku8uYRQAv0d+lDo +-----END CERTIFICATE----- diff --git a/examples/wifi/wpa2_enterprise/main/wpa2_server.key b/examples/wifi/wpa2_enterprise/main/wpa2_server.key new file mode 100644 index 00000000..9b343327 --- /dev/null +++ b/examples/wifi/wpa2_enterprise/main/wpa2_server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAydji4HWRg4fYyIDGIE3pFCQwmDNT+lYO7JpDf4epIpQmBses +tdnsVQaBtw3JJFFJ+kf7S0787XWK4SgyvMXgTEXEWGAVZx5rQBk/8KuSYZItcRAu +8uu8gS9aO3TKX/3g7tHZB2psIMAHiLSLD60eyU98EZg3iRXeJLERGnyXSs/zyMt5 +npzDcdqmlJf1lf1hBkTiPxJDCx0zSJHSzk+Xoe1qMMddmLVuCrdP2QPsgHYJsECh +oa+rKlnED1YivL4UvhjfEH1dIr/lBHd6dWs+620goadg1PGHnZ9gudPbLCX0kUrx +0kDloRCIoEFamEDKFdfj5j7AatVGsrSQtK474wIDAQABAoIBAQC2kGDEPBJdMSW2 +VCLfXRiPixwYzXQLXIMrJWwfkQg9qlmqkDd6U50aWkRA2UswegW7RhfYSZ0i+cmf +VMhvTVpOIlwwwtcY6b5/v1bBy60eaySGuuh79xQMlFO8qynQIMStvUfbGTqrdIRb +9VBB4YeS9T12fILejtTZwv2BQ2dj1Y1SCay6Ri85UzJqSClRKgHISybvVdLNjPvP +0TRFBr57zyjL6WE8teKiKchzQko2u86No5uBCdKGsrAkrsdcR0YqlM/pZxd3VKNm ++eny0k+dZZlvcPxzkzP4hEp9+Rw5rP9/s3s/cCwvuuC5JO32ATBWKCbTvPv/XPDb +MdSJtOshAoGBAPzk0eswkcbFYtpnpBNmBAr1dtAdW1lfjUI2ucMMwt7Wns0P/tt+ +gq6Hi1wTaGP0l/dIECgeHwjtWj31ZJjQtFJ1y/kafxo4o9cA8vCydpdvSZaldAfg +sbLlDTDYzEpelaDIbNQBBXFoC5U9JlBhBsIFCL5Z8ZuIeFPsb7t5wwuHAoGBAMxT +jyWfNm1uNxp1xgCnrRsLPQPVnURrSFAqcHrECqRu3F7sozTN7q/cZViemxPvVDGQ +p9c+9bHwaYvW4trO5qDHJ++gGwm5L52bMAY1VUfeTt67fqrey43XpdmzcTX1V9Uj +QWawPUCSDzFjL1MjfCIejtyYf5ash53vj+T8r/vFAoGAA/OPVB1uKazr3n3AEo2F +gqZTNO1AgCT+EArK3EFWyiSQVqPpV4SihheYFdg3yVgJB9QYbIgL9BfBUTaEW97m +8mLkzP+c/Mvlw3ZAVYJ0V+llPPVY2saoACOUES9SAdd4fwqiqK1baGo3xB0wfBEI +CgAKIu9E1ylKuAT5ufQtGAECgYEAtP/kU5h5N3El4QupTdU7VDSdZTMqsHw0v8cI +gsf9AXKvRmtrnBA8u46KPHmruHoO5CVXeSZtsaXdaaH+rYQQ6yXg67WxnehtFLlv +TmCaXiLBTS9cYvMf8FOyuGnsBLeEietEOTov2G5KhR5uwsAxa2wUc7endor5S9/2 +YQuyvV0CgYALbiFpILd5l1ip65eE6JdA3hfttUbV2j2NSW12ej69vqbeOfaSgNse +uYCcXFsBbQPhNPwA+4d1oCe8SyXZg1f7gE812z2Tyr/3vdVnNZlitoxhsHmGiyS7 +gZdaTYCb78l9z0EBdaCVvA16owEle4SR6f9eCwzSI0WPOUra+x/hrA== +-----END RSA PRIVATE KEY----- diff --git a/examples/wifi/wpa2_enterprise/main/wpa2_server.pem b/examples/wifi/wpa2_enterprise/main/wpa2_server.pem new file mode 100644 index 00000000..ce49ae63 --- /dev/null +++ b/examples/wifi/wpa2_enterprise/main/wpa2_server.pem @@ -0,0 +1,57 @@ +Bag Attributes + localKeyID: 63 3B C1 EE 3A 4A 9B 3E FF 9E E7 BC 17 50 D7 F7 B7 7E 3B C0 +subject=/C=FR/ST=Radius/O=Example Inc./CN=Example Server Certificate/emailAddress=admin@example.com +issuer=/C=FR/ST=Radius/L=Somewhere/O=Example Inc./emailAddress=admin@example.com/CN=Example Certificate Authority +-----BEGIN CERTIFICATE----- +MIIDWTCCAsKgAwIBAgIBLzANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UEBhMCRlIx +DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF +eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMSYw +JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNzA2MDcw +ODA2NDlaFw0yNzA2MDUwODA2NDlaMHwxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZS +YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEjMCEGA1UEAwwaRXhhbXBsZSBT +ZXJ2ZXIgQ2VydGlmaWNhdGUxIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUu +Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAydji4HWRg4fYyIDG +IE3pFCQwmDNT+lYO7JpDf4epIpQmBsestdnsVQaBtw3JJFFJ+kf7S0787XWK4Sgy +vMXgTEXEWGAVZx5rQBk/8KuSYZItcRAu8uu8gS9aO3TKX/3g7tHZB2psIMAHiLSL +D60eyU98EZg3iRXeJLERGnyXSs/zyMt5npzDcdqmlJf1lf1hBkTiPxJDCx0zSJHS +zk+Xoe1qMMddmLVuCrdP2QPsgHYJsEChoa+rKlnED1YivL4UvhjfEH1dIr/lBHd6 +dWs+620goadg1PGHnZ9gudPbLCX0kUrx0kDloRCIoEFamEDKFdfj5j7AatVGsrSQ +tK474wIDAQABo08wTTATBgNVHSUEDDAKBggrBgEFBQcDATA2BgNVHR8ELzAtMCug +KaAnhiVodHRwOi8vd3d3LmV4YW1wbGUuY29tL2V4YW1wbGVfY2EuY3JsMA0GCSqG +SIb3DQEBBQUAA4GBAKQlIVELImxjjanBTwQzaXk08Dbdj2onXweiHe+L8Jbm56O4 +O4VePyZDio6VWJym25xRv+pTFj7BqBEaxs8OoRcYZNIF8cCcpisWxClUA9IXvRV0 +1q2Kjy3MJzuIiPLq0KLL6UJX3yafiqICLzW2GR0mQ0SvEku8uYRQAv0d+lDo +-----END CERTIFICATE----- +Bag Attributes + localKeyID: 63 3B C1 EE 3A 4A 9B 3E FF 9E E7 BC 17 50 D7 F7 B7 7E 3B C0 +Key Attributes: +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIzOJcU8NzjKACAggA +MBQGCCqGSIb3DQMHBAjdK4dY0QgcrgSCBMgCv4euS7bxaRTMSXYliVA+UrAI0iJL +oQgZ6cGFqXqasKu7K6BltLXaKJLX321gV2ofHHAxa1CWFwIgrv/bhci10cJr6BaL +a3+5L5e/EO0eRKVzoplput9mTP27pcJSu8jN5jOu054oamzFKtgGcBuL9mRCAfVq +JSCwbSUHPrwZX6jVDsLggrvE78SIXEYAQDfUrbvBY1kGNNV+U9v410SREYXI3q+8 +a4Paotf2HN5G0nvr0BSJUbY0NUe4AP1NfOM1VE8UiLL3LfZE3ZbOm6XsM8Mxf8rV +BkDOzhErF/dXOE65b5xG4KfoXy78WN7OeJpjNgNUDUl1/3+bc++8f7ZBzOtVAjP7 +diDYU/UrQeLUrLVxTLD6C9J9HvbW5JCEa1FN+pKQKxeaQ9j/dDhJ7XI4ctaIIQui +zrOTLEhBqC10TaNabLNVsLFCgFQMREBVYZoBYbFIZLT4FuRk5yaAYRDCrRKgqlu6 +61E0jOD/hDZIbBO/Vf94d+VaypeFonoz6SdrWgf4heN530d5fd5nOxElRl8LmUcH +LriwJIXB7XCfwgjREceRQpjiqmWPl8hcfe9E2NzwldbE+eLN0mlXgRyLlU+eXova +3r1u60Hb9ocLux3kHUXpZ+MomwkwqIn9qFC0U3KBGt7SmhUWypmoNUPiXP0U4Lno +tynPBODzhDeRv2T0qxULc6OznDE3WOuy88B94/JXhB5D56bRb9FkkpHpmn8MP8SW +fIV3cK/cBoGxgyk+IaL0aQbojFtyeyKbkrz1W3WnPtB3/kqd8USzpCvUBMBVG83D +XzHcU/mRYO61BgyGv0VBXdBtPbmW1CK73eX7gc/uOtqhxUQc3g5hNZDL2rxPCpID +alyGlSQxY8cPWJbYMPq2RHoJNDxnTbFhDm3mdOoTg5qK4+cUZMBqfq1kswXM9Z4p +LSnIviRjpL4a7hhDVAJYOo29rBP+QNdt5CkyLNwjnDv+l8jHOMTP4t1RPvoQx6hc +aOQOcSQnIqDgzot3//dnC2mBTQ66jsOnRPMMs9MQ4up6Wrc5PEt5avBi3B4AQC2Q +TUjL6QAcMR5cJbkecp+5h7W5pUw3OYcYpg6G+unJVGSgZkye2lySGgNuRLmcYjVA +gXfA9a8+HN/TFGHP668pZvHQrfV470ETnHhrh+NLN5AIDR+UFG8is7Xj7Ly2/5bN +M3Q3AYSCb8P8/ZrV3Dfm3qoCoxuNYax0PYt/JBWXTtPG1SClUQg+yo8VMiNw877h +IEdkg1QBxKWY5x+ThK79y+Cwub795ym9bYTwAtH8kpLmamoBT8UvgJumh0i8NGg3 +04B/oyg1P8/TxfbD0uaTPC+tmbIKVyHybTQL6/E0bs2knPp2Zyxno3yE3AI70msZ +mGNuK9mkxITIUbijiRZyeQvZz/x/daYwQ0WuaXXpPQvKcFu7LyIEad8QpIqtSCax +VS9Mtwe7zTCp0jmQDBGltlk1B2rUlQ5rxsFN6kJtBULdQc1TTj0NUp3ESeAE3A/l +wE8fPzBht1OKghJqVndGoh01XhKJfpfaQ85CES0HzA6Nll8SQO0bn6u4SBZGEvBT +pFarRrtS6KbulTDB3yBPrKgd1ZoBQv5/ScW8fPcLi55U6nhR28uu+zKy4yZFgOFR +2KU= +-----END ENCRYPTED PRIVATE KEY-----