diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/include/wpa/common.h b/cpu/esp32/vendor/esp-idf/wpa_supplicant/include/wpa/common.h new file mode 100644 index 0000000000..2e6012f868 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/include/wpa/common.h @@ -0,0 +1,337 @@ +/* + * wpa_supplicant/hostapd / common helper functions, etc. + * Copyright (c) 2002-2007, 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 COMMON_H +#define COMMON_H + +#if defined(__ets__) +#endif /* ets */ +#include "os.h" + +/* Define platform specific variable type macros */ +#if defined(ESP_PLATFORM) +#include +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; +typedef int64_t s64; +typedef int32_t s32; +typedef int16_t s16; +typedef int8_t s8; +#endif /*ESP_PLATFORM*/ + +#if defined(__XTENSA__) +#include +#define __BYTE_ORDER BYTE_ORDER +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#define __BIG_ENDIAN BIG_ENDIAN +#endif /*__XTENSA__*/ + +#if defined(__linux__) || defined(__GLIBC__) || defined(__ets__) +#include +#include +#endif /* __linux__ */ + +/* Define platform specific byte swapping macros */ + +#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS) + +static inline unsigned short wpa_swap_16(unsigned short v) +{ + return ((v & 0xff) << 8) | (v >> 8); +} + +static inline unsigned int wpa_swap_32(unsigned int v) +{ + return ((v & 0xff) << 24) | ((v & 0xff00) << 8) | + ((v & 0xff0000) >> 8) | (v >> 24); +} + +#define le_to_host16(n) (n) +#define host_to_le16(n) (n) +#define be_to_host16(n) wpa_swap_16(n) +#define host_to_be16(n) wpa_swap_16(n) +#define le_to_host32(n) (n) +#define be_to_host32(n) wpa_swap_32(n) +#define host_to_be32(n) wpa_swap_32(n) + +#define WPA_BYTE_SWAP_DEFINED + +#endif /* __CYGWIN__ || CONFIG_NATIVE_WINDOWS */ + + +#ifndef WPA_BYTE_SWAP_DEFINED + +#ifndef __BYTE_ORDER +#ifndef __LITTLE_ENDIAN +#ifndef __BIG_ENDIAN +#define __LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 +#if defined(sparc) +#define __BYTE_ORDER __BIG_ENDIAN +#endif +#endif /* __BIG_ENDIAN */ +#endif /* __LITTLE_ENDIAN */ +#endif /* __BYTE_ORDER */ + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define le_to_host16(n) ((__force u16) (le16) (n)) +#define host_to_le16(n) ((__force le16) (u16) (n)) +#define be_to_host16(n) __bswap_16((__force u16) (be16) (n)) +#define host_to_be16(n) ((__force be16) __bswap_16((n))) +#define le_to_host32(n) ((__force u32) (le32) (n)) +#define host_to_le32(n) ((__force le32) (u32) (n)) +#define be_to_host32(n) __bswap_32((__force u32) (be32) (n)) +#define host_to_be32(n) ((__force be32) __bswap_32((n))) +#define le_to_host64(n) ((__force u64) (le64) (n)) +#define host_to_le64(n) ((__force le64) (u64) (n)) +#define be_to_host64(n) __bswap_64((__force u64) (be64) (n)) +#define host_to_be64(n) ((__force be64) bswap_64((n))) +#elif __BYTE_ORDER == __BIG_ENDIAN +#define le_to_host16(n) __bswap_16(n) +#define host_to_le16(n) __bswap_16(n) +#define be_to_host16(n) (n) +#define host_to_be16(n) (n) +#define le_to_host32(n) __bswap_32(n) +#define be_to_host32(n) (n) +#define host_to_be32(n) (n) +#define le_to_host64(n) __bswap_64(n) +#define host_to_le64(n) __bswap_64(n) +#define be_to_host64(n) (n) +#define host_to_be64(n) (n) +#ifndef WORDS_BIGENDIAN +#define WORDS_BIGENDIAN +#endif +#else +#error Could not determine CPU byte order +#endif + +#define WPA_BYTE_SWAP_DEFINED +#endif /* !WPA_BYTE_SWAP_DEFINED */ + + +/* Macros for handling unaligned memory accesses */ + +#define WPA_GET_BE16(a) ((u16) (((a)[0] << 8) | (a)[1])) +#define WPA_PUT_BE16(a, val) \ + do { \ + (a)[0] = ((u16) (val)) >> 8; \ + (a)[1] = ((u16) (val)) & 0xff; \ + } while (0) + +#define WPA_GET_LE16(a) ((u16) (((a)[1] << 8) | (a)[0])) +#define WPA_PUT_LE16(a, val) \ + do { \ + (a)[1] = ((u16) (val)) >> 8; \ + (a)[0] = ((u16) (val)) & 0xff; \ + } while (0) + +#define WPA_GET_BE24(a) ((((u32) (a)[0]) << 16) | (((u32) (a)[1]) << 8) | \ + ((u32) (a)[2])) +#define WPA_PUT_BE24(a, val) \ + do { \ + (a)[0] = (u8) ((((u32) (val)) >> 16) & 0xff); \ + (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \ + (a)[2] = (u8) (((u32) (val)) & 0xff); \ + } while (0) + +#define WPA_GET_BE32(a) ((((u32) (a)[0]) << 24) | (((u32) (a)[1]) << 16) | \ + (((u32) (a)[2]) << 8) | ((u32) (a)[3])) +#define WPA_PUT_BE32(a, val) \ + do { \ + (a)[0] = (u8) ((((u32) (val)) >> 24) & 0xff); \ + (a)[1] = (u8) ((((u32) (val)) >> 16) & 0xff); \ + (a)[2] = (u8) ((((u32) (val)) >> 8) & 0xff); \ + (a)[3] = (u8) (((u32) (val)) & 0xff); \ + } while (0) + +#define WPA_GET_LE32(a) ((((u32) (a)[3]) << 24) | (((u32) (a)[2]) << 16) | \ + (((u32) (a)[1]) << 8) | ((u32) (a)[0])) +#define WPA_PUT_LE32(a, val) \ + do { \ + (a)[3] = (u8) ((((u32) (val)) >> 24) & 0xff); \ + (a)[2] = (u8) ((((u32) (val)) >> 16) & 0xff); \ + (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \ + (a)[0] = (u8) (((u32) (val)) & 0xff); \ + } while (0) + +#define WPA_GET_BE64(a) ((((u64) (a)[0]) << 56) | (((u64) (a)[1]) << 48) | \ + (((u64) (a)[2]) << 40) | (((u64) (a)[3]) << 32) | \ + (((u64) (a)[4]) << 24) | (((u64) (a)[5]) << 16) | \ + (((u64) (a)[6]) << 8) | ((u64) (a)[7])) +#define WPA_PUT_BE64(a, val) \ + do { \ + (a)[0] = (u8) (((u64) (val)) >> 56); \ + (a)[1] = (u8) (((u64) (val)) >> 48); \ + (a)[2] = (u8) (((u64) (val)) >> 40); \ + (a)[3] = (u8) (((u64) (val)) >> 32); \ + (a)[4] = (u8) (((u64) (val)) >> 24); \ + (a)[5] = (u8) (((u64) (val)) >> 16); \ + (a)[6] = (u8) (((u64) (val)) >> 8); \ + (a)[7] = (u8) (((u64) (val)) & 0xff); \ + } while (0) + +#define WPA_GET_LE64(a) ((((u64) (a)[7]) << 56) | (((u64) (a)[6]) << 48) | \ + (((u64) (a)[5]) << 40) | (((u64) (a)[4]) << 32) | \ + (((u64) (a)[3]) << 24) | (((u64) (a)[2]) << 16) | \ + (((u64) (a)[1]) << 8) | ((u64) (a)[0])) + + +#ifndef ETH_ALEN +#define ETH_ALEN 6 +#endif +//#ifndef IFNAMSIZ +//#define IFNAMSIZ 16 +//#endif +#ifndef ETH_P_ALL +#define ETH_P_ALL 0x0003 +#endif +#ifndef ETH_P_PAE +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#endif /* ETH_P_PAE */ +#ifndef ETH_P_EAPOL +#define ETH_P_EAPOL ETH_P_PAE +#endif /* ETH_P_EAPOL */ +#ifndef ETH_P_RSN_PREAUTH +#define ETH_P_RSN_PREAUTH 0x88c7 +#endif /* ETH_P_RSN_PREAUTH */ +#ifndef ETH_P_RRB +#define ETH_P_RRB 0x890D +#endif /* ETH_P_RRB */ + + +#ifdef __GNUC__ +#define PRINTF_FORMAT(a,b) __attribute__ ((format (printf, (a), (b)))) +#define STRUCT_PACKED __attribute__ ((packed)) +#else +#define PRINTF_FORMAT(a,b) +#define STRUCT_PACKED +#endif + +#ifdef CONFIG_ANSI_C_EXTRA + +/* inline - define as __inline or just define it to be empty, if needed */ +#ifdef CONFIG_NO_INLINE +#define inline +#else +#define inline __inline +#endif + +#ifndef __func__ +#define __func__ "__func__ not defined" +#endif + +#ifndef bswap_16 +#define bswap_16(a) ((((u16) (a) << 8) & 0xff00) | (((u16) (a) >> 8) & 0xff)) +#endif + +#ifndef bswap_32 +#define bswap_32(a) ((((u32) (a) << 24) & 0xff000000) | \ + (((u32) (a) << 8) & 0xff0000) | \ + (((u32) (a) >> 8) & 0xff00) | \ + (((u32) (a) >> 24) & 0xff)) +#endif + +#ifndef MSG_DONTWAIT +#define MSG_DONTWAIT 0 +#endif + +#ifdef _WIN32_WCE +void perror(const char *s); +#endif /* _WIN32_WCE */ + +#endif /* CONFIG_ANSI_C_EXTRA */ + +#ifndef MAC2STR +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" +#endif + +#ifndef BIT +#define BIT(x) (1 << (x)) +#endif + +/* + * Definitions for sparse validation + * (http://kernel.org/pub/linux/kernel/people/josh/sparse/) + */ +#ifdef __CHECKER__ +#define __force __attribute__((force)) +#define __bitwise __attribute__((bitwise)) +#else +#define __force +#define __bitwise +#endif + +typedef u16 __bitwise be16; +typedef u16 __bitwise le16; +typedef u32 __bitwise be32; +typedef u32 __bitwise le32; +typedef u64 __bitwise be64; +typedef u64 __bitwise le64; + +#ifndef __must_check +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +#define __must_check __attribute__((__warn_unused_result__)) +#else +#define __must_check +#endif /* __GNUC__ */ +#endif /* __must_check */ + +int hwaddr_aton(const char *txt, u8 *addr); +int hwaddr_aton2(const char *txt, u8 *addr); +int hexstr2bin(const char *hex, u8 *buf, size_t len); +void inc_byte_array(u8 *counter, size_t len); +void wpa_get_ntp_timestamp(u8 *buf); +int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len); +int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data, + size_t len); + +#ifdef CONFIG_NATIVE_WINDOWS +void wpa_unicode2ascii_inplace(TCHAR *str); +TCHAR * wpa_strdup_tchar(const char *str); +#else /* CONFIG_NATIVE_WINDOWS */ +#define wpa_unicode2ascii_inplace(s) do { } while (0) +#define wpa_strdup_tchar(s) strdup((s)) +#endif /* CONFIG_NATIVE_WINDOWS */ + +const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len); +char * wpa_config_parse_string(const char *value, size_t *len); + +static inline int is_zero_ether_addr(const u8 *a) +{ + return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]); +} + +extern const struct eth_addr ethbroadcast; +#define broadcast_ether_addr ðbroadcast + +#include "wpabuf.h" +#include "wpa_debug.h" + + +/* + * gcc 4.4 ends up generating strict-aliasing warnings about some very common + * networking socket uses that do not really result in a real problem and + * cannot be easily avoided with union-based type-punning due to struct + * definitions including another struct in system header files. To avoid having + * to fully disable strict-aliasing warnings, provide a mechanism to hide the + * typecast from aliasing for now. A cleaner solution will hopefully be found + * in the future to handle these cases. + */ +void * __hide_aliasing_typecast(void *foo); +#define aliasing_hide_typecast(a,t) (t *) __hide_aliasing_typecast((a)) + +#endif /* COMMON_H */ diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/include/wpa/wpa.h b/cpu/esp32/vendor/esp-idf/wpa_supplicant/include/wpa/wpa.h new file mode 100644 index 0000000000..0cbd445caf --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/include/wpa/wpa.h @@ -0,0 +1,186 @@ +/* + * wpa_supplicant - WPA definitions + * Copyright (c) 2003-2007, 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 WPA_H +#define WPA_H + +#include "rom/ets_sys.h" +#include "wpa/common.h" +#include "wpa/defs.h" +#include "wpa/wpa_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define WPA_SM_STATE(_sm) ((_sm)->wpa_state) + +struct wpa_sm; + +int wpa_sm_rx_eapol(u8 *src_addr, u8 *buf, u32 len); + +#define WPA_ASSERT ASSERT + +struct install_key { + int mic_errors_seen; /* Michael MIC errors with the current PTK */ + int keys_cleared; + enum wpa_alg alg; + u8 addr[ETH_ALEN]; + int key_idx; + int set_tx; + u8 seq[10]; + u8 key[32]; +}; + +/** + * struct wpa_sm - Internal WPA state machine data + */ +struct wpa_sm { + u8 pmk[PMK_LEN]; + size_t pmk_len; + + struct wpa_ptk ptk, tptk; + int ptk_set, tptk_set; + u8 snonce[WPA_NONCE_LEN]; + u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */ + int renew_snonce; + u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN]; + int rx_replay_counter_set; + u8 request_counter[WPA_REPLAY_COUNTER_LEN]; + + unsigned int pairwise_cipher; + unsigned int group_cipher; + unsigned int key_mgmt; + unsigned int mgmt_group_cipher; + + int rsn_enabled; /* Whether RSN is enabled in configuration */ + + int countermeasures; /*TKIP countermeasures state flag, 1:in countermeasures state*/ + ETSTimer cm_timer; + + u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */ + size_t assoc_wpa_ie_len; + + u8 eapol_version; + + int wpa_ptk_rekey; + u8 own_addr[ETH_ALEN]; + + u8 bssid[ETH_ALEN]; + + unsigned int proto; + enum wpa_states wpa_state; + + u8 *ap_wpa_ie, *ap_rsn_ie; + size_t ap_wpa_ie_len, ap_rsn_ie_len; + + struct install_key install_ptk; + struct install_key install_gtk; + int key_entry_valid; //present current avaliable entry for bssid, for pairkey:0,5,10,15,20, gtk: pairkey_no+i (i:1~4) + + struct pbuf *pb; + + void (* sendto) (struct pbuf *pb); + void (*config_assoc_ie) (u8 proto, u8 *assoc_buf, u32 assoc_wpa_ie_len); + void (*install_ppkey) (enum wpa_alg alg, u8 *addr, int key_idx, int set_tx, + u8 *seq, unsigned int seq_len, u8 *key, unsigned int key_len, int key_entry_valid); + void (*wpa_deauthenticate)(u8 reason_code); + void (*wpa_neg_complete)(void); + struct wpa_gtk_data gd; //used for calllback save param + u16 key_info; //used for txcallback param +}; + +struct l2_ethhdr { + u8 h_dest[ETH_ALEN]; + u8 h_source[ETH_ALEN]; + be16 h_proto; +} STRUCT_PACKED; + +/** + * set_key - Configure encryption key + * @ifname: Interface name (for multi-SSID/VLAN support) + * @priv: private driver interface data + * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, + * %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK); + * %WPA_ALG_NONE clears the key. + * @addr: address of the peer STA or ff:ff:ff:ff:ff:ff for + * broadcast/default keys + * @key_idx: key index (0..3), usually 0 for unicast keys; 0..4095 for + * IGTK + * @set_tx: configure this key as the default Tx key (only used when + * driver does not support separate unicast/individual key + * @seq: sequence number/packet number, seq_len octets, the next + * packet number to be used for in replay protection; configured + * for Rx keys (in most cases, this is only used with broadcast + * keys and set to zero for unicast keys) + * @seq_len: length of the seq, depends on the algorithm: + * TKIP: 6 octets, CCMP: 6 octets, IGTK: 6 octets + * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key, + * 8-byte Rx Mic Key + * @key_len: length of the key buffer in octets (WEP: 5 or 13, + * TKIP: 32, CCMP: 16, IGTK: 16) + * + * Returns: 0 on success, -1 on failure + * + * Configure the given key for the kernel driver. If the driver + * supports separate individual keys (4 default keys + 1 individual), + * addr can be used to determine whether the key is default or + * individual. If only 4 keys are supported, the default key with key + * index 0 is used as the individual key. STA must be configured to use + * it as the default Tx key (set_tx is set) and accept Rx for all the + * key indexes. In most cases, WPA uses only key indexes 1 and 2 for + * broadcast keys, so key index 0 is available for this kind of + * configuration. + * + * Please note that TKIP keys include separate TX and RX MIC keys and + * some drivers may expect them in different order than wpa_supplicant + * is using. If the TX/RX keys are swapped, all TKIP encrypted packets + * will tricker Michael MIC errors. This can be fixed by changing the + * order of MIC keys by swapping te bytes 16..23 and 24..31 of the key + * in driver_*.c set_key() implementation, see driver_ndis.c for an + * example on how this can be done. + */ + + +/** + * send_eapol - Optional function for sending EAPOL packets + * @priv: private driver interface data + * @dest: Destination MAC address + * @proto: Ethertype + * @data: EAPOL packet starting with IEEE 802.1X header + * @data_len: Size of the EAPOL packet + * + * Returns: 0 on success, -1 on failure + * + * This optional function can be used to override l2_packet operations + * with driver specific functionality. If this function pointer is set, + * l2_packet module is not used at all and the driver interface code is + * responsible for receiving and sending all EAPOL packets. The + * received EAPOL packets are sent to core code with EVENT_EAPOL_RX + * event. The driver interface is required to implement get_mac_addr() + * handler if send_eapol() is used. + */ + +#define KEYENTRY_TABLE_MAP(key_entry_valid) ((key_entry_valid)%5) + +void wpa_sm_set_state(enum wpa_states state); + +char * dup_binstr(const void *src, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* WPA_H */ + diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/include/wpa/wpa_debug.h b/cpu/esp32/vendor/esp-idf/wpa_supplicant/include/wpa/wpa_debug.h new file mode 100644 index 0000000000..895ae2c10f --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/include/wpa/wpa_debug.h @@ -0,0 +1,219 @@ +/* + * wpa_supplicant/hostapd / Debug prints + * Copyright (c) 2002-2007, 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 WPA_DEBUG_H +#define WPA_DEBUG_H + +#include "wpabuf.h" +#include "esp_log.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef ESPRESSIF_USE + +#define TAG "wpa" + +#define MSG_ERROR ESP_LOG_ERROR +#define MSG_WARNING ESP_LOG_WARN +#define MSG_INFO ESP_LOG_INFO +#define MSG_DEBUG ESP_LOG_DEBUG +#define MSG_MSGDUMP ESP_LOG_VERBOSE + +#else +enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR }; +#endif + +/** EAP authentication completed successfully */ +#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS " + +int wpa_debug_open_file(const char *path); +void wpa_debug_close_file(void); + +/** + * wpa_debug_printf_timestamp - Print timestamp for debug output + * + * This function prints a timestamp in seconds_from_1970.microsoconds + * format if debug output has been configured to include timestamps in debug + * messages. + */ +void wpa_debug_print_timestamp(void); + +/** + * wpa_printf - conditional printf + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. + * + * Note: New line '\n' is added to the end of the text when printing to stdout. + */ +#define DEBUG_PRINT +#define MSG_PRINT + +/** + * wpa_hexdump - conditional hex dump + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of buf is printed out has hex dump. + */ +#ifdef DEBUG_PRINT +#ifdef RIOT_VERSION +#define wpa_printf(level,fmt, args...) ESP_LOG_LEVEL_LOCAL(level, TAG, fmt "\n", ##args) +#else +#define wpa_printf(level,fmt, args...) ESP_LOG_LEVEL_LOCAL(level, TAG, fmt, ##args) +#endif + +static inline void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len) +{ + +} + +static inline void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, size_t len) +{ +} + + +void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len); + +static inline void wpa_hexdump_buf(int level, const char *title, + const struct wpabuf *buf) +{ + wpa_hexdump(level, title, wpabuf_head(buf), wpabuf_len(buf)); +} + +/** + * wpa_hexdump_key - conditional hex dump, hide keys + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of buf is printed out has hex dump. This works + * like wpa_hexdump(), but by default, does not include secret keys (passwords, + * etc.) in debug output. + */ +void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len); + + +static inline void wpa_hexdump_buf_key(int level, const char *title, + const struct wpabuf *buf) +{ + wpa_hexdump_key(level, title, wpabuf_head(buf), wpabuf_len(buf)); +} + +/** + * wpa_hexdump_ascii - conditional hex dump + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of buf is printed out has hex dump with both + * the hex numbers and ASCII characters (for printable range) are shown. 16 + * bytes per line will be shown. + */ +void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, + size_t len); + +/** + * wpa_hexdump_ascii_key - conditional hex dump, hide keys + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of buf is printed out has hex dump with both + * the hex numbers and ASCII characters (for printable range) are shown. 16 + * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by + * default, does not include secret keys (passwords, etc.) in debug output. + */ +void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, + size_t len); +#else +#define wpa_printf(level,fmt, args...) +#define wpa_hexdump(...) +#define wpa_hexdump_buf(...) +#define wpa_hexdump_key(...) +#define wpa_hexdump_buf_key(...) +#define wpa_hexdump_ascii(...) +#define wpa_hexdump_ascii_key(...) +#endif + +#define wpa_auth_logger +#define wpa_auth_vlogger + +/** + * wpa_msg - Conditional printf for default target and ctrl_iface monitors + * @ctx: Pointer to context data; this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. This function is like wpa_printf(), but it also sends the + * same message to all attached ctrl_iface monitors. + * + * Note: New line '\n' is added to the end of the text when printing to stdout. + */ +void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4); + +/** + * wpa_msg_ctrl - Conditional printf for ctrl_iface monitors + * @ctx: Pointer to context data; this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. + * This function is like wpa_msg(), but it sends the output only to the + * attached ctrl_iface monitors. In other words, it can be used for frequent + * events that do not need to be sent to syslog. + */ +void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...) +PRINTF_FORMAT(3, 4); + +typedef void (*wpa_msg_cb_func)(void *ctx, int level, const char *txt, + size_t len); + +typedef void (*eloop_timeout_handler)(void *eloop_data, void *user_ctx); + +int eloop_cancel_timeout(eloop_timeout_handler handler, + void *eloop_data, void *user_data); + +int eloop_register_timeout(unsigned int secs, unsigned int usecs, + eloop_timeout_handler handler, + void *eloop_data, void *user_data); + + +#ifdef __cplusplus +} +#endif + +#endif /* WPA_DEBUG_H */ diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/include/wpa/wpabuf.h b/cpu/esp32/vendor/esp-idf/wpa_supplicant/include/wpa/wpabuf.h new file mode 100644 index 0000000000..1e36526655 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/include/wpa/wpabuf.h @@ -0,0 +1,176 @@ +/* + * Dynamic data buffer + * Copyright (c) 2007-2009, 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 WPABUF_H +#define WPABUF_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Internal data structure for wpabuf. Please do not touch this directly from + * elsewhere. This is only defined in header file to allow inline functions + * from this file to access data. + */ +struct wpabuf { + size_t size; /* total size of the allocated buffer */ + size_t used; /* length of data in the buffer */ + u8 *ext_data; /* pointer to external data; NULL if data follows + * struct wpabuf */ + /* optionally followed by the allocated buffer */ +}; + + +int wpabuf_resize(struct wpabuf **buf, size_t add_len); +struct wpabuf * wpabuf_alloc(size_t len); +struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len); +struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len); +struct wpabuf * wpabuf_dup(const struct wpabuf *src); +void wpabuf_free(struct wpabuf *buf); +void * wpabuf_put(struct wpabuf *buf, size_t len); +struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b); +struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len); +void wpabuf_printf(struct wpabuf *buf, char *fmt, ...) PRINTF_FORMAT(2, 3); + + +/** + * wpabuf_size - Get the currently allocated size of a wpabuf buffer + * @buf: wpabuf buffer + * Returns: Currently allocated size of the buffer + */ +static inline size_t wpabuf_size(const struct wpabuf *buf) +{ + return buf->size; +} + +/** + * wpabuf_len - Get the current length of a wpabuf buffer data + * @buf: wpabuf buffer + * Returns: Currently used length of the buffer + */ +static inline size_t wpabuf_len(const struct wpabuf *buf) +{ + return buf->used; +} + +/** + * wpabuf_tailroom - Get size of available tail room in the end of the buffer + * @buf: wpabuf buffer + * Returns: Tail room (in bytes) of available space in the end of the buffer + */ +static inline size_t wpabuf_tailroom(const struct wpabuf *buf) +{ + return buf->size - buf->used; +} + +/** + * wpabuf_head - Get pointer to the head of the buffer data + * @buf: wpabuf buffer + * Returns: Pointer to the head of the buffer data + */ +static inline const void * wpabuf_head(const struct wpabuf *buf) +{ + if (buf->ext_data) + return buf->ext_data; + return buf + 1; +} + +static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf) +{ + return wpabuf_head(buf); +} + +/** + * wpabuf_mhead - Get modifiable pointer to the head of the buffer data + * @buf: wpabuf buffer + * Returns: Pointer to the head of the buffer data + */ +static inline void * wpabuf_mhead(struct wpabuf *buf) +{ + if (buf->ext_data) + return buf->ext_data; + return buf + 1; +} + +static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf) +{ + return wpabuf_mhead(buf); +} + +static inline void wpabuf_put_u8(struct wpabuf *buf, u8 data) +{ + u8 *pos = wpabuf_put(buf, 1); + *pos = data; +} + +static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data) +{ + u8 *pos = wpabuf_put(buf, 2); + WPA_PUT_LE16(pos, data); +} + +static inline void wpabuf_put_le32(struct wpabuf *buf, u32 data) +{ + u8 *pos = wpabuf_put(buf, 4); + WPA_PUT_LE32(pos, data); +} + +static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data) +{ + u8 *pos = wpabuf_put(buf, 2); + WPA_PUT_BE16(pos, data); +} + +static inline void wpabuf_put_be24(struct wpabuf *buf, u32 data) +{ + u8 *pos = wpabuf_put(buf, 3); + WPA_PUT_BE24(pos, data); +} + +static inline void wpabuf_put_be32(struct wpabuf *buf, u32 data) +{ + u8 *pos = wpabuf_put(buf, 4); + WPA_PUT_BE32(pos, data); +} + +static inline void wpabuf_put_data(struct wpabuf *buf, const void *data, + size_t len) +{ + if (data) + os_memcpy(wpabuf_put(buf, len), data, len); +} + +static inline void wpabuf_put_buf(struct wpabuf *dst, + const struct wpabuf *src) +{ + wpabuf_put_data(dst, wpabuf_head(src), wpabuf_len(src)); +} + +static inline void wpabuf_set(struct wpabuf *buf, const void *data, size_t len) +{ + buf->ext_data = (u8 *) data; + buf->size = buf->used = len; +} + +static inline void wpabuf_put_str(struct wpabuf *dst, const char *str) +{ + wpabuf_put_data(dst, str, os_strlen(str)); +} + +#ifdef __cplusplus +} +#endif + +#endif /* WPABUF_H */ diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/include/wpa2/eap_peer/eap.h b/cpu/esp32/vendor/esp-idf/wpa_supplicant/include/wpa2/eap_peer/eap.h new file mode 100644 index 0000000000..39e609670d --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/include/wpa2/eap_peer/eap.h @@ -0,0 +1,62 @@ +/* + * 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" + +#ifdef __cplusplus +extern "C" { +#endif + +struct eap_sm; + +struct eap_method_type { + int vendor; + EapType method; +}; + +extern u8 *g_wpa_anonymous_identity; +extern int g_wpa_anonymous_identity_len; +extern u8 *g_wpa_username; +extern int g_wpa_username_len; +extern const u8 *g_wpa_client_cert; +extern int g_wpa_client_cert_len; +extern const u8 *g_wpa_private_key; +extern int g_wpa_private_key_len; +extern const u8 *g_wpa_private_key_passwd; +extern int g_wpa_private_key_passwd_len; + +extern const u8 *g_wpa_ca_cert; +extern int g_wpa_ca_cert_len; + +extern u8 *g_wpa_password; +extern int g_wpa_password_len; + +extern u8 *g_wpa_new_password; +extern 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); + +#ifdef __cplusplus +} +#endif + +#endif /* EAP_H */ diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/include/wpa2/eap_peer/eap_i.h b/cpu/esp32/vendor/esp-idf/wpa_supplicant/include/wpa2/eap_peer/eap_i.h new file mode 100644 index 0000000000..d068fd2f95 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/include/wpa2/eap_peer/eap_i.h @@ -0,0 +1,151 @@ +/* + * 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 "wpa2/eap_peer/eap.h" +#include "wpa2/eap_peer/eap_common.h" +#include "wpa2/eap_peer/eap_config.h" +#include "esp_wpa2.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* 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; +}; + +extern 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); + +#ifdef __cplusplus +} +#endif + +#endif /* EAP_I_H */ diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/chap.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/chap.c new file mode 100644 index 0000000000..d31e1c03b1 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/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 wpa_md5_vector(3, addr, len, response); +} + +#endif /* CHAP_MD5 */ diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap.c new file mode 100644 index 0000000000..865da65470 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap.c @@ -0,0 +1,731 @@ +/* + * 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 + + +static bool gl_disable_time_check = true; +void eap_peer_config_deinit(struct eap_sm *sm); +void eap_peer_blob_deinit(struct eap_sm *sm); +void eap_deinit_prev_method(struct eap_sm *sm, const char *txt); + +extern bool ieee80211_unregister_wpa2_cb(void); + +#ifdef EAP_PEER_METHOD +static struct eap_method *eap_methods = NULL; + +const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method) +{ + struct eap_method *m; + for (m = eap_methods; m; m = m->next) { + if (m->vendor == vendor && m->method == method) + return m; + } + return NULL; +} + +const struct eap_method * eap_peer_get_methods(size_t *count) +{ + int c = 0; + struct eap_method *m; + + for (m = eap_methods; m; m = m->next) + c++; + + *count = c; + return eap_methods; +} + +EapType eap_peer_get_type(const char *name, int *vendor) +{ + struct eap_method *m; + for (m = eap_methods; m; m = m->next) { + if (os_strcmp(m->name, name) == 0) { + *vendor = m->vendor; + return m->method; + } + } + *vendor = EAP_VENDOR_IETF; + return EAP_TYPE_NONE; +} + +static int +eap_allowed_phase2_type(int vendor, int type) +{ + if (vendor != EAP_VENDOR_IETF) + return 0; + return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS && + type != EAP_TYPE_FAST; +} + +u32 eap_get_phase2_type(const char *name, int *vendor) +{ + int v; + u8 type = eap_peer_get_type(name, &v); + if (eap_allowed_phase2_type(v, type)) { + *vendor = v; + return type; + } + *vendor = EAP_VENDOR_IETF; + return EAP_TYPE_NONE; +} + +struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config, + size_t *count) +{ + struct eap_method_type *buf; + u32 method; + int vendor; + size_t mcount; + const struct eap_method *methods, *m; + + methods = eap_peer_get_methods(&mcount); + if (methods == NULL) + return NULL; + *count = 0; + buf = os_malloc(mcount * sizeof(struct eap_method_type)); + if (buf == NULL) + return NULL; + + for (m = methods; m; m = m->next) { + vendor = m->vendor; + method = m->method; + if (eap_allowed_phase2_type(vendor, method)) { + if (vendor == EAP_VENDOR_IETF && + method == EAP_TYPE_TLS && config && + config->private_key2 == NULL) + continue; + buf[*count].vendor = vendor; + buf[*count].method = method; + (*count)++; + } + } + + return buf; +} + +struct eap_method * eap_peer_method_alloc(int vendor, EapType method, + const char *name) +{ + struct eap_method *eap; + eap = (struct eap_method *)os_zalloc(sizeof(*eap)); + if (eap == NULL) + return NULL; + eap->vendor = vendor; + eap->method = method; + eap->name = name; + return eap; +} + +void eap_peer_method_free(struct eap_method *method) +{ + os_free(method); +} + +int eap_peer_method_register(struct eap_method *method) +{ + struct eap_method *m, *last = NULL; + + if (method == NULL || method->name == NULL) + return -1; + for (m = eap_methods; m; m = m->next) { + if (m->vendor == method->vendor && + m->method == method->method && + os_strcmp(m->name, method->name)) + return -2; + last = m; + } + if (last) + last->next = method; + else + eap_methods = method; + return 0; +} + +void eap_peer_unregister_methods(void) +{ + struct eap_method *m; + while (eap_methods) { + m = eap_methods; + eap_methods = eap_methods->next; + + if (m->free) + m->free(m); + else + eap_peer_method_free(m); + } +} + +int eap_peer_register_methods(void) +{ + int ret = 0; + +#ifdef EAP_MD5 + if (ret == 0) + ret = eap_peer_md5_register(); +#endif + +#ifdef EAP_TLS + if (ret == 0) + ret = eap_peer_tls_register(); +#endif + +#ifdef EAP_MSCHAPv2 + if (ret == 0) + ret = eap_peer_mschapv2_register(); +#endif + +#ifdef EAP_PEAP + if (ret == 0) + ret = eap_peer_peap_register(); +#endif + +#ifdef EAP_TTLS + if (ret == 0) + ret = eap_peer_ttls_register(); +#endif + + return ret; +} + +void eap_deinit_prev_method(struct eap_sm *sm, const char *txt) +{ + if (sm->m == NULL || sm->eap_method_priv == NULL) + return; + sm->m->deinit(sm, sm->eap_method_priv); + sm->eap_method_priv = NULL; + sm->m = NULL; +} + +struct wpabuf * eap_sm_build_identity_resp(struct eap_sm *sm, u8 id, int encrypted) +{ + const u8 *identity; + size_t identity_len; + struct wpabuf *eap_buf = NULL; + struct eap_peer_config *config = eap_get_config(sm); + + if (config == NULL) { + wpa_printf(MSG_ERROR, "EAP: Build Identity Resp-> configuration was not available\n"); + return NULL; + } + + if (sm->m && sm->m->get_identity) { + identity = sm->m->get_identity(sm, + sm->eap_method_priv, + &identity_len); + } else if (!encrypted && config->anonymous_identity) { + identity = config->anonymous_identity; + identity_len = config->anonymous_identity_len; + } else { + identity = config->identity; + identity_len = config->identity_len; + } + + if (identity == NULL) { + wpa_printf(MSG_ERROR, "EAP: Build Identity Resp-> identity was not available\n"); + return NULL; + } + + eap_buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, + identity_len, EAP_CODE_RESPONSE, id); + if (!eap_buf) { + return NULL; + } + + wpabuf_put_data(eap_buf, identity, identity_len); + return eap_buf; +} + +struct wpabuf * eap_sm_build_nak(struct eap_sm *sm, EapType type, u8 id) +{ + size_t count = 0; + int found = 0; + struct wpabuf *resp; + const struct eap_method *methods, *m; + + methods = eap_peer_get_methods(&count); + if (methods == NULL) + return NULL; + + if (type == EAP_TYPE_EXPANDED) { + /*Build Expanded NAK*/ + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EXPANDED, + 8 + 8 * (count + 1), EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + wpabuf_put_be24(resp, EAP_VENDOR_IETF); + wpabuf_put_be32(resp, EAP_TYPE_NAK); + } else { + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, + sizeof(struct eap_hdr) + 1 + count + 1, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + wpabuf_put(resp, 0); + } + + for (m = methods; m; m = m->next) { + if (type == EAP_TYPE_EXPANDED) { + wpabuf_put_u8(resp, EAP_TYPE_EXPANDED); + wpabuf_put_be24(resp, m->vendor); + wpabuf_put_be32(resp, m->method); + } else + wpabuf_put_u8(resp, 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); + sm->blob[0].len = g_wpa_client_cert_len; + sm->blob[0].data = g_wpa_client_cert; + } + + if (g_wpa_private_key && g_wpa_private_key_len) { + sm->blob[1].name = (char *)os_zalloc(BLOB_NAME_LEN+1); + if (sm->blob[1].name == NULL) { + ret = -2; + goto _out; + } + os_strncpy(sm->blob[1].name, PRIVATE_KEY_NAME, BLOB_NAME_LEN); + sm->blob[1].len = g_wpa_private_key_len; + sm->blob[1].data = g_wpa_private_key; + } + + if (g_wpa_ca_cert && g_wpa_ca_cert_len) { + sm->blob[2].name = (char *)os_zalloc(BLOB_NAME_LEN+1); + if (sm->blob[2].name == NULL) { + ret = -2; + goto _out; + } + os_strncpy(sm->blob[2].name, CA_CERT_NAME, BLOB_NAME_LEN); + sm->blob[2].len = g_wpa_ca_cert_len; + sm->blob[2].data = g_wpa_ca_cert; + } + + return 0; +_out: + for (i = 0; i < BLOB_NUM; i++) { + if (sm->blob[i].name) { + os_free(sm->blob[i].name); + sm->blob[i].name = NULL; + } + } + os_bzero(&sm->blob[0], sizeof(struct wpa_config_blob)*BLOB_NUM); + + return ret; +} + +void eap_peer_blob_deinit(struct eap_sm *sm) +{ + int i; + for (i = 0; i < BLOB_NUM; i++) { + if (sm->blob[i].name) { + os_free(sm->blob[i].name); + sm->blob[i].name = NULL; + } + } + os_bzero(&sm->blob[0], sizeof(struct wpa_config_blob)*BLOB_NUM); + + sm->config.client_cert = NULL; + sm->config.private_key = NULL; + sm->config.ca_cert = NULL; +} + +void eap_sm_abort(struct eap_sm *sm) +{ + wpabuf_free(sm->lastRespData); + sm->lastRespData = NULL; + //os_free(sm->eapKeyData); + //sm->eapKeyData = NULL; +} + +/** + * eap_get_config - Get current network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * Returns: Pointer to the current network configuration or %NULL if not found + * + * EAP peer methods should avoid using this function if they can use other + * access functions, like eap_get_config_identity() and + * eap_get_config_password(), that do not require direct access to + * struct eap_peer_config. + */ +struct eap_peer_config * eap_get_config(struct eap_sm *sm) +{ + return &sm->config; +} + +const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + *len = config->identity_len; + return config->identity; +} + +const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + *len = config->password_len; + return config->password; +} + +const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + + *len = config->password_len; + if (hash) + *hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH); + return config->password; +} + +const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + *len = config->new_password_len; + return config->new_password; +} +/** + * eap_get_config_blob - Get a named configuration blob + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @name: Name of the blob + * Returns: Pointer to blob data or %NULL if not found + */ +const struct wpa_config_blob * eap_get_config_blob(struct eap_sm *sm, + const char *name) +{ + int i; + + if (!sm) + return NULL; + + for (i = 0; i < BLOB_NUM; i++) { + if (sm->blob[i].name == NULL) + continue; + if (os_strncmp(name, sm->blob[i].name, BLOB_NAME_LEN) == 0) { + return &sm->blob[i]; + } + } + return NULL; +} + +esp_err_t esp_wifi_sta_wpa2_ent_set_cert_key(const unsigned char *client_cert, int client_cert_len, const unsigned char *private_key, int private_key_len, const unsigned char *private_key_passwd, int private_key_passwd_len) +{ + if (client_cert && client_cert_len > 0) { + g_wpa_client_cert = client_cert; + g_wpa_client_cert_len = client_cert_len; + } + if (private_key && private_key_len > 0) { + g_wpa_private_key = private_key; + g_wpa_private_key_len = private_key_len; + } + if (private_key_passwd && private_key_passwd_len > 0) { + g_wpa_private_key_passwd = private_key_passwd; + g_wpa_private_key_passwd_len = private_key_passwd_len; + } + + return ESP_OK; +} + +void esp_wifi_sta_wpa2_ent_clear_cert_key(void) +{ + ieee80211_unregister_wpa2_cb(); + + g_wpa_client_cert = NULL; + g_wpa_client_cert_len = 0; + g_wpa_private_key = NULL; + g_wpa_private_key_len = 0; + g_wpa_private_key_passwd = NULL; + g_wpa_private_key_passwd_len = 0; +} + +esp_err_t esp_wifi_sta_wpa2_ent_set_ca_cert(const unsigned char *ca_cert, int ca_cert_len) +{ + if (ca_cert && ca_cert_len > 0) { + g_wpa_ca_cert = ca_cert; + g_wpa_ca_cert_len = ca_cert_len; + } + + return ESP_OK; +} + +void esp_wifi_sta_wpa2_ent_clear_ca_cert(void) +{ + g_wpa_ca_cert = NULL; + g_wpa_ca_cert_len = 0; +} + +#define ANONYMOUS_ID_LEN_MAX 128 +esp_err_t esp_wifi_sta_wpa2_ent_set_identity(const unsigned char *identity, int len) +{ + if (len <= 0 || len > ANONYMOUS_ID_LEN_MAX) { + return ESP_ERR_INVALID_ARG; + } + + if (g_wpa_anonymous_identity) { + os_free(g_wpa_anonymous_identity); + g_wpa_anonymous_identity = NULL; + } + + g_wpa_anonymous_identity = (u8 *)os_zalloc(len); + if (g_wpa_anonymous_identity == NULL) { + return ESP_ERR_NO_MEM; + } + + os_memcpy(g_wpa_anonymous_identity, identity, len); + g_wpa_anonymous_identity_len = len; + + return ESP_OK; +} + +void esp_wifi_sta_wpa2_ent_clear_identity(void) +{ + if (g_wpa_anonymous_identity) + os_free(g_wpa_anonymous_identity); + + g_wpa_anonymous_identity = NULL; + g_wpa_anonymous_identity_len = 0; +} + +#define USERNAME_LEN_MAX 128 +esp_err_t esp_wifi_sta_wpa2_ent_set_username(const unsigned char *username, int len) +{ + if (len <= 0 || len > USERNAME_LEN_MAX) + return ESP_ERR_INVALID_ARG; + + if (g_wpa_username) { + os_free(g_wpa_username); + g_wpa_username = NULL; + } + + g_wpa_username = (u8 *)os_zalloc(len); + if (g_wpa_username == NULL) + return ESP_ERR_NO_MEM; + + os_memcpy(g_wpa_username, username, len); + g_wpa_username_len = len; + + return ESP_OK; +} + +void esp_wifi_sta_wpa2_ent_clear_username(void) +{ + if (g_wpa_username) + os_free(g_wpa_username); + + g_wpa_username = NULL; + g_wpa_username_len = 0; +} + +esp_err_t esp_wifi_sta_wpa2_ent_set_password(const unsigned char *password, int len) +{ + if (len <= 0) + return ESP_ERR_INVALID_ARG; + + if (g_wpa_password) { + os_free(g_wpa_password); + g_wpa_password = NULL; + } + + g_wpa_password = (u8 *)os_zalloc(len); + if (g_wpa_password == NULL) + return ESP_ERR_NO_MEM; + + os_memcpy(g_wpa_password, password, len); + g_wpa_password_len = len; + + return ESP_OK; +} + +void esp_wifi_sta_wpa2_ent_clear_password(void) +{ + if (g_wpa_password) + os_free(g_wpa_password); + g_wpa_password = NULL; + g_wpa_password_len = 0; +} + +esp_err_t esp_wifi_sta_wpa2_ent_set_new_password(const unsigned char *new_password, int len) +{ + if (len <= 0) + return ESP_ERR_INVALID_ARG; + + if (g_wpa_new_password) { + os_free(g_wpa_new_password); + g_wpa_new_password = NULL; + } + + g_wpa_new_password = (u8 *)os_zalloc(len); + if (g_wpa_new_password == NULL) + return ESP_ERR_NO_MEM; + + os_memcpy(g_wpa_new_password, new_password, len); + g_wpa_password_len = len; + + return ESP_OK; +} + +void esp_wifi_sta_wpa2_ent_clear_new_password(void) +{ + if (g_wpa_new_password) + os_free(g_wpa_new_password); + g_wpa_new_password = NULL; + g_wpa_new_password_len = 0; +} + +esp_err_t esp_wifi_sta_wpa2_ent_set_disable_time_check(bool disable) +{ + gl_disable_time_check = disable; + return ESP_OK; +} + +bool wifi_sta_get_enterprise_disable_time_check(void) +{ + return gl_disable_time_check; +} + +esp_err_t esp_wifi_sta_wpa2_ent_get_disable_time_check(bool *disable) +{ + *disable = wifi_sta_get_enterprise_disable_time_check(); + return ESP_OK; +} + diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap_common.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap_common.c new file mode 100644 index 0000000000..a1748b140f --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap_common.c @@ -0,0 +1,205 @@ +/* + * 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"); + return 0; + } + + len = be_to_host16(hdr->length); + if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) { + wpa_printf(MSG_INFO, "EAP: Invalid EAP length"); + return 0; + } + + return 1; +} + + +/** + * eap_hdr_validate - Validate EAP header + * @vendor: Expected EAP Vendor-Id (0 = IETF) + * @eap_type: Expected EAP type number + * @msg: EAP frame (starting with EAP header) + * @plen: Pointer to variable to contain the returned payload length + * Returns: Pointer to EAP payload (after type field), or %NULL on failure + * + * This is a helper function for EAP method implementations. This is usually + * called in the beginning of struct eap_method::process() function to verify + * that the received EAP request packet has a valid header. This function is + * able to process both legacy and expanded EAP headers and in most cases, the + * caller can just use the returned payload pointer (into *plen) for processing + * the payload regardless of whether the packet used the expanded EAP header or + * not. + */ +const u8 * eap_hdr_validate(int vendor, EapType eap_type, + const struct wpabuf *msg, size_t *plen) +{ + const struct eap_hdr *hdr; + const u8 *pos; + size_t len; + + if (!eap_hdr_len_valid(msg, 1)) + return NULL; + + hdr = wpabuf_head(msg); + len = be_to_host16(hdr->length); + pos = (const u8 *) (hdr + 1); + + if (*pos == EAP_TYPE_EXPANDED) { + int exp_vendor; + u32 exp_type; + if (len < sizeof(*hdr) + 8) { + wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP " + "length"); + return NULL; + } + pos++; + exp_vendor = WPA_GET_BE24(pos); + pos += 3; + exp_type = WPA_GET_BE32(pos); + pos += 4; + if (exp_vendor != vendor || exp_type != (u32) eap_type) { + wpa_printf(MSG_INFO, "EAP: Invalid expanded frame " + "type"); + return NULL; + } + + *plen = len - sizeof(*hdr) - 8; + return pos; + } else { + if (vendor != EAP_VENDOR_IETF || *pos != eap_type) { + wpa_printf(MSG_INFO, "EAP: Invalid frame type"); + return NULL; + } + *plen = len - sizeof(*hdr) - 1; + return pos + 1; + } +} + + +/** + * eap_msg_alloc - Allocate a buffer for an EAP message + * @vendor: Vendor-Id (0 = IETF) + * @type: EAP type + * @payload_len: Payload length in bytes (data after Type) + * @code: Message Code (EAP_CODE_*) + * @identifier: Identifier + * Returns: Pointer to the allocated message buffer or %NULL on error + * + * This function can be used to allocate a buffer for an EAP message and fill + * in the EAP header. This function is automatically using expanded EAP header + * if the selected Vendor-Id is not IETF. In other words, most EAP methods do + * not need to separately select which header type to use when using this + * function to allocate the message buffers. The returned buffer has room for + * payload_len bytes and has the EAP header and Type field already filled in. + */ +struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len, + u8 code, u8 identifier) +{ + struct wpabuf *buf; + struct eap_hdr *hdr; + size_t len; + + len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) + + payload_len; + buf = wpabuf_alloc(len); + if (buf == NULL) + return NULL; + + hdr = wpabuf_put(buf, sizeof(*hdr)); + hdr->code = code; + hdr->identifier = identifier; + hdr->length = host_to_be16(len); + + if (vendor == EAP_VENDOR_IETF) { + wpabuf_put_u8(buf, type); + } else { + wpabuf_put_u8(buf, EAP_TYPE_EXPANDED); + wpabuf_put_be24(buf, vendor); + wpabuf_put_be32(buf, type); + } + + return buf; +} + + +/** + * eap_update_len - Update EAP header length + * @msg: EAP message from eap_msg_alloc + * + * This function updates the length field in the EAP header to match with the + * current length for the buffer. This allows eap_msg_alloc() to be used to + * allocate a larger buffer than the exact message length (e.g., if exact + * message length is not yet known). + */ +void eap_update_len(struct wpabuf *msg) +{ + struct eap_hdr *hdr; + hdr = wpabuf_mhead(msg); + if (wpabuf_len(msg) < sizeof(*hdr)) + return; + hdr->length = host_to_be16(wpabuf_len(msg)); +} + + +/** + * eap_get_id - Get EAP Identifier from wpabuf + * @msg: Buffer starting with an EAP header + * Returns: The Identifier field from the EAP header + */ +u8 eap_get_id(const struct wpabuf *msg) +{ + const struct eap_hdr *eap; + + if (wpabuf_len(msg) < sizeof(*eap)) + return 0; + + eap = wpabuf_head(msg); + return eap->identifier; +} + + +/** + * eap_get_id - Get EAP Type from wpabuf + * @msg: Buffer starting with an EAP header + * Returns: The EAP Type after the EAP header + */ +EapType eap_get_type(const struct wpabuf *msg) +{ + if (wpabuf_len(msg) < sizeof(struct eap_hdr) + 1) + return EAP_TYPE_NONE; + + return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)]; +} diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap_mschapv2.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap_mschapv2.c new file mode 100644 index 0000000000..89d7b8fc19 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap_mschapv2.c @@ -0,0 +1,671 @@ +/* + * 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" + +#define MSCHAPV2_OP_CHALLENGE 1 +#define MSCHAPV2_OP_RESPONSE 2 +#define MSCHAPV2_OP_SUCCESS 3 +#define MSCHAPV2_OP_FAILURE 4 +#define MSCHAPV2_OP_CHANGE_PASSWORD 7 + +#define PASSWD_CHANGE_CHAL_LEN 16 +#define MSCHAPV2_KEY_LEN 16 + +#define ERROR_RESTRICTED_LOGON_HOURS 646 +#define ERROR_ACCT_DISABLED 647 +#define ERROR_PASSWD_EXPIRED 648 +#define ERROR_NO_DIALIN_PERMISSION 649 +#define ERROR_AUTHENTICATION_FAILURE 691 +#define ERROR_CHANGING_PASSWORD 709 + +struct eap_mschapv2_hdr { + u8 op_code; + u8 mschapv2_id; + u8 ms_length[2]; +} __packed; + +struct ms_response { + u8 peer_challenge[MSCHAPV2_CHAL_LEN]; + u8 reserved[8]; + u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN]; + u8 flags; +} __packed; + +struct ms_change_password { + u8 encr_password[516]; + u8 encr_hash[16]; + u8 peer_challenge[MSCHAPV2_CHAL_LEN]; + u8 reserved[8]; + u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN]; + u8 flags[2]; +} __packed; + +struct eap_mschapv2_data { + u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN]; + int auth_response_valid; + + int prev_error; + u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN]; + int passwd_change_challenge_valid; + int passwd_change_version; + + u8 *peer_challenge; + u8 *auth_challenge; + + int phase2; + u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; + int master_key_valid; + int success; + + struct wpabuf *prev_challenge; +}; + +static void +eap_mschapv2_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_mschapv2_data *data = priv; + + os_free(data->peer_challenge); + os_free(data->auth_challenge); + wpabuf_free(data->prev_challenge); + os_free(data); +} + +static void * +eap_mschapv2_init(struct eap_sm *sm) +{ + struct eap_mschapv2_data *data; + data = (struct eap_mschapv2_data *)os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + data->phase2 = sm->init_phase2; + + return data; +} + +static struct wpabuf * +eap_mschapv2_challenge_reply( + struct eap_sm *sm, struct eap_mschapv2_data *data, + u8 id, u8 mschapv2_id, const u8 *auth_challenge) +{ + struct wpabuf *resp; + struct eap_mschapv2_hdr *ms; + u8 *peer_challenge; + int ms_len; + struct ms_response *r; + size_t identity_len, password_len; + const u8 *identity, *password; + int pwhash; + + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generate Challenge Response\n"); + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password2(sm, &password_len, &pwhash); + if (identity == NULL || password == NULL) + return NULL; + + ms_len = sizeof(*ms) + 1 + sizeof(*r) + identity_len; + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, + ms_len, EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + ms = wpabuf_put(resp, sizeof(*ms)); + ms->op_code = MSCHAPV2_OP_RESPONSE; + ms->mschapv2_id = mschapv2_id; + if (data->prev_error) + ms->mschapv2_id++; + WPA_PUT_BE16(ms->ms_length, ms_len); + wpabuf_put_u8(resp, sizeof(*r)); + + /* Response */ + r = wpabuf_put(resp, sizeof(*r)); + peer_challenge = r->peer_challenge; + if (data->peer_challenge) { + peer_challenge = data->peer_challenge; + os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN); + } else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) { + wpabuf_free(resp); + return NULL; + } + os_memset(r->reserved, 0, 8); + if (data->auth_challenge) + auth_challenge = data->auth_challenge; + if (mschapv2_derive_response(identity, identity_len, password, + password_len, pwhash, auth_challenge, + peer_challenge, r->nt_response, + data->auth_response, data->master_key)) { + wpabuf_free(resp); + return NULL; + } + data->auth_response_valid = 1; + data->master_key_valid = 1; + + r->flags = 0; + + wpabuf_put_data(resp, identity, identity_len); + return resp; +} + +static struct wpabuf * +eap_mschapv2_challenge( + struct eap_sm *sm, struct eap_mschapv2_data *data, + struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, + size_t req_len, u8 id) +{ + size_t len, challenge_len; + const u8 *pos, *challenge; + + if (eap_get_config_identity(sm, &len) == NULL || + eap_get_config_password(sm, &len) == NULL) + return NULL; + + if (req_len < sizeof(*req) + 1) { + ret->ignore = true; + return NULL; + } + pos = (const u8 *)(req + 1); + challenge_len = *pos++; + len = req_len - sizeof(*req) - 1; + if (challenge_len != MSCHAPV2_CHAL_LEN) { + ret->ignore = true; + return NULL; + } + + if (len < challenge_len) { + ret->ignore = true; + return NULL; + } + + if (data->passwd_change_challenge_valid) + challenge = data->passwd_change_challenge; + else + challenge = pos; + pos += challenge_len; + len -= challenge_len; + + ret->ignore = false; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = true; + + return eap_mschapv2_challenge_reply(sm, data, id, req->mschapv2_id, + challenge); +} + +static void +eap_mschapv2_password_changed(struct eap_sm *sm, + struct eap_mschapv2_data *data) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config && config->new_password) { + data->prev_error = 0; + os_free(config->password); + if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + } else if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) { + config->password = os_malloc(16); + config->password_len = 16; + if (config->password) { + nt_password_hash(config->new_password, + config->new_password_len, + config->password); + } + os_free(config->new_password); + } else { + config->password = config->new_password; + config->password_len = config->new_password_len; + } + config->new_password = NULL; + config->new_password_len = 0; + } +} + +static struct wpabuf * +eap_mschapv2_success(struct eap_sm *sm, + struct eap_mschapv2_data *data, + struct eap_method_ret *ret, + const struct eap_mschapv2_hdr *req, + size_t req_len, u8 id) +{ + struct wpabuf *resp; + const u8 *pos; + size_t len; + + len = req_len - sizeof(*req); + pos = (const u8 *)(req + 1); + if (!data->auth_response_valid || + mschapv2_verify_auth_response(data->auth_response, pos, len)) { + ret->methodState = METHOD_NONE; + ret->decision = DECISION_FAIL; + return NULL; + } + pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN; + len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN; + while (len > 0 && *pos == ' ') { + pos++; + len--; + } + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1, + EAP_CODE_RESPONSE, id); + if (resp == NULL) { + ret->ignore = true; + return NULL; + } + + wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + ret->allowNotifications = false; + data->success = 1; + + if (data->prev_error == ERROR_PASSWD_EXPIRED) + eap_mschapv2_password_changed(sm, data); + + return resp; +} + +static int +eap_mschapv2_failure_txt(struct eap_sm *sm, + struct eap_mschapv2_data *data, char *txt) +{ + char *pos; + //char *msg = ""; + int retry = 1; + struct eap_peer_config *config = eap_get_config(sm); + + pos = txt; + + if (pos && os_strncmp(pos, "E=", 2) == 0) { + pos += 2; + data->prev_error = atoi(pos); + pos = (char *)os_strchr(pos, ' '); + if (pos) + pos++; + } + + if (pos && os_strncmp(pos, "R=", 2) == 0) { + pos += 2; + retry = atoi(pos); + pos = (char *)os_strchr(pos, ' '); + if (pos) + pos++; + } + + if (pos && os_strncmp(pos, "C=", 2) == 0) { + int hex_len; + pos += 2; + hex_len = (char *)os_strchr(pos, ' ') - (char *)pos; + if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) { + if (hexstr2bin(pos, data->passwd_change_challenge, + PASSWD_CHANGE_CHAL_LEN)) { + wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: invalid failure challenge\n"); + } else { + data->passwd_change_challenge_valid = 1; + } + } else { + wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: required challenge field " + "was not present in failure message\n"); + } + } + + if (pos && os_strncmp(pos, "V=", 2) == 0) { + pos += 2; + data->passwd_change_version = atoi(pos); + pos = (char *)os_strchr(pos, ' '); + if (pos) + pos++; + } + + if (pos && os_strncmp(pos, "M=", 2) == 0) { + pos += 2; + //msg = pos; + } + #if 0 + wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error %d)", + msg, retry == 1? "" : "not ", data->prev_error); + #endif + if (data->prev_error == ERROR_PASSWD_EXPIRED && + data->passwd_change_version == 3 && config) { + if (config->new_password == NULL) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Password expired - " + "password change reqired\n"); + //eap_sm_request_new_password(sm); + } + } else if (retry == 1 && config) { + if (!config->mschapv2_retry) + //eap_sm_request_identity(sm); + //eap_sm_request_password(sm); + config->mschapv2_retry = 1; + } else if (config) { + config->mschapv2_retry = 0; + } + + return retry == 1; +} + +static struct wpabuf * +eap_mschapv2_change_password( + struct eap_sm *sm, struct eap_mschapv2_data *data, + struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id) +{ + struct wpabuf *resp; + int ms_len; + const u8 *username, *password, *new_password; + size_t username_len, password_len, new_password_len; + struct eap_mschapv2_hdr *ms; + struct ms_change_password *cp; + u8 password_hash[16], password_hash_hash[16]; + int pwhash; + + username = eap_get_config_identity(sm, &username_len); + password = eap_get_config_password2(sm, &password_len, &pwhash); + new_password = eap_get_config_new_password(sm, &new_password_len); + if (username == NULL || password == NULL || new_password == NULL) + return NULL; + + username = mschapv2_remove_domain(username, &username_len); + + ret->ignore = false; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_COND_SUCC; + ret->allowNotifications = TRUE; + + ms_len = sizeof(*ms) + sizeof(*cp); + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + ms = wpabuf_put(resp, sizeof(*ms)); + ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD; + ms->mschapv2_id = req->mschapv2_id + 1; + WPA_PUT_BE16(ms->ms_length, ms_len); + cp = wpabuf_put(resp, sizeof(*cp)); + + if (pwhash) { + if (encrypt_pw_block_with_password_hash( + new_password, new_password_len, + password, cp->encr_password)) + goto fail; + } else { + if (new_password_encrypted_with_old_nt_password_hash( + new_password, new_password_len, + password, password_len, cp->encr_password)) + goto fail; + } + + if (pwhash) { + u8 new_password_hash[16]; + nt_password_hash(new_password, new_password_len, + new_password_hash); + nt_password_hash_encrypted_with_block(password, + new_password_hash, + cp->encr_hash); + } else { + old_nt_password_hash_encrypted_with_new_nt_password_hash( + new_password, new_password_len, + password, password_len, cp->encr_hash); + } + + if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN)) + goto fail; + + os_memset(cp->reserved, 0, 8); + + generate_nt_response(data->passwd_change_challenge, cp->peer_challenge, + username, username_len, new_password, + new_password_len, cp->nt_response); + + generate_authenticator_response(new_password, new_password_len, + cp->peer_challenge, + data->passwd_change_challenge, + username, username_len, + cp->nt_response, data->auth_response); + data->auth_response_valid = 1; + + nt_password_hash(new_password, new_password_len, password_hash); + hash_nt_password_hash(password_hash, password_hash_hash); + get_master_key(password_hash_hash, cp->nt_response, data->master_key); + data->master_key_valid = 1; + + os_memset(cp->flags, 0, 2); + + return resp; + +fail: + wpabuf_free(resp); + return NULL; +} + +static struct wpabuf * +eap_mschapv2_failure(struct eap_sm *sm, + struct eap_mschapv2_data *data, + struct eap_method_ret *ret, + const struct eap_mschapv2_hdr *req, + size_t req_len, u8 id) +{ + struct wpabuf *resp; + const u8 *msdata = (const u8 *)(req + 1); + char *buf; + size_t len = req_len - sizeof(*req); + int retry = 0; + + buf = (char *)dup_binstr(msdata, len); + if (buf) { + retry = eap_mschapv2_failure_txt(sm, data, buf); + os_free(buf); + } + + ret->ignore = false; + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + ret->allowNotifications = false; + + if (data->prev_error == ERROR_PASSWD_EXPIRED && + data->passwd_change_version == 3) { + struct eap_peer_config *config = eap_get_config(sm); + if (config && config->new_password) + return eap_mschapv2_change_password(sm, data, ret, + req, id); + //if (config && config->pending_req_new_password) + // return NULL; + } else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) { + return NULL; + } + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); + return resp; +} + +static int +eap_mschapv2_check_config(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + + if (config == NULL) + return -1; + + if (config->identity == NULL || + config->identity_len == 0) { + wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: idetity not configured\n"); + return -1; + } + + if (config->password == NULL || + config->password_len == 0) { + wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Password not configured\n"); + return -1; + } + + return 0; +} + +static int +eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len, + const struct eap_mschapv2_hdr *ms) +{ + size_t ms_len = WPA_GET_BE16(ms->ms_length); + + if (ms_len == len) + return 0; + + if (sm->workaround) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Workaround, ignore Invalid" + " header len=%lu ms_len=%lu\n", + (unsigned long)len, (unsigned long)ms_len); + return 0; + } + wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Invalid header len=%lu ms_len=%lu\n", + (unsigned long)len, (unsigned long)ms_len); + + return -1; +} + +static void +eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data, + const struct wpabuf *reqData) +{ + wpabuf_free(data->prev_challenge); + data->prev_challenge = wpabuf_dup(reqData); +} + +static struct wpabuf * +eap_mschapv2_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + u8 id; + size_t len; + const u8 *pos; + int using_prev_challenge = 0; + const struct eap_mschapv2_hdr *ms; + struct eap_mschapv2_data *data = priv; + struct eap_peer_config *config = eap_get_config(sm); + + if (eap_mschapv2_check_config(sm)) { + ret->ignore = true; + return NULL; + } + + if (config->mschapv2_retry && data->prev_challenge && + data->prev_error == ERROR_AUTHENTICATION_FAILURE) { + reqData = data->prev_challenge; + using_prev_challenge = 1; + config->mschapv2_retry = 0; + } + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, + reqData, &len); + if (pos == NULL || len < sizeof(*ms) + 1) { + ret->ignore = true; + return NULL; + } + + ms = (const struct eap_mschapv2_hdr *)pos; + if (eap_mschapv2_check_mslen(sm, len, ms)) { + ret->ignore = true; + return NULL; + } + + id = eap_get_id(reqData); + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d\n", + id, ms->mschapv2_id); + switch (ms->op_code) { + case MSCHAPV2_OP_CHALLENGE: + if (!using_prev_challenge) + eap_mschapv2_copy_challenge(data, reqData); + return eap_mschapv2_challenge(sm, data, ret, ms, len, id); + case MSCHAPV2_OP_SUCCESS: + return eap_mschapv2_success(sm, data, ret, ms, len, id); + case MSCHAPV2_OP_FAILURE: + return eap_mschapv2_failure(sm, data, ret, ms, len, id); + default: + wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Unknow op code %d -ignored\n", + ms->op_code); + return NULL; + } +} + +static bool +eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_mschapv2_data *data = priv; + return data->success && data->master_key_valid; +} + +static u8 * +eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_mschapv2_data *data = priv; + u8 *key; + int key_len; + + if (!data->master_key_valid || !data->success) + return NULL; + + key_len = 2 * MSCHAPV2_KEY_LEN; + + key = os_malloc(key_len); + + /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, + * peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */ + get_asymetric_start_key(data->master_key, key, + MSCHAPV2_KEY_LEN, 1, 0); + get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, + MSCHAPV2_KEY_LEN, 0, 0); + + *len = key_len; + return key; +} + +int +eap_peer_mschapv2_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, + "MSCHAPV2"); + + if (eap == NULL) + return -1; + + eap->init = eap_mschapv2_init; + eap->deinit = eap_mschapv2_deinit; + eap->process = eap_mschapv2_process; + eap->isKeyAvailable = eap_mschapv2_isKeyAvailable; + eap->getKey = eap_mschapv2_getKey; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} + +#endif /* EAP_MSCHAPv2 */ diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap_peap.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap_peap.c new file mode 100644 index 0000000000..0aee5f0e6b --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap_peap.c @@ -0,0 +1,1357 @@ +/* + * 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" + +/* 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]); + wpa_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)); + wpa_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[24]; + 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"; + strcpy(label, "client PEAP encryption"); + else + //label = "client EAP encryption"; + strcpy(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 */ diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap_peap_common.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap_peap_common.c new file mode 100644 index 0000000000..7bd19884ab --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap_peap_common.c @@ -0,0 +1,90 @@ +/* + * 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 (wpa_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/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap_tls.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap_tls.c new file mode 100644 index 0000000000..00dabfe40a --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap_tls.c @@ -0,0 +1,232 @@ +/* + * 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" + +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/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap_tls_common.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap_tls_common.c new file mode 100644 index 0000000000..ce07ec7d8f --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap_tls_common.c @@ -0,0 +1,1064 @@ +/* + * 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_INFO, "TLS: Include TLS Message Length in " + "unfragmented packets"); + data->include_tls_length = 1; + } + + return 0; +} + + +/** + * eap_peer_tls_ssl_deinit - Deinitialize shared TLS functionality + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * + * This function deinitializes shared TLS functionality that was initialized + * with eap_peer_tls_ssl_init(). + */ +void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) +{ + tls_connection_deinit(data->ssl_ctx, data->conn); + eap_peer_tls_reset_input(data); + eap_peer_tls_reset_output(data); +} + + +/** + * eap_peer_tls_derive_key - Derive a key based on TLS session data + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @label: Label string for deriving the keys, e.g., "client EAP encryption" + * @len: Length of the key material to generate (usually 64 for MSK) + * Returns: Pointer to allocated key on success or %NULL on failure + * + * This function uses TLS-PRF to generate pseudo-random data based on the TLS + * session data (client/server random and master key). Each key type may use a + * different label to bind the key usage into the generated material. + * + * The caller is responsible for freeing the returned buffer. + */ +u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, + const char *label, size_t len) +{ + struct tls_keys keys; + u8 *rnd = NULL, *out; + + out = os_malloc(len); + if (out == NULL) + return NULL; + + /* First, try to use TLS library function for PRF, if available. */ + if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, out, len) + == 0) + return out; + + /* + * TLS library did not support key generation, so get the needed TLS + * session parameters and use an internal implementation of TLS PRF to + * derive the key. + */ + if (tls_connection_get_keys(data->ssl_ctx, data->conn, &keys)) + goto fail; + + if (keys.client_random == NULL || keys.server_random == NULL || + keys.master_key == NULL) + goto fail; + + rnd = os_malloc(keys.client_random_len + keys.server_random_len); + if (rnd == NULL) + goto fail; + os_memcpy(rnd, keys.client_random, keys.client_random_len); + os_memcpy(rnd + keys.client_random_len, keys.server_random, + keys.server_random_len); + + if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len, + label, rnd, keys.client_random_len + + keys.server_random_len, out, len)) { + goto fail; + } + + os_free(rnd); + return out; + +fail: + os_free(out); + os_free(rnd); + return NULL; +} + + +/** + * eap_peer_tls_derive_session_id - Derive a Session-Id based on TLS data + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) + * @len: Pointer to length of the session ID generated + * Returns: Pointer to allocated Session-Id on success or %NULL on failure + * + * This function derive the Session-Id based on the TLS session data + * (client/server random and method type). + * + * The caller is responsible for freeing the returned buffer. + */ +u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, + struct eap_ssl_data *data, u8 eap_type, + size_t *len) +{ + struct tls_keys keys; + u8 *out; + + /* + * TLS library did not support session ID generation, + * so get the needed TLS session parameters + */ + if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) + return NULL; + + if (keys.client_random == NULL || keys.server_random == NULL || + keys.master_key == NULL) + return NULL; + + *len = 1 + keys.client_random_len + keys.server_random_len; + out = os_malloc(*len); + if (out == NULL) + return NULL; + + /* Session-Id = EAP type || client.random || server.random */ + out[0] = eap_type; + os_memcpy(out + 1, keys.client_random, keys.client_random_len); + os_memcpy(out + 1 + keys.client_random_len, keys.server_random, + keys.server_random_len); + + return out; +} + + +/** + * eap_peer_tls_reassemble_fragment - Reassemble a received fragment + * @data: Data for TLS processing + * @in_data: Next incoming TLS segment + * Returns: 0 on success, 1 if more data is needed for the full message, or + * -1 on error + */ +static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data, + const struct wpabuf *in_data) +{ + size_t tls_in_len, in_len; + + tls_in_len = data->tls_in ? wpabuf_len(data->tls_in) : 0; + in_len = in_data ? wpabuf_len(in_data) : 0; + + if (tls_in_len + in_len == 0) { + /* No message data received?! */ + wpa_printf(MSG_WARNING, "SSL: Invalid reassembly state: " + "tls_in_left=%lu tls_in_len=%lu in_len=%lu", + (unsigned long) data->tls_in_left, + (unsigned long) tls_in_len, + (unsigned long) in_len); + eap_peer_tls_reset_input(data); + return -1; + } + + if (tls_in_len + in_len > 65536) { + /* + * Limit length to avoid rogue servers from causing large + * memory allocations. + */ + wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size over " + "64 kB)"); + eap_peer_tls_reset_input(data); + return -1; + } + + if (in_len > data->tls_in_left) { + /* Sender is doing something odd - reject message */ + wpa_printf(MSG_INFO, "SSL: more data than TLS message length " + "indicated"); + eap_peer_tls_reset_input(data); + return -1; + } + + if (wpabuf_resize(&data->tls_in, in_len) < 0) { + wpa_printf(MSG_INFO, "SSL: Could not allocate memory for TLS " + "data"); + eap_peer_tls_reset_input(data); + return -1; + } + if (in_data) + wpabuf_put_buf(data->tls_in, in_data); + data->tls_in_left -= in_len; + + if (data->tls_in_left > 0) { + wpa_printf(MSG_INFO, "SSL: Need %lu bytes more input " + "data", (unsigned long) data->tls_in_left); + return 1; + } + + return 0; +} + + +/** + * eap_peer_tls_data_reassemble - Reassemble TLS data + * @data: Data for TLS processing + * @in_data: Next incoming TLS segment + * @need_more_input: Variable for returning whether more input data is needed + * to reassemble this TLS packet + * Returns: Pointer to output data, %NULL on error or when more data is needed + * for the full message (in which case, *need_more_input is also set to 1). + * + * This function reassembles TLS fragments. Caller must not free the returned + * data buffer since an internal pointer to it is maintained. + */ +static const struct wpabuf * eap_peer_tls_data_reassemble( + struct eap_ssl_data *data, const struct wpabuf *in_data, + int *need_more_input) +{ + *need_more_input = 0; + + if (data->tls_in_left > wpabuf_len(in_data) || data->tls_in) { + /* Message has fragments */ + int res = eap_peer_tls_reassemble_fragment(data, in_data); + if (res) { + if (res == 1) + *need_more_input = 1; + return NULL; + } + + /* Message is now fully reassembled. */ + } else { + /* No fragments in this message, so just make a copy of it. */ + data->tls_in_left = 0; + data->tls_in = wpabuf_dup(in_data); + if (data->tls_in == NULL) + return NULL; + } + + return data->tls_in; +} + + +/** + * eap_tls_process_input - Process incoming TLS message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @in_data: Message received from the server + * @in_len: Length of in_data + * @out_data: Buffer for returning a pointer to application data (if available) + * Returns: 0 on success, 1 if more input data is needed, 2 if application data + * is available, -1 on failure + */ +static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data, + const u8 *in_data, size_t in_len, + struct wpabuf **out_data) +{ + const struct wpabuf *msg; + int need_more_input; + struct wpabuf *appl_data; + struct wpabuf buf; + + wpabuf_set(&buf, in_data, in_len); + msg = eap_peer_tls_data_reassemble(data, &buf, &need_more_input); + if (msg == NULL) + return need_more_input ? 1 : -1; + + /* Full TLS message reassembled - continue handshake processing */ + if (data->tls_out) { + /* This should not happen.. */ + wpa_printf(MSG_INFO, "SSL: eap_tls_process_input - pending " + "tls_out data even though tls_out_len = 0"); + wpabuf_free(data->tls_out); + //WPA_ASSERT(data->tls_out == NULL); + } + appl_data = NULL; + data->tls_out = tls_connection_handshake(data->ssl_ctx, data->conn, + msg, &appl_data); + + eap_peer_tls_reset_input(data); + if (appl_data && + tls_connection_established(data->ssl_ctx, data->conn) && + !tls_connection_get_failed(data->ssl_ctx, data->conn)) { + wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application data", + appl_data); + *out_data = appl_data; + return 2; + } + + wpabuf_free(appl_data); + + return 0; +} + + +/** + * eap_tls_process_output - Process outgoing TLS message + * @data: Data for TLS processing + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) + * @peap_version: Version number for EAP-PEAP/TTLS + * @id: EAP identifier for the response + * @ret: Return value to use on success + * @out_data: Buffer for returning the allocated output buffer + * Returns: ret (0 or 1) on success, -1 on failure + */ +static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type, + int peap_version, u8 id, int ret, + struct wpabuf **out_data) +{ + size_t len; + u8 *flags; + int more_fragments, length_included; + + if (data->tls_out == NULL) + return -1; + len = wpabuf_len(data->tls_out) - data->tls_out_pos; + wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total " + "%lu bytes)", + (unsigned long) len, + (unsigned long) wpabuf_len(data->tls_out)); + + /* + * Limit outgoing message to the configured maximum size. Fragment + * message if needed. + */ + if (len > data->tls_out_limit) { + more_fragments = 1; + len = data->tls_out_limit; + wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments " + "will follow", (unsigned long) len); + } else + more_fragments = 0; + + length_included = data->tls_out_pos == 0 && + (wpabuf_len(data->tls_out) > data->tls_out_limit || + data->include_tls_length); + if (!length_included && + eap_type == EAP_TYPE_PEAP && peap_version == 0 && + !tls_connection_established(data->eap->ssl_ctx, data->conn)) { + /* + * Windows Server 2008 NPS really wants to have the TLS Message + * length included in phase 0 even for unfragmented frames or + * it will get very confused with Compound MAC calculation and + * Outer TLVs. + */ + length_included = 1; + } + + *out_data = eap_tls_msg_alloc(eap_type, 1 + length_included * 4 + len, + EAP_CODE_RESPONSE, id); + if (*out_data == NULL) { + printf("[Debug] out_data is null, return \n"); + return -1; + } + + flags = wpabuf_put(*out_data, 1); + *flags = peap_version; + if (more_fragments) + *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS; + if (length_included) { + *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED; + wpabuf_put_be32(*out_data, wpabuf_len(data->tls_out)); + } + wpabuf_put_data(*out_data, + wpabuf_head_u8(data->tls_out) + data->tls_out_pos, + len); + data->tls_out_pos += len; + + if (!more_fragments) + eap_peer_tls_reset_output(data); + + return ret; +} + + +/** + * eap_peer_tls_process_helper - Process TLS handshake message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) + * @peap_version: Version number for EAP-PEAP/TTLS + * @id: EAP identifier for the response + * @in_data: Message received from the server + * @in_len: Length of in_data + * @out_data: Buffer for returning a pointer to the response message + * Returns: 0 on success, 1 if more input data is needed, 2 if application data + * is available, or -1 on failure + * + * This function can be used to process TLS handshake messages. It reassembles + * the received fragments and uses a TLS library to process the messages. The + * response data from the TLS library is fragmented to suitable output messages + * that the caller can send out. + * + * out_data is used to return the response message if the return value of this + * function is 0, 2, or -1. In case of failure, the message is likely a TLS + * alarm message. The caller is responsible for freeing the allocated buffer if + * *out_data is not %NULL. + * + * This function is called for each received TLS message during the TLS + * handshake after eap_peer_tls_process_init() call and possible processing of + * TLS Flags field. Once the handshake has been completed, i.e., when + * tls_connection_established() returns 1, EAP method specific decrypting of + * the tunneled data is used. + */ +int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, + EapType eap_type, int peap_version, + u8 id, const u8 *in_data, size_t in_len, + struct wpabuf **out_data) +{ + int ret = 0; + + *out_data = NULL; + + if (data->tls_out && wpabuf_len(data->tls_out) > 0 && in_len > 0) { + wpa_printf(MSG_DEBUG, "SSL: Received non-ACK when output " + "fragments are waiting to be sent out"); + return -1; + } + + if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) { + /* + * No more data to send out - expect to receive more data from + * the AS. + */ + int res = eap_tls_process_input(sm, data, in_data, in_len, + out_data); + if (res) { + /* + * Input processing failed (res = -1) or more data is + * needed (res = 1). + */ + return res; + } + + /* + * The incoming message has been reassembled and processed. The + * response was allocated into data->tls_out buffer. + */ + } + + if (data->tls_out == NULL) { + /* + * No outgoing fragments remaining from the previous message + * and no new message generated. This indicates an error in TLS + * processing. + */ + eap_peer_tls_reset_output(data); + return -1; + } + + if (tls_connection_get_failed(data->ssl_ctx, data->conn)) { + /* TLS processing has failed - return error */ + wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to " + "report error"); + ret = -1; + /* TODO: clean pin if engine used? */ + } + + if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) { + /* + * TLS negotiation should now be complete since all other cases + * needing more data should have been caught above based on + * the TLS Message Length field. + */ + wpa_printf(MSG_DEBUG, "SSL: No data to be sent out"); + wpabuf_free(data->tls_out); + data->tls_out = NULL; + return 1; + } + + /* Send the pending message (in fragments, if needed). */ + return eap_tls_process_output(data, eap_type, peap_version, id, ret, + out_data); +} + + +/** + * eap_peer_tls_build_ack - Build a TLS ACK frame + * @id: EAP identifier for the response + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) + * @peap_version: Version number for EAP-PEAP/TTLS + * Returns: Pointer to the allocated ACK frame or %NULL on failure + */ +struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type, + int peap_version) +{ + struct wpabuf *resp; + + resp = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + wpa_printf(MSG_DEBUG, "SSL: Building ACK (type=%d id=%d ver=%d) \n", + (int) eap_type, id, peap_version); + wpabuf_put_u8(resp, peap_version); /* Flags */ + return resp; +} + + +/** + * eap_peer_tls_reauth_init - Re-initialize shared TLS for session resumption + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * Returns: 0 on success, -1 on failure + */ +int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data) +{ + eap_peer_tls_reset_input(data); + eap_peer_tls_reset_output(data); + return tls_connection_shutdown(data->ssl_ctx, data->conn); +} + + +/** + * eap_peer_tls_status - Get TLS status + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf. + */ +int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, + char *buf, size_t buflen, int verbose) +{ + char name[128]; + int len = 0, ret; + + if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) == 0) + { + //ret = os_snprintf(buf + len, buflen - len, + ret = sprintf(buf + len, + "EAP TLS cipher=%s\n", name); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + } + + return len; +} + + +/** + * eap_peer_tls_process_init - Initial validation/processing of EAP requests + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) + * @ret: Return values from EAP request validation and processing + * @reqData: EAP request to be processed (eapReqData) + * @len: Buffer for returning length of the remaining payload + * @flags: Buffer for returning TLS flags + * Returns: Pointer to payload after TLS flags and length or %NULL on failure + * + * This function validates the EAP header and processes the optional TLS + * Message Length field. If this is the first fragment of a TLS message, the + * TLS reassembly code is initialized to receive the indicated number of bytes. + * + * EAP-TLS, EAP-PEAP, EAP-TTLS, and EAP-FAST methods are expected to use this + * function as the first step in processing received messages. They will need + * to process the flags (apart from Message Length Included) that are returned + * through the flags pointer and the message payload that will be returned (and + * the length is returned through the len pointer). Return values (ret) are set + * for continuation of EAP method processing. The caller is responsible for + * setting these to indicate completion (either success or failure) based on + * the authentication result. + */ +const u8 * eap_peer_tls_process_init(struct eap_sm *sm, + struct eap_ssl_data *data, + EapType eap_type, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + size_t *len, u8 *flags) +{ + const u8 *pos; + size_t left; + unsigned int tls_msg_len; + + if (tls_get_errors(data->ssl_ctx)) { + wpa_printf(MSG_INFO, "SSL: TLS errors detected"); + ret->ignore = TRUE; + return NULL; + } + + if (eap_type == EAP_UNAUTH_TLS_TYPE) + pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, reqData, + &left); + else + pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData, + &left); + if (pos == NULL) { + ret->ignore = TRUE; + return NULL; + } + if (left == 0) { + wpa_printf(MSG_DEBUG, "SSL: Invalid TLS message: no Flags " + "octet included"); + if (!sm->workaround) { + ret->ignore = TRUE; + return NULL; + } + + wpa_printf(MSG_DEBUG, "SSL: Workaround - assume no Flags " + "indicates ACK frame"); + *flags = 0; + } else { + *flags = *pos++; + left--; + } + wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - " + "Flags 0x%02x", (unsigned long) wpabuf_len(reqData), + *flags); + if (*flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { + if (left < 4) { + wpa_printf(MSG_INFO, "SSL: Short frame with TLS " + "length"); + ret->ignore = TRUE; + return NULL; + } + tls_msg_len = WPA_GET_BE32(pos); + wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d", + tls_msg_len); + if (data->tls_in_left == 0) { + data->tls_in_total = tls_msg_len; + data->tls_in_left = tls_msg_len; + wpabuf_free(data->tls_in); + data->tls_in = NULL; + } + pos += 4; + left -= 4; + + if (left > tls_msg_len) { + wpa_printf(MSG_INFO, "SSL: TLS Message Length (%d " + "bytes) smaller than this fragment (%d " + "bytes)", (int) tls_msg_len, (int) left); + ret->ignore = TRUE; + return NULL; + } + } + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + *len = left; + return pos; +} + + +/** + * eap_peer_tls_reset_input - Reset input buffers + * @data: Data for TLS processing + * + * This function frees any allocated memory for input buffers and resets input + * state. + */ +void eap_peer_tls_reset_input(struct eap_ssl_data *data) +{ + data->tls_in_left = data->tls_in_total = 0; + wpabuf_free(data->tls_in); + data->tls_in = NULL; +} + + +/** + * eap_peer_tls_reset_output - Reset output buffers + * @data: Data for TLS processing + * + * This function frees any allocated memory for output buffers and resets + * output state. + */ +void eap_peer_tls_reset_output(struct eap_ssl_data *data) +{ + data->tls_out_pos = 0; + wpabuf_free(data->tls_out); + data->tls_out = NULL; +} + + +/** + * eap_peer_tls_decrypt - Decrypt received phase 2 TLS message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @in_data: Message received from the server + * @in_decrypted: Buffer for returning a pointer to the decrypted message + * Returns: 0 on success, 1 if more input data is needed, or -1 on failure + */ +int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data, + const struct wpabuf *in_data, + struct wpabuf **in_decrypted) +{ + const struct wpabuf *msg; + int need_more_input; + + msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input); + if (msg == NULL) + return need_more_input ? 1 : -1; + + *in_decrypted = tls_connection_decrypt(data->ssl_ctx, data->conn, msg); + eap_peer_tls_reset_input(data); + if (*in_decrypted == NULL) { + wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data"); + return -1; + } + return 0; +} + + +/** + * eap_peer_tls_encrypt - Encrypt phase 2 TLS message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) + * @peap_version: Version number for EAP-PEAP/TTLS + * @id: EAP identifier for the response + * @in_data: Plaintext phase 2 data to encrypt or %NULL to continue fragments + * @out_data: Buffer for returning a pointer to the encrypted response message + * Returns: 0 on success, -1 on failure + */ +int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data, + EapType eap_type, int peap_version, u8 id, + const struct wpabuf *in_data, + struct wpabuf **out_data) +{ + if (in_data) { + eap_peer_tls_reset_output(data); + data->tls_out = tls_connection_encrypt(data->ssl_ctx, + data->conn, in_data); + if (data->tls_out == NULL) { + wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 " + "data (in_len=%lu)", + (unsigned long) wpabuf_len(in_data)); + eap_peer_tls_reset_output(data); + return -1; + } + } + + return eap_tls_process_output(data, eap_type, peap_version, id, 0, + out_data); +} + +/** + * eap_peer_select_phase2_methods - Select phase 2 EAP method + * @config: Pointer to the network configuration + * @prefix: 'phase2' configuration prefix, e.g., "auth=" + * @types: Buffer for returning allocated list of allowed EAP methods + * @num_types: Buffer for returning number of allocated EAP methods + * Returns: 0 on success, -1 on failure + * + * This function is used to parse EAP method list and select allowed methods + * for Phase2 authentication. + */ +int eap_peer_select_phase2_methods(struct eap_peer_config *config, + const char *prefix, + struct eap_method_type **types, + size_t *num_types) +{ + char *start, *pos, *buf; + struct eap_method_type *methods = NULL, *_methods; + u8 method; + size_t num_methods = 0, prefix_len; + + if (config == NULL || config->phase2 == NULL) + goto get_defaults; + + start = buf = os_strdup(config->phase2); + if (buf == NULL) + return -1; + + prefix_len = os_strlen(prefix); + + while (start && *start != '\0') { + int vendor; + pos = os_strstr(start, prefix); + if (pos == NULL) + break; + if (start != pos && *(pos - 1) != ' ') { + start = pos + prefix_len; + continue; + } + + start = pos + prefix_len; + pos = (char *)os_strchr(start, ' '); + if (pos) + *pos++ = '\0'; + method = eap_get_phase2_type(start, &vendor); + if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_NONE) { + wpa_printf(MSG_INFO, "TLS: Unsupported Phase2 EAP " + "method '%s'\n", start); + } else { + num_methods++; + _methods = (struct eap_method_type *)os_realloc(methods, + num_methods * sizeof(*methods)); + if (_methods == NULL) { + os_free(methods); + os_free(buf); + return -1; + } + methods = _methods; + methods[num_methods - 1].vendor = vendor; + methods[num_methods - 1].method = method; + } + + start = pos; + } + + os_free(buf); + +get_defaults: + if (methods == NULL) + methods = eap_get_phase2_types(config, &num_methods); + if (methods == NULL) { + wpa_printf(MSG_ERROR, "TLS: No Phase EAP methods available\n"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "TLS: Phase2 EAP types", + (u8 *) methods, + num_methods * sizeof(struct eap_method_type)); + + *types = methods; + *num_types = num_methods; + + return 0; +} + +/** + * eap_peer_tls_phase2_nak - Generate EAP-Nak for Phase 2 + * @types: Buffer for returning allocated list of allowed EAP methods + * @num_types: Buffer for returning number of allocated EAP methods + * @hdr: EAP-Request header (and the following EAP type octet) + * @resp: Buffer for returning the EAP-Nak message + * Returns: 0 on success, -1 on failure + */ +int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types, + struct eap_hdr *hdr, struct wpabuf **resp) +{ + u8 *pos = (u8 *) (hdr + 1); + size_t i; + + (void)pos; /* to satisfy the compiler */ + /* TODO: add support for expanded Nak */ + wpa_printf(MSG_DEBUG, "TLS: Phase Request: Nak type=%d\n", *pos); + wpa_hexdump(MSG_DEBUG, "TLS: Allowed Phase2 EAP types", + (u8 *) types, num_types * sizeof(struct eap_method_type)); + *resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, num_types, + EAP_CODE_RESPONSE, hdr->identifier); + if (*resp == NULL) + return -1; + + for (i = 0; i < num_types; i++) { + if (types[i].vendor == EAP_VENDOR_IETF && + types[i].method < 256) + wpabuf_put_u8(*resp, types[i].method); + } + + eap_update_len(*resp); + + return 0; +} diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap_ttls.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap_ttls.c new file mode 100644 index 0000000000..6e8984b350 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/eap_ttls.c @@ -0,0 +1,1673 @@ +/* + * 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 */ diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/mschapv2.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/mschapv2.c new file mode 100644 index 0000000000..33351989b5 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/eap_peer/mschapv2.c @@ -0,0 +1,97 @@ +/* + * MSCHAPV2 + */ + +#ifdef EAP_MSCHAPv2 + +#include "wpa/includes.h" +#include "wpa/common.h" +#include "crypto/ms_funcs.h" +#include "wpa2/eap_peer/mschapv2.h" + +const u8 * mschapv2_remove_domain(const u8 *username, size_t *len) +{ + size_t i; + + /* + * MSCHAPV2 does not include optional domain name in the + * challenge-response calculation, so remove domain prefix + * (if present) + */ + for (i = 0; i < *len; i++) { + if (username[i] == '\\') { + *len -= i + 1; + return username + i + 1; + } + } + + return username; +} + +int mschapv2_derive_response(const u8 *identity, size_t identity_len, + const u8 *password, size_t password_len, + int pwhash, + const u8 *auth_challenge, + const u8 *peer_challenge, + u8 *nt_response, u8 *auth_response, + u8 *master_key) +{ + const u8 *username; + size_t username_len; + u8 password_hash[16], password_hash_hash[16]; + + username_len = identity_len; + username = mschapv2_remove_domain(identity, &username_len); + + if (pwhash) { + if (generate_nt_response_pwhash(auth_challenge, peer_challenge, + username, username_len, + password, nt_response) || + generate_authenticator_response_pwhash( + password, peer_challenge, auth_challenge, + username, username_len, nt_response, + auth_response)) + return -1; + } else { + if (generate_nt_response(auth_challenge, peer_challenge, + username, username_len, + password, password_len, + nt_response) || + generate_authenticator_response(password, password_len, + peer_challenge, + auth_challenge, + username, username_len, + nt_response, + auth_response)) + return -1; + } + + if (pwhash) { + if (hash_nt_password_hash(password, password_hash_hash)) + return -1; + } else { + if (nt_password_hash(password, password_len, password_hash) || + hash_nt_password_hash(password_hash, password_hash_hash)) + return -1; + } + if (get_master_key(password_hash_hash, nt_response, master_key)) + return -1; + + return 0; +} + +int mschapv2_verify_auth_response(const u8 *auth_response, + const u8 *buf, size_t buf_len) +{ + u8 recv_response[MSCHAPV2_AUTH_RESPONSE_LEN]; + if (buf_len < 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN || + buf[0] != 'S' || buf[1] != '=' || + hexstr2bin((char *)(buf + 2), recv_response, + MSCHAPV2_AUTH_RESPONSE_LEN) || + os_memcmp(auth_response, recv_response, + MSCHAPV2_AUTH_RESPONSE_LEN) != 0) + return -1; + return 0; +} + +#endif /* EAP_MSCHAPv2 */ diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/asn1.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/asn1.c new file mode 100644 index 0000000000..77f4318373 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/asn1.c @@ -0,0 +1,207 @@ +/* + * 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_DEBUG, "ASN.1: Extended tag data: " + "0x%02x", tmp); + hdr->tag = (hdr->tag << 7) | (tmp & 0x7f); + } while (tmp & 0x80); + } else + hdr->tag = hdr->identifier & 0x1f; + + tmp = *pos++; + if (tmp & 0x80) { + if (tmp == 0xff) { + wpa_printf(MSG_DEBUG, "ASN.1: Reserved length " + "value 0xff used"); + return -1; + } + tmp &= 0x7f; /* number of subsequent octets */ + hdr->length = 0; + if (tmp > 4) { + wpa_printf(MSG_DEBUG, "ASN.1: Too long length field"); + return -1; + } + while (tmp--) { + if (pos >= end) { + wpa_printf(MSG_DEBUG, "ASN.1: Length " + "underflow"); + return -1; + } + hdr->length = (hdr->length << 8) | *pos++; + } + } else { + /* Short form - length 0..127 in one octet */ + hdr->length = tmp; + } + + if (end < pos || hdr->length > (unsigned int) (end - pos)) { + wpa_printf(MSG_DEBUG, "ASN.1: Contents underflow"); + return -1; + } + + hdr->payload = pos; + return 0; +} + + +int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid) +{ + const u8 *pos, *end; + unsigned long val; + u8 tmp; + + os_memset(oid, 0, sizeof(*oid)); + + pos = buf; + end = buf + len; + + while (pos < end) { + val = 0; + + do { + if (pos >= end) + return -1; + tmp = *pos++; + val = (val << 7) | (tmp & 0x7f); + } while (tmp & 0x80); + + if (oid->len >= ASN1_MAX_OID_LEN) { + wpa_printf(MSG_DEBUG, "ASN.1: Too long OID value"); + return -1; + } + if (oid->len == 0) { + /* + * The first octet encodes the first two object + * identifier components in (X*40) + Y formula. + * X = 0..2. + */ + oid->oid[0] = val / 40; + if (oid->oid[0] > 2) + oid->oid[0] = 2; + oid->oid[1] = val - oid->oid[0] * 40; + oid->len = 2; + } else + oid->oid[oid->len++] = val; + } + + return 0; +} + + +int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid, + const u8 **next) +{ + struct asn1_hdr hdr; + + if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0) + return -1; + + if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OID) { + wpa_printf(MSG_DEBUG, "ASN.1: Expected OID - found class %d " + "tag 0x%x", hdr.class, hdr.tag); + return -1; + } + + *next = hdr.payload + hdr.length; + + return asn1_parse_oid(hdr.payload, hdr.length, oid); +} + + +void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len) +{ + char *pos = buf; + size_t i; + int ret; + + if (len == 0) + return; + + buf[0] = '\0'; + + for (i = 0; i < oid->len; i++) { + //ret = os_snprintf(pos, buf + len - pos, + ret = sprintf(pos, + "%s%lu", + i == 0 ? "" : ".", oid->oid[i]); + if (ret < 0 || ret >= buf + len - pos) + break; + pos += ret; + } + buf[len - 1] = '\0'; +} + + +static u8 rotate_bits(u8 octet) +{ + int i; + u8 res; + + res = 0; + for (i = 0; i < 8; i++) { + res <<= 1; + if (octet & 1) + res |= 1; + octet >>= 1; + } + + return res; +} + + +unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len) +{ + unsigned long val = 0; + const u8 *pos = buf; + + /* BER requires that unused bits are zero, so we can ignore the number + * of unused bits */ + pos++; + + if (len >= 2) + val |= rotate_bits(*pos++); + if (len >= 3) + val |= ((unsigned long) rotate_bits(*pos++)) << 8; + if (len >= 4) + val |= ((unsigned long) rotate_bits(*pos++)) << 16; + if (len >= 5) + val |= ((unsigned long) rotate_bits(*pos++)) << 24; + if (len >= 6) { + wpa_printf(MSG_DEBUG, "X509: %s - some bits ignored " + "(BIT STRING length %lu)", + __func__, (unsigned long) len); + } + return val; +} diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/bignum.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/bignum.c new file mode 100644 index 0000000000..aef8f953b3 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/bignum.c @@ -0,0 +1,244 @@ +/* + * 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 "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/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/pkcs1.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/pkcs1.c new file mode 100644 index 0000000000..6266806bc1 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/pkcs1.c @@ -0,0 +1,195 @@ +/* + * 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/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/pkcs5.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/pkcs5.c new file mode 100644 index 0000000000..785fe86d18 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/pkcs5.c @@ -0,0 +1,262 @@ +/* + * 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" + +#include "wpa2/eap_peer/eap_i.h" + +struct pkcs5_params { + enum pkcs5_alg { + PKCS5_ALG_UNKNOWN, + PKCS5_ALG_MD5_DES_CBC + } alg; + u8 salt[8]; + size_t salt_len; + unsigned int iter_count; +}; + + +static enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid) +{ + if (oid->len == 7 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */ && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 5 /* pkcs-5 */ && + oid->oid[6] == 3 /* pbeWithMD5AndDES-CBC */) + return PKCS5_ALG_MD5_DES_CBC; + + return PKCS5_ALG_UNKNOWN; +} + + +static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len, + struct pkcs5_params *params) +{ + struct asn1_hdr hdr; + const u8 *enc_alg_end, *pos, *end; + struct asn1_oid oid; + char obuf[80]; + + /* AlgorithmIdentifier */ + + enc_alg_end = enc_alg + enc_alg_len; + + os_memset(params, 0, sizeof(*params)); + + if (asn1_get_oid(enc_alg, enc_alg_end - enc_alg, &oid, &pos)) { + wpa_printf(MSG_DEBUG, "PKCS #5: Failed to parse OID " + "(algorithm)"); + return -1; + } + + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #5: encryption algorithm %s", obuf); + params->alg = pkcs5_get_alg(&oid); + if (params->alg == PKCS5_ALG_UNKNOWN) { + wpa_printf(MSG_INFO, "PKCS #5: unsupported encryption " + "algorithm %s", obuf); + return -1; + } + + /* + * PKCS#5, Section 8 + * PBEParameter ::= SEQUENCE { + * salt OCTET STRING SIZE(8), + * iterationCount INTEGER } + */ + + if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "PKCS #5: Expected SEQUENCE " + "(PBEParameter) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* salt OCTET STRING SIZE(8) */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING || + hdr.length != 8) { + wpa_printf(MSG_DEBUG, "PKCS #5: Expected OCTETSTRING SIZE(8) " + "(salt) - found class %d tag 0x%x size %d", + hdr.class, hdr.tag, hdr.length); + return -1; + } + pos = hdr.payload + hdr.length; + os_memcpy(params->salt, hdr.payload, hdr.length); + params->salt_len = hdr.length; + wpa_hexdump(MSG_DEBUG, "PKCS #5: salt", + params->salt, params->salt_len); + + /* iterationCount INTEGER */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, "PKCS #5: Expected INTEGER - found " + "class %d tag 0x%x", hdr.class, hdr.tag); + return -1; + } + if (hdr.length == 1) + params->iter_count = *hdr.payload; + else if (hdr.length == 2) + params->iter_count = WPA_GET_BE16(hdr.payload); + else if (hdr.length == 4) + params->iter_count = WPA_GET_BE32(hdr.payload); + else { + wpa_hexdump(MSG_DEBUG, "PKCS #5: Unsupported INTEGER value " + " (iterationCount)", + hdr.payload, hdr.length); + return -1; + } + wpa_printf(MSG_DEBUG, "PKCS #5: iterationCount=0x%x", + params->iter_count); + if (params->iter_count == 0 || params->iter_count > 0xffff) { + wpa_printf(MSG_INFO, "PKCS #5: Unsupported " + "iterationCount=0x%x", params->iter_count); + return -1; + } + + return 0; +} + + +static struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params, + const char *passwd) +{ + unsigned int i; + u8 hash[MD5_MAC_LEN]; + const u8 *addr[2]; + size_t len[2]; + + if (params->alg != PKCS5_ALG_MD5_DES_CBC) { + return NULL; + } + + addr[0] = (const u8 *) passwd; + len[0] = os_strlen(passwd); + addr[1] = params->salt; + len[1] = params->salt_len; + if (wpa_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 (wpa_md5_vector(1, addr, len, hash) < 0) + return NULL; + } + /* TODO: DES key parity bits(?) */ + wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES key", hash, 8); + wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES IV", hash + 8, 8); + + if (wpa2_crypto_funcs.crypto_cipher_init) { + return wpa2_crypto_funcs.crypto_cipher_init(CRYPTO_CIPHER_ALG_DES, hash + 8, hash, 8); + } else { + wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_cipher_init function! \r\n", __FUNCTION__); + return NULL; + } +} + +u8 * pkcs5_decrypt(const u8 *enc_alg, size_t enc_alg_len, + const u8 *enc_data, size_t enc_data_len, + const char *passwd, size_t *data_len) +{ + struct crypto_cipher *ctx = NULL; + u8 *eb, pad; + struct pkcs5_params params; + unsigned int i; + + if (pkcs5_get_params(enc_alg, enc_alg_len, ¶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); + if (wpa2_crypto_funcs.crypto_cipher_deinit) { + wpa2_crypto_funcs.crypto_cipher_deinit(ctx); + } else { + wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n"); + return NULL; + } + return NULL; + } + + eb = os_malloc(enc_data_len); + if (eb == NULL) { + if (wpa2_crypto_funcs.crypto_cipher_deinit) { + wpa2_crypto_funcs.crypto_cipher_deinit(ctx); + } else { + wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n"); + return NULL; + } + return NULL; + } + + if (wpa2_crypto_funcs.crypto_cipher_decrypt) { + if ((int)wpa2_crypto_funcs.crypto_cipher_decrypt(ctx, enc_data, eb, enc_data_len) < 0) { + wpa_printf(MSG_DEBUG, "PKCS #5: Failed to decrypt EB"); + wpa2_crypto_funcs.crypto_cipher_deinit(ctx); + os_free(eb); + return NULL; + } + } else { + wpa_printf(MSG_ERROR, "Fail to register crypto cipher decrypt function.\r\n"); + wpa2_crypto_funcs.crypto_cipher_deinit(ctx); + os_free(eb); + return NULL; + } + + if (wpa2_crypto_funcs.crypto_cipher_deinit) { + wpa2_crypto_funcs.crypto_cipher_deinit(ctx); + } else { + wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n"); + return NULL; + } + + pad = eb[enc_data_len - 1]; + if (pad > 8) { + wpa_printf(MSG_INFO, "PKCS #5: Invalid PS octet 0x%x", pad); + os_free(eb); + return NULL; + } + for (i = enc_data_len - pad; i < enc_data_len; i++) { + if (eb[i] != pad) { + wpa_hexdump(MSG_INFO, "PKCS #5: Invalid PS", + eb + enc_data_len - pad, pad); + os_free(eb); + return NULL; + } + } + + wpa_hexdump_key(MSG_MSGDUMP, "PKCS #5: message M (encrypted key)", + eb, enc_data_len - pad); + + *data_len = enc_data_len - pad; + return eb; +} diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/pkcs8.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/pkcs8.c new file mode 100644 index 0000000000..0f39c4558e --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/pkcs8.c @@ -0,0 +1,186 @@ +/* + * 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" + +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/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/rsa.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/rsa.c new file mode 100644 index 0000000000..0b09d4d807 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/rsa.c @@ -0,0 +1,353 @@ +/* + * 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 "soc/dport_reg.h" + +struct crypto_rsa_key { + int private_key; /* whether private key is set */ + struct bignum *n; /* modulus (p * q) */ + struct bignum *e; /* public exponent */ + /* The following parameters are available only if private_key is set */ + struct bignum *d; /* private exponent */ + struct bignum *p; /* prime p (factor of n) */ + struct bignum *q; /* prime q (factor of n) */ + struct bignum *dmp1; /* d mod (p - 1); CRT exponent */ + struct bignum *dmq1; /* d mod (q - 1); CRT exponent */ + struct bignum *iqmp; /* 1 / q mod p; CRT coefficient */ +}; + + +static const u8 * crypto_rsa_parse_integer(const u8 *pos, const u8 *end, + struct bignum *num) +{ + struct asn1_hdr hdr; + + if (pos == NULL) + return NULL; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, "RSA: Expected INTEGER - found class %d " + "tag 0x%x", hdr.class, hdr.tag); + return NULL; + } + + if (bignum_set_unsigned_bin(num, hdr.payload, hdr.length) < 0) { + wpa_printf(MSG_DEBUG, "RSA: Failed to parse INTEGER"); + return NULL; + } + + return hdr.payload + hdr.length; +} + + +/** + * crypto_rsa_import_public_key - Import an RSA public key + * @buf: Key buffer (DER encoded RSA public key) + * @len: Key buffer length in bytes + * Returns: Pointer to the public key or %NULL on failure + */ +struct crypto_rsa_key * +crypto_rsa_import_public_key(const u8 *buf, size_t len) +{ + struct crypto_rsa_key *key; + struct asn1_hdr hdr; + const u8 *pos, *end; + + key = (struct crypto_rsa_key *)os_zalloc(sizeof(*key)); + if (key == NULL) + return NULL; + + key->n = bignum_init(); + key->e = bignum_init(); + if (key->n == NULL || key->e == NULL) { + crypto_rsa_free(key); + return NULL; + } + + /* + * PKCS #1, 7.1: + * RSAPublicKey ::= SEQUENCE { + * modulus INTEGER, -- n + * publicExponent INTEGER -- e + * } + */ + + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE " + "(public key) - found class %d tag 0x%x", + hdr.class, hdr.tag); + goto error; + } + pos = hdr.payload; + end = pos + hdr.length; + + pos = crypto_rsa_parse_integer(pos, end, key->n); + pos = crypto_rsa_parse_integer(pos, end, key->e); + + if (pos == NULL) + goto error; + + if (pos != end) { + wpa_hexdump(MSG_DEBUG, + "RSA: Extra data in public key SEQUENCE", + pos, end - pos); + goto error; + } + + return key; + +error: + crypto_rsa_free(key); + return NULL; +} + + +/** + * crypto_rsa_import_private_key - Import an RSA private key + * @buf: Key buffer (DER encoded RSA private key) + * @len: Key buffer length in bytes + * Returns: Pointer to the private key or %NULL on failure + */ +struct crypto_rsa_key * +crypto_rsa_import_private_key(const u8 *buf, size_t len) +{ + struct crypto_rsa_key *key; + struct bignum *zero; + struct asn1_hdr hdr; + const u8 *pos, *end; + + key = (struct crypto_rsa_key *)os_zalloc(sizeof(*key)); + if (key == NULL) + return NULL; + + key->private_key = 1; + + key->n = bignum_init(); + key->e = bignum_init(); + key->d = bignum_init(); + key->p = bignum_init(); + key->q = bignum_init(); + key->dmp1 = bignum_init(); + key->dmq1 = bignum_init(); + key->iqmp = bignum_init(); + + if (key->n == NULL || key->e == NULL || key->d == NULL || + key->p == NULL || key->q == NULL || key->dmp1 == NULL || + key->dmq1 == NULL || key->iqmp == NULL) { + crypto_rsa_free(key); + return NULL; + } + + /* + * PKCS #1, 7.2: + * RSAPrivateKey ::= SEQUENCE { + * version Version, + * modulus INTEGER, -- n + * publicExponent INTEGER, -- e + * privateExponent INTEGER, -- d + * prime1 INTEGER, -- p + * prime2 INTEGER, -- q + * exponent1 INTEGER, -- d mod (p-1) + * exponent2 INTEGER, -- d mod (q-1) + * coefficient INTEGER -- (inverse of q) mod p + * } + * + * Version ::= INTEGER -- shall be 0 for this version of the standard + */ + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE " + "(public key) - found class %d tag 0x%x", + hdr.class, hdr.tag); + goto error; + } + pos = hdr.payload; + end = pos + hdr.length; + + zero = bignum_init(); + if (zero == NULL) + goto error; + pos = crypto_rsa_parse_integer(pos, end, zero); + if (pos == NULL || bignum_cmp_d(zero, 0) != 0) { + wpa_printf(MSG_DEBUG, "RSA: Expected zero INTEGER in the " + "beginning of private key; not found"); + bignum_deinit(zero); + goto error; + } + bignum_deinit(zero); + + pos = crypto_rsa_parse_integer(pos, end, key->n); + pos = crypto_rsa_parse_integer(pos, end, key->e); + pos = crypto_rsa_parse_integer(pos, end, key->d); + pos = crypto_rsa_parse_integer(pos, end, key->p); + pos = crypto_rsa_parse_integer(pos, end, key->q); + pos = crypto_rsa_parse_integer(pos, end, key->dmp1); + pos = crypto_rsa_parse_integer(pos, end, key->dmq1); + pos = crypto_rsa_parse_integer(pos, end, key->iqmp); + + if (pos == NULL) + goto error; + + if (pos != end) { + wpa_hexdump(MSG_DEBUG, + "RSA: Extra data in public key SEQUENCE", + pos, end - pos); + goto error; + } + + return key; + +error: + crypto_rsa_free(key); + return NULL; +} + + +/** + * crypto_rsa_get_modulus_len - Get the modulus length of the RSA key + * @key: RSA key + * Returns: Modulus length of the key + */ +size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key) +{ + return bignum_get_unsigned_bin_len(key->n); +} + + +/** + * crypto_rsa_exptmod - RSA modular exponentiation + * @in: Input data + * @inlen: Input data length + * @out: Buffer for output data + * @outlen: Maximum size of the output buffer and used size on success + * @key: RSA key + * @use_private: 1 = Use RSA private key, 0 = Use RSA public key + * Returns: 0 on success, -1 on failure + */ +int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen, + struct crypto_rsa_key *key, int use_private) +{ + struct bignum *tmp, *a = NULL, *b = NULL; + int ret = -1; + size_t modlen; + + if (use_private && !key->private_key) + return -1; + + tmp = bignum_init(); + if (tmp == NULL) + return -1; + + if (bignum_set_unsigned_bin(tmp, in, inlen) < 0) + goto error; + if (bignum_cmp(key->n, tmp) < 0) { + /* Too large input value for the RSA key modulus */ + goto error; + } + + if (use_private) { + /* + * Decrypt (or sign) using Chinese remainer theorem to speed + * up calculation. This is equivalent to tmp = tmp^d mod n + * (which would require more CPU to calculate directly). + * + * dmp1 = (1/e) mod (p-1) + * dmq1 = (1/e) mod (q-1) + * iqmp = (1/q) mod p, where p > q + * m1 = c^dmp1 mod p + * m2 = c^dmq1 mod q + * h = q^-1 (m1 - m2) mod p + * m = m2 + hq + */ + a = bignum_init(); + b = bignum_init(); + if (a == NULL || b == NULL) + goto error; + + /* a = tmp^dmp1 mod p */ + if (bignum_exptmod(tmp, key->dmp1, key->p, a) < 0) + goto error; + + /* b = tmp^dmq1 mod q */ + if (bignum_exptmod(tmp, key->dmq1, key->q, b) < 0) + goto error; + + /* tmp = (a - b) * (1/q mod p) (mod p) */ + if (bignum_sub(a, b, tmp) < 0 || + bignum_mulmod(tmp, key->iqmp, key->p, tmp) < 0) + goto error; + + /* tmp = b + q * tmp */ + if (bignum_mul(tmp, key->q, tmp) < 0 || + bignum_add(tmp, b, tmp) < 0) + goto error; + } else { + /* Encrypt (or verify signature) */ + /* tmp = tmp^e mod N */ + if (bignum_exptmod(tmp, key->e, key->n, tmp) < 0) + goto error; + } + + modlen = crypto_rsa_get_modulus_len(key); + if (modlen > *outlen) { + *outlen = modlen; + goto error; + } + + if (bignum_get_unsigned_bin_len(tmp) > modlen) + goto error; /* should never happen */ + + *outlen = modlen; + os_memset(out, 0, modlen); + if (bignum_get_unsigned_bin( + tmp, out + + (modlen - bignum_get_unsigned_bin_len(tmp)), NULL) < 0) + goto error; + + ret = 0; + +error: + + bignum_deinit(tmp); + bignum_deinit(a); + bignum_deinit(b); + return ret; +} + + +/** + * crypto_rsa_free - Free RSA key + * @key: RSA key to be freed + * + * This function frees an RSA key imported with either + * crypto_rsa_import_public_key() or crypto_rsa_import_private_key(). + */ +void crypto_rsa_free(struct crypto_rsa_key *key) +{ + if (key) { + bignum_deinit(key->n); + bignum_deinit(key->e); + bignum_deinit(key->d); + bignum_deinit(key->p); + bignum_deinit(key->q); + bignum_deinit(key->dmp1); + bignum_deinit(key->dmq1); + bignum_deinit(key->iqmp); + os_free(key); + } +} diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tls_internal.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tls_internal.c new file mode 100644 index 0000000000..79c8e56de1 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tls_internal.c @@ -0,0 +1,716 @@ +/* + * 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 + +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--; + } + + wpa_hmac_md5_vector(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], A_MD5); + wpa_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) { + wpa_hmac_md5_vector(S1, L_S1, 3, MD5_addr, MD5_len, P_MD5); + MD5_pos = 0; + wpa_hmac_md5(S1, L_S1, A_MD5, MD5_MAC_LEN, A_MD5); + } + if (SHA1_pos == SHA1_MAC_LEN) { + wpa_hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len, + P_SHA1); + SHA1_pos = 0; + wpa_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/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_client.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_client.c new file mode 100644 index 0000000000..5283af2c41 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_client.c @@ -0,0 +1,837 @@ +/* + * 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" + +/* TODO: + * Support for a message fragmented across several records (RFC 2246, 6.2.1) + */ + + +void tls_alert(struct tlsv1_client *conn, u8 level, u8 description) +{ + conn->alert_level = level; + conn->alert_description = description; +} + + +void tlsv1_client_free_dh(struct tlsv1_client *conn) +{ + os_free(conn->dh_p); + os_free(conn->dh_g); + os_free(conn->dh_ys); + conn->dh_p = conn->dh_g = conn->dh_ys = NULL; +} + + +int tls_derive_pre_master_secret(u8 *pre_master_secret) +{ + WPA_PUT_BE16(pre_master_secret, TLS_VERSION); + if (os_get_random(pre_master_secret + 2, + TLS_PRE_MASTER_SECRET_LEN - 2)) + return -1; + return 0; +} + + +int tls_derive_keys(struct tlsv1_client *conn, + const u8 *pre_master_secret, size_t pre_master_secret_len) +{ + u8 seed[2 * TLS_RANDOM_LEN]; + u8 key_block[TLS_MAX_KEY_BLOCK_LEN]; + u8 *pos; + size_t key_block_len; + + if (pre_master_secret) { + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret", + pre_master_secret, pre_master_secret_len); + os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random, + TLS_RANDOM_LEN); + if (tls_prf(conn->rl.tls_version, + pre_master_secret, pre_master_secret_len, + "master secret", seed, 2 * TLS_RANDOM_LEN, + conn->master_secret, TLS_MASTER_SECRET_LEN)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive " + "master_secret"); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret", + conn->master_secret, TLS_MASTER_SECRET_LEN); + } + + os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN); + key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len); + if (conn->rl.tls_version == TLS_VERSION_1) + key_block_len += 2 * conn->rl.iv_size; + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + "key expansion", seed, 2 * TLS_RANDOM_LEN, + key_block, key_block_len)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block"); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block", + key_block, key_block_len); + + pos = key_block; + + /* client_write_MAC_secret */ + os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size); + pos += conn->rl.hash_size; + /* server_write_MAC_secret */ + os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size); + pos += conn->rl.hash_size; + + /* client_write_key */ + os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len); + pos += conn->rl.key_material_len; + /* server_write_key */ + os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len); + pos += conn->rl.key_material_len; + + if (conn->rl.tls_version == TLS_VERSION_1) { + /* client_write_IV */ + os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size); + pos += conn->rl.iv_size; + /* server_write_IV */ + os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size); + pos += conn->rl.iv_size; + } else { + /* + * Use IV field to set the mask value for TLS v1.1. A fixed + * mask of zero is used per the RFC 4346, 6.2.3.2 CBC Block + * Cipher option 2a. + */ + os_memset(conn->rl.write_iv, 0, conn->rl.iv_size); + } + + return 0; +} + + +/** + * tlsv1_client_handshake - Process TLS handshake + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @in_data: Input data from TLS peer + * @in_len: Input data length + * @out_len: Length of the output buffer. + * @appl_data: Pointer to application data pointer, or %NULL if dropped + * @appl_data_len: Pointer to variable that is set to appl_data length + * @need_more_data: Set to 1 if more data would be needed to complete + * processing + * Returns: Pointer to output data, %NULL on failure + */ +u8 * tlsv1_client_handshake(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + size_t *out_len, u8 **appl_data, + size_t *appl_data_len, int *need_more_data) +{ + const u8 *pos, *end; + u8 *msg = NULL, *in_msg = NULL, *in_pos, *in_end, alert, ct; + size_t in_msg_len; + int no_appl_data; + int used; + + if (need_more_data) + *need_more_data = 0; + + if (conn->state == CLIENT_HELLO) { + if (in_len) + return NULL; + return tls_send_client_hello(conn, out_len); + } + + if (conn->partial_input) { + if (wpabuf_resize(&conn->partial_input, in_len) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " + "memory for pending record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + goto failed; + } + wpabuf_put_data(conn->partial_input, in_data, in_len); + in_data = wpabuf_head(conn->partial_input); + in_len = wpabuf_len(conn->partial_input); + } + + if (in_data == NULL || in_len == 0) + return NULL; + + pos = in_data; + end = in_data + in_len; + in_msg = os_malloc(in_len); + if (in_msg == NULL) + return NULL; + + /* Each received packet may include multiple records */ + while (pos < end) { + in_msg_len = in_len; + used = tlsv1_record_receive(&conn->rl, pos, end - pos, + in_msg, &in_msg_len, &alert); + if (used < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Processing received " + "record failed"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + goto failed; + } + if (used == 0) { + struct wpabuf *partial; + wpa_printf(MSG_DEBUG, "TLSv1: Need more data"); + partial = wpabuf_alloc_copy(pos, end - pos); + wpabuf_free(conn->partial_input); + conn->partial_input = partial; + if (conn->partial_input == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to " + "allocate memory for pending " + "record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + goto failed; + } + os_free(in_msg); + if (need_more_data) + *need_more_data = 1; + return NULL; + } + ct = pos[0]; + + in_pos = in_msg; + in_end = in_msg + in_msg_len; + + /* Each received record may include multiple messages of the + * same ContentType. */ + while (in_pos < in_end) { + in_msg_len = in_end - in_pos; + if (tlsv1_client_process_handshake(conn, ct, in_pos, + &in_msg_len, + appl_data, + appl_data_len) < 0) + goto failed; + in_pos += in_msg_len; + } + + pos += used; + } + + os_free(in_msg); + in_msg = NULL; + + no_appl_data = appl_data == NULL || *appl_data == NULL; + msg = tlsv1_client_handshake_write(conn, out_len, no_appl_data); + +failed: + os_free(in_msg); + if (conn->alert_level) { + wpabuf_free(conn->partial_input); + conn->partial_input = NULL; + conn->state = FAILED; + os_free(msg); + msg = tlsv1_client_send_alert(conn, conn->alert_level, + conn->alert_description, + out_len); + } else if (msg == NULL) { + msg = (u8 *)os_zalloc(1); + *out_len = 0; + } + + if (need_more_data == NULL || !(*need_more_data)) { + wpabuf_free(conn->partial_input); + conn->partial_input = NULL; + } + + return msg; +} + + +/** + * tlsv1_client_encrypt - Encrypt data into TLS tunnel + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @in_data: Pointer to plaintext data to be encrypted + * @in_len: Input buffer length + * @out_data: Pointer to output buffer (encrypted TLS data) + * @out_len: Maximum out_data length + * Returns: Number of bytes written to out_data, -1 on failure + * + * This function is used after TLS handshake has been completed successfully to + * send data in the encrypted tunnel. + */ +int tlsv1_client_encrypt(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len) +{ + size_t rlen; + + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData", + in_data, in_len); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA, + out_data, out_len, in_data, in_len, &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + return rlen; +} + + +/** + * tlsv1_client_decrypt - Decrypt data from TLS tunnel + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @in_data: Pointer to input buffer (encrypted TLS data) + * @in_len: Input buffer length + * @need_more_data: Set to 1 if more data would be needed to complete + * processing + * Returns: Decrypted data or %NULL on failure + * + * This function is used after TLS handshake has been completed successfully to + * receive data from the encrypted tunnel. + */ +struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + int *need_more_data) +{ + const u8 *in_end, *pos; + int used; + u8 alert, *out_pos, ct; + size_t olen; + struct wpabuf *buf = NULL; + + if (need_more_data) + *need_more_data = 0; + + if (conn->partial_input) { + if (wpabuf_resize(&conn->partial_input, in_len) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " + "memory for pending record"); + alert = TLS_ALERT_INTERNAL_ERROR; + goto fail; + } + wpabuf_put_data(conn->partial_input, in_data, in_len); + in_data = wpabuf_head(conn->partial_input); + in_len = wpabuf_len(conn->partial_input); + } + + pos = in_data; + in_end = in_data + in_len; + + while (pos < in_end) { + ct = pos[0]; + if (wpabuf_resize(&buf, in_end - pos) < 0) { + alert = TLS_ALERT_INTERNAL_ERROR; + goto fail; + } + out_pos = wpabuf_put(buf, 0); + olen = wpabuf_tailroom(buf); + used = tlsv1_record_receive(&conn->rl, pos, in_end - pos, + out_pos, &olen, &alert); + if (used < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing " + "failed"); + goto fail; + } + if (used == 0) { + struct wpabuf *partial; + wpa_printf(MSG_DEBUG, "TLSv1: Need more data"); + partial = wpabuf_alloc_copy(pos, in_end - pos); + wpabuf_free(conn->partial_input); + conn->partial_input = partial; + if (conn->partial_input == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to " + "allocate memory for pending " + "record"); + alert = TLS_ALERT_INTERNAL_ERROR; + goto fail; + } + if (need_more_data) + *need_more_data = 1; + return buf; + } + + if (ct == TLS_CONTENT_TYPE_ALERT) { + if (olen < 2) { + wpa_printf(MSG_DEBUG, "TLSv1: Alert " + "underflow"); + alert = TLS_ALERT_DECODE_ERROR; + goto fail; + } + wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", + out_pos[0], out_pos[1]); + if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) { + /* Continue processing */ + pos += used; + continue; + } + + alert = out_pos[1]; + goto fail; + } + + if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type " + "0x%x when decrypting application data", + pos[0]); + alert = TLS_ALERT_UNEXPECTED_MESSAGE; + goto fail; + } + + wpabuf_put(buf, olen); + + pos += used; + } + + wpabuf_free(conn->partial_input); + conn->partial_input = NULL; + return buf; + +fail: + wpabuf_free(buf); + wpabuf_free(conn->partial_input); + conn->partial_input = NULL; + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + return NULL; +} + + +/** + * tlsv1_client_global_init - Initialize TLSv1 client + * Returns: 0 on success, -1 on failure + * + * This function must be called before using any other TLSv1 client functions. + */ +int tlsv1_client_global_init(void) +{ + return crypto_global_init(); +} + + +/** + * tlsv1_client_global_deinit - Deinitialize TLSv1 client + * + * This function can be used to deinitialize the TLSv1 client that was + * initialized by calling tlsv1_client_global_init(). No TLSv1 client functions + * can be called after this before calling tlsv1_client_global_init() again. + */ +void tlsv1_client_global_deinit(void) +{ + crypto_global_deinit(); +} + + +/** + * tlsv1_client_init - Initialize TLSv1 client connection + * Returns: Pointer to TLSv1 client connection data or %NULL on failure + */ +struct tlsv1_client * tlsv1_client_init(void) +{ + struct tlsv1_client *conn; + size_t count; + u16 *suites; + + conn = (struct tlsv1_client *)os_zalloc(sizeof(*conn)); + if (conn == NULL) + return NULL; + + conn->state = CLIENT_HELLO; + + if (tls_verify_hash_init(&conn->verify) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify " + "hash"); + os_free(conn); + return NULL; + } + + count = 0; + suites = conn->cipher_suites; + suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256; + suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; + suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256; + suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; + suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; + suites[count++] = TLS_RSA_WITH_RC4_128_SHA; + suites[count++] = TLS_RSA_WITH_RC4_128_MD5; + conn->num_cipher_suites = count; + + conn->rl.tls_version = TLS_VERSION; + + return conn; +} + + +/** + * tlsv1_client_deinit - Deinitialize TLSv1 client connection + * @conn: TLSv1 client connection data from tlsv1_client_init() + */ +void tlsv1_client_deinit(struct tlsv1_client *conn) +{ + crypto_public_key_free(conn->server_rsa_key); + tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL); + tlsv1_record_change_write_cipher(&conn->rl); + tlsv1_record_change_read_cipher(&conn->rl); + tls_verify_hash_free(&conn->verify); + os_free(conn->client_hello_ext); + tlsv1_client_free_dh(conn); + tlsv1_cred_free(conn->cred); + wpabuf_free(conn->partial_input); + os_free(conn); +} + + +/** + * tlsv1_client_established - Check whether connection has been established + * @conn: TLSv1 client connection data from tlsv1_client_init() + * Returns: 1 if connection is established, 0 if not + */ +int tlsv1_client_established(struct tlsv1_client *conn) +{ + return conn->state == ESTABLISHED; +} + + +/** + * tlsv1_client_prf - Use TLS-PRF to derive keying material + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @label: Label (e.g., description of the key) for PRF + * @server_random_first: seed is 0 = client_random|server_random, + * 1 = server_random|client_random + * @out: Buffer for output data from TLS-PRF + * @out_len: Length of the output buffer + * Returns: 0 on success, -1 on failure + */ +int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, + int server_random_first, u8 *out, size_t out_len) +{ + u8 seed[2 * TLS_RANDOM_LEN]; + + if (conn->state != ESTABLISHED) + return -1; + + if (server_random_first) { + os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, + TLS_RANDOM_LEN); + } else { + os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random, + TLS_RANDOM_LEN); + } + + return tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + label, seed, 2 * TLS_RANDOM_LEN, out, out_len); +} + + +/** + * tlsv1_client_get_cipher - Get current cipher name + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @buf: Buffer for the cipher name + * @buflen: buf size + * Returns: 0 on success, -1 on failure + * + * Get the name of the currently used cipher. + */ +int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, + size_t buflen) +{ +#ifndef ESPRESSIF_USE + char *cipher; + + switch (conn->rl.cipher_suite) { + case TLS_RSA_WITH_RC4_128_MD5: + cipher = "RC4-MD5"; + break; + case TLS_RSA_WITH_RC4_128_SHA: + cipher = "RC4-SHA"; + break; + case TLS_RSA_WITH_DES_CBC_SHA: + cipher = "DES-CBC-SHA"; + break; + case TLS_RSA_WITH_3DES_EDE_CBC_SHA: + cipher = "DES-CBC3-SHA"; + break; + case TLS_DH_anon_WITH_AES_128_CBC_SHA256: + cipher = "ADH-AES-128-SHA256"; + break; + case TLS_DH_anon_WITH_AES_128_CBC_SHA: + cipher = "ADH-AES-128-SHA"; + break; + case TLS_RSA_WITH_AES_256_CBC_SHA: + cipher = "AES-256-SHA"; + break; + case TLS_RSA_WITH_AES_256_CBC_SHA256: + cipher = "AES-256-SHA256"; + break; + case TLS_RSA_WITH_AES_128_CBC_SHA: + cipher = "AES-128-SHA"; + break; + case TLS_RSA_WITH_AES_128_CBC_SHA256: + cipher = "AES-128-SHA256"; + break; + default: + return -1; + } + + os_memcpy((u8 *)buf, (u8 *)cipher, buflen); + + return 0; +#else + char cipher[20]; + + switch (conn->rl.cipher_suite) { + case TLS_RSA_WITH_RC4_128_MD5: + strcpy(cipher, "RC4-MD5"); + break; + case TLS_RSA_WITH_RC4_128_SHA: + strcpy(cipher, "RC4-SHA"); + break; + case TLS_RSA_WITH_DES_CBC_SHA: + strcpy(cipher, "DES-CBC-SHA"); + break; + case TLS_RSA_WITH_3DES_EDE_CBC_SHA: + strcpy(cipher, "DES-CBC3-SHA"); + break; + case TLS_DH_anon_WITH_AES_128_CBC_SHA256: + strcpy(cipher, "ADH-AES-128-SHA256"); + break; + case TLS_DH_anon_WITH_AES_128_CBC_SHA: + strcpy(cipher, "ADH-AES-128-SHA"); + break; + case TLS_RSA_WITH_AES_256_CBC_SHA: + strcpy(cipher, "AES-256-SHA"); + break; + case TLS_RSA_WITH_AES_256_CBC_SHA256: + strcpy(cipher, "AES-256-SHA256"); + break; + case TLS_RSA_WITH_AES_128_CBC_SHA: + strcpy(cipher, "AES-128-SHA"); + break; + case TLS_RSA_WITH_AES_128_CBC_SHA256: + strcpy(cipher, "AES-128-SHA256"); + break; + default: + return -1; + } + + os_memcpy((u8 *)buf, (u8 *)cipher, buflen); + + return 0; +#endif +} + + +/** + * tlsv1_client_shutdown - Shutdown TLS connection + * @conn: TLSv1 client connection data from tlsv1_client_init() + * Returns: 0 on success, -1 on failure + */ +int tlsv1_client_shutdown(struct tlsv1_client *conn) +{ + conn->state = CLIENT_HELLO; + + if (tls_verify_hash_init(&conn->verify) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify " + "hash"); + return -1; + } + + tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL); + tlsv1_record_change_write_cipher(&conn->rl); + tlsv1_record_change_read_cipher(&conn->rl); + + conn->certificate_requested = 0; + crypto_public_key_free(conn->server_rsa_key); + conn->server_rsa_key = NULL; + conn->session_resumed = 0; + + return 0; +} + + +/** + * tlsv1_client_resumed - Was session resumption used + * @conn: TLSv1 client connection data from tlsv1_client_init() + * Returns: 1 if current session used session resumption, 0 if not + */ +int tlsv1_client_resumed(struct tlsv1_client *conn) +{ + return !!conn->session_resumed; +} + + +/** + * tlsv1_client_hello_ext - Set TLS extension for ClientHello + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @ext_type: Extension type + * @data: Extension payload (%NULL to remove extension) + * @data_len: Extension payload length + * Returns: 0 on success, -1 on failure + */ +int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type, + const u8 *data, size_t data_len) +{ + u8 *pos; + + conn->session_ticket_included = 0; + os_free(conn->client_hello_ext); + conn->client_hello_ext = NULL; + conn->client_hello_ext_len = 0; + + if (data == NULL || data_len == 0) + return 0; + + pos = conn->client_hello_ext = os_malloc(6 + data_len); + if (pos == NULL) + return -1; + + WPA_PUT_BE16(pos, 4 + data_len); + pos += 2; + WPA_PUT_BE16(pos, ext_type); + pos += 2; + WPA_PUT_BE16(pos, data_len); + pos += 2; + os_memcpy(pos, data, data_len); + conn->client_hello_ext_len = 6 + data_len; + + if (ext_type == TLS_EXT_PAC_OPAQUE) { + conn->session_ticket_included = 1; + wpa_printf(MSG_DEBUG, "TLSv1: Using session ticket"); + } + + return 0; +} + + +/** + * tlsv1_client_get_keys - Get master key and random data from TLS connection + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @keys: Structure of key/random data (filled on success) + * Returns: 0 on success, -1 on failure + */ +int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys) +{ + os_memset(keys, 0, sizeof(*keys)); + if (conn->state == CLIENT_HELLO) + return -1; + + keys->client_random = conn->client_random; + keys->client_random_len = TLS_RANDOM_LEN; + + if (conn->state != SERVER_HELLO) { + keys->server_random = conn->server_random; + keys->server_random_len = TLS_RANDOM_LEN; + keys->master_key = conn->master_secret; + keys->master_key_len = TLS_MASTER_SECRET_LEN; + } + + return 0; +} + + +/** + * tlsv1_client_get_keyblock_size - Get TLS key_block size + * @conn: TLSv1 client connection data from tlsv1_client_init() + * Returns: Size of the key_block for the negotiated cipher suite or -1 on + * failure + */ +int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn) +{ + if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO) + return -1; + + return 2 * (conn->rl.hash_size + conn->rl.key_material_len + + conn->rl.iv_size); +} + + +/** + * tlsv1_client_set_cipher_list - Configure acceptable cipher suites + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers + * (TLS_CIPHER_*). + * Returns: 0 on success, -1 on failure + */ +int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers) +{ + size_t count; + u16 *suites; + + /* TODO: implement proper configuration of cipher suites */ + if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) { + count = 0; + suites = conn->cipher_suites; + suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA256; + suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA; + suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA256; + suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA; + suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA; + suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5; + suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA; + + /* + * Cisco AP (at least 350 and 1200 series) local authentication + * server does not know how to search cipher suites from the + * list and seem to require that the last entry in the list is + * the one that it wants to use. However, TLS specification + * requires the list to be in the client preference order. As a + * workaround, add anon-DH AES-128-SHA1 again at the end of the + * list to allow the Cisco code to find it. + */ + suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA; + conn->num_cipher_suites = count; + } + + return 0; +} + + +/** + * tlsv1_client_set_cred - Set client credentials + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @cred: Credentials from tlsv1_cred_alloc() + * Returns: 0 on success, -1 on failure + * + * On success, the client takes ownership of the credentials block and caller + * must not free it. On failure, caller is responsible for freeing the + * credential block. + */ +int tlsv1_client_set_cred(struct tlsv1_client *conn, + struct tlsv1_credentials *cred) +{ + tlsv1_cred_free(conn->cred); + conn->cred = cred; + return 0; +} + + +void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled) +{ + conn->disable_time_checks = !enabled; +} + + +void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn, + tlsv1_client_session_ticket_cb cb, + void *ctx) +{ + wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)", + cb, ctx); + conn->session_ticket_cb = cb; + conn->session_ticket_cb_ctx = ctx; +} diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_client_read.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_client_read.c new file mode 100644 index 0000000000..13330bdbf1 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_client_read.c @@ -0,0 +1,1001 @@ +/* + * 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" +#include "wpa2/eap_peer/eap_i.h" + +static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len); +static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len); +static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len); + + +static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len, i; + u16 cipher_suite; + u16 tls_version; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) + goto decode_error; + + /* HandshakeType msg_type */ + if (*pos != TLS_HANDSHAKE_TYPE_SERVER_HELLO) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected ServerHello)", *pos); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHello"); + pos++; + /* uint24 length */ + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) + goto decode_error; + + /* body - ServerHello */ + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerHello", pos, len); + end = pos + len; + + /* ProtocolVersion server_version */ + if (end - pos < 2) + goto decode_error; + tls_version = WPA_GET_BE16(pos); + if (!tls_version_ok(tls_version)) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " + "ServerHello %u.%u", pos[0], pos[1]); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_PROTOCOL_VERSION); + return -1; + } + pos += 2; + + wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s", + tls_version_str(tls_version)); + conn->rl.tls_version = tls_version; + + /* Random random */ + if (end - pos < TLS_RANDOM_LEN) + goto decode_error; + + os_memcpy(conn->server_random, pos, TLS_RANDOM_LEN); + pos += TLS_RANDOM_LEN; + wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random", + conn->server_random, TLS_RANDOM_LEN); + + /* SessionID session_id */ + if (end - pos < 1) + goto decode_error; + if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN) + goto decode_error; + if (conn->session_id_len && conn->session_id_len == *pos && + os_memcmp(conn->session_id, pos + 1, conn->session_id_len) == 0) { + pos += 1 + conn->session_id_len; + wpa_printf(MSG_DEBUG, "TLSv1: Resuming old session"); + conn->session_resumed = 1; + } else { + conn->session_id_len = *pos; + pos++; + os_memcpy(conn->session_id, pos, conn->session_id_len); + pos += conn->session_id_len; + } + wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id", + conn->session_id, conn->session_id_len); + + /* CipherSuite cipher_suite */ + if (end - pos < 2) + goto decode_error; + cipher_suite = WPA_GET_BE16(pos); + pos += 2; + for (i = 0; i < conn->num_cipher_suites; i++) { + if (cipher_suite == conn->cipher_suites[i]) + break; + } + if (i == conn->num_cipher_suites) { + wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected " + "cipher suite 0x%04x", cipher_suite); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_ILLEGAL_PARAMETER); + return -1; + } + + if (conn->session_resumed && cipher_suite != conn->prev_cipher_suite) { + wpa_printf(MSG_DEBUG, "TLSv1: Server selected a different " + "cipher suite for a resumed connection (0x%04x != " + "0x%04x)", cipher_suite, conn->prev_cipher_suite); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_ILLEGAL_PARAMETER); + return -1; + } + + if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for " + "record layer"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + conn->prev_cipher_suite = cipher_suite; + + /* CompressionMethod compression_method */ + if (end - pos < 1) + goto decode_error; + if (*pos != TLS_COMPRESSION_NULL) { + wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected " + "compression 0x%02x", *pos); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_ILLEGAL_PARAMETER); + return -1; + } + pos++; + + if (end != pos) { + /* TODO: ServerHello extensions */ + wpa_hexdump(MSG_DEBUG, "TLSv1: Unexpected extra data in the " + "end of ServerHello", pos, end - pos); + goto decode_error; + } + + if (conn->session_ticket_included && conn->session_ticket_cb) { + /* TODO: include SessionTicket extension if one was included in + * ServerHello */ + int res = conn->session_ticket_cb( + conn->session_ticket_cb_ctx, NULL, 0, + conn->client_random, conn->server_random, + conn->master_secret); + if (res < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback " + "indicated failure"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_HANDSHAKE_FAILURE); + return -1; + } + conn->use_session_ticket = !!res; + } + + if ((conn->session_resumed || conn->use_session_ticket) && + tls_derive_keys(conn, NULL, 0)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + *in_len = end - in_data; + + conn->state = (conn->session_resumed || conn->use_session_ticket) ? + SERVER_CHANGE_CIPHER_SPEC : SERVER_CERTIFICATE; + + return 0; + +decode_error: + wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ServerHello"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; +} + + +static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len, list_len, cert_len, idx; + u8 type; + struct x509_certificate *chain = NULL, *last = NULL, *cert; + int reason; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message " + "(len=%lu)", (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message " + "length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + if (type == TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) + return tls_process_server_key_exchange(conn, ct, in_data, + in_len); + if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) + return tls_process_certificate_request(conn, ct, in_data, + in_len); + if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) + return tls_process_server_hello_done(conn, ct, in_data, + in_len); + if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected Certificate/" + "ServerKeyExchange/CertificateRequest/" + "ServerHelloDone)", type); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received Certificate (certificate_list len %lu)", + (unsigned long) len); + + /* + * opaque ASN.1Cert<2^24-1>; + * + * struct { + * ASN.1Cert certificate_list<1..2^24-1>; + * } Certificate; + */ + + end = pos + len; + + if (end - pos < 3) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate " + "(left=%lu)", (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + list_len = WPA_GET_BE24(pos); + pos += 3; + + if ((size_t) (end - pos) != list_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list " + "length (len=%lu left=%lu)", + (unsigned long) list_len, + (unsigned long) (end - pos)); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + idx = 0; + while (pos < end) { + if (end - pos < 3) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " + "certificate_list"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + x509_certificate_chain_free(chain); + return -1; + } + + cert_len = WPA_GET_BE24(pos); + pos += 3; + + if ((size_t) (end - pos) < cert_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate " + "length (len=%lu left=%lu)", + (unsigned long) cert_len, + (unsigned long) (end - pos)); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + x509_certificate_chain_free(chain); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)", + (unsigned long) idx, (unsigned long) cert_len); + + if (idx == 0) { + crypto_public_key_free(conn->server_rsa_key); + if (tls_parse_cert(pos, cert_len, + &conn->server_rsa_key)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " + "the certificate"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + } + + cert = x509_certificate_parse(pos, cert_len); + if (cert == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " + "the certificate"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + + if (last == NULL) + chain = cert; + else + last->next = cert; + last = cert; + + idx++; + pos += cert_len; + } + + if (conn->cred && + x509_certificate_chain_validate(conn->cred->trusted_certs, chain, + &reason, conn->disable_time_checks) + < 0) { + int tls_reason; + wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " + "validation failed (reason=%d)", reason); + switch (reason) { + case X509_VALIDATE_BAD_CERTIFICATE: + tls_reason = TLS_ALERT_BAD_CERTIFICATE; + break; + case X509_VALIDATE_UNSUPPORTED_CERTIFICATE: + tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE; + break; + case X509_VALIDATE_CERTIFICATE_REVOKED: + tls_reason = TLS_ALERT_CERTIFICATE_REVOKED; + break; + case X509_VALIDATE_CERTIFICATE_EXPIRED: + tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED; + break; + case X509_VALIDATE_CERTIFICATE_UNKNOWN: + tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN; + break; + case X509_VALIDATE_UNKNOWN_CA: + tls_reason = TLS_ALERT_UNKNOWN_CA; + break; + default: + tls_reason = TLS_ALERT_BAD_CERTIFICATE; + break; + } + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason); + x509_certificate_chain_free(chain); + return -1; + } + + x509_certificate_chain_free(chain); + + *in_len = end - in_data; + conn->state = SERVER_KEY_EXCHANGE; + + return 0; +} + + +static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, + const u8 *buf, size_t len) +{ + const u8 *pos, *end; + + tlsv1_client_free_dh(conn); + + pos = buf; + end = buf + len; + + if (end - pos < 3) + goto fail; + conn->dh_p_len = WPA_GET_BE16(pos); + pos += 2; + if (conn->dh_p_len == 0 || end - pos < (int) conn->dh_p_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %lu", + (unsigned long) conn->dh_p_len); + goto fail; + } + conn->dh_p = os_malloc(conn->dh_p_len); + if (conn->dh_p == NULL) + goto fail; + os_memcpy(conn->dh_p, pos, conn->dh_p_len); + pos += conn->dh_p_len; + wpa_hexdump(MSG_DEBUG, "TLSv1: DH p (prime)", + conn->dh_p, conn->dh_p_len); + + if (end - pos < 3) + goto fail; + conn->dh_g_len = WPA_GET_BE16(pos); + pos += 2; + if (conn->dh_g_len == 0 || end - pos < (int) conn->dh_g_len) + goto fail; + conn->dh_g = os_malloc(conn->dh_g_len); + if (conn->dh_g == NULL) + goto fail; + os_memcpy(conn->dh_g, pos, conn->dh_g_len); + pos += conn->dh_g_len; + wpa_hexdump(MSG_DEBUG, "TLSv1: DH g (generator)", + conn->dh_g, conn->dh_g_len); + if (conn->dh_g_len == 1 && conn->dh_g[0] < 2) + goto fail; + + if (end - pos < 3) + goto fail; + conn->dh_ys_len = WPA_GET_BE16(pos); + pos += 2; + if (conn->dh_ys_len == 0 || end - pos < (int) conn->dh_ys_len) + goto fail; + conn->dh_ys = os_malloc(conn->dh_ys_len); + if (conn->dh_ys == NULL) + goto fail; + os_memcpy(conn->dh_ys, pos, conn->dh_ys_len); + pos += conn->dh_ys_len; + wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)", + conn->dh_ys, conn->dh_ys_len); + + return 0; + +fail: + wpa_printf(MSG_DEBUG, "TLSv1: Processing DH params failed"); + tlsv1_client_free_dh(conn); + return -1; +} + + +static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len; + u8 type; + const struct tls_cipher_suite *suite; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerKeyExchange " + "(Left=%lu)", (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerKeyExchange " + "length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + end = pos + len; + + if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) + return tls_process_certificate_request(conn, ct, in_data, + in_len); + if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) + return tls_process_server_hello_done(conn, ct, in_data, + in_len); + if (type != TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected ServerKeyExchange/" + "CertificateRequest/ServerHelloDone)", type); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received ServerKeyExchange"); + + if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) { + wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not allowed " + "with the selected cipher suite"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "TLSv1: ServerKeyExchange", pos, len); + suite = tls_get_cipher_suite(conn->rl.cipher_suite); + if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) { + if (tlsv1_process_diffie_hellman(conn, pos, len) < 0) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + } else { + wpa_printf(MSG_DEBUG, "TLSv1: UnexpectedServerKeyExchange"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + *in_len = end - in_data; + conn->state = SERVER_CERTIFICATE_REQUEST; + + return 0; +} + + +static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len; + u8 type; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateRequest " + "(left=%lu)", (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in CertificateRequest " + "length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + end = pos + len; + + if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) + return tls_process_server_hello_done(conn, ct, in_data, + in_len); + if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected CertificateRequest/" + "ServerHelloDone)", type); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateRequest"); + + conn->certificate_requested = 1; + + *in_len = end - in_data; + conn->state = SERVER_HELLO_DONE; + + return 0; +} + + +static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len; + u8 type; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerHelloDone " + "(left=%lu)", (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerHelloDone " + "length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + end = pos + len; + + if (type != TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected ServerHelloDone)", type); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHelloDone"); + + *in_len = end - in_data; + conn->state = CLIENT_KEY_EXCHANGE; + + return 0; +} + + +static int tls_process_server_change_cipher_spec(struct tlsv1_client *conn, + u8 ct, const u8 *in_data, + size_t *in_len) +{ + const u8 *pos; + size_t left; + + if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " + "received content type 0x%x", ct); + if (conn->use_session_ticket) { + int res; + wpa_printf(MSG_DEBUG, "TLSv1: Server may have " + "rejected SessionTicket"); + conn->use_session_ticket = 0; + + /* Notify upper layers that SessionTicket failed */ + res = conn->session_ticket_cb( + conn->session_ticket_cb_ctx, NULL, 0, NULL, + NULL, NULL); + if (res < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket " + "callback indicated failure"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_HANDSHAKE_FAILURE); + return -1; + } + printf("[Debug] set the state to server certificate \n"); + conn->state = SERVER_CERTIFICATE; + return tls_process_certificate(conn, ct, in_data, + in_len); + } + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 1) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + if (*pos != TLS_CHANGE_CIPHER_SPEC) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " + "received data 0x%x", *pos); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec"); + if (tlsv1_record_change_read_cipher(&conn->rl) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher " + "for record layer"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + *in_len = pos + 1 - in_data; + conn->state = SERVER_FINISHED; + + return 0; +} + + +static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len, hlen; + u8 verify_data[TLS_VERIFY_DATA_LEN]; + u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; " + "received content type 0x%x", ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for " + "Finished", + (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received " + "type 0x%x", pos[0]); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + len = WPA_GET_BE24(pos + 1); + + pos += 4; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished " + "(len=%lu > left=%lu)", + (unsigned long) len, (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + end = pos + len; + if (len != TLS_VERIFY_DATA_LEN) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length " + "in Finished: %lu (expected %d)", + (unsigned long) len, TLS_VERIFY_DATA_LEN); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished", + pos, TLS_VERIFY_DATA_LEN); + +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + hlen = SHA256_MAC_LEN; + if (wpa2_crypto_funcs.crypto_hash_finish) { + if (conn->verify.sha256_server == NULL || + wpa_crypto_funcs.crypto_hash_finish(conn->verify.sha256_server, hash, &hlen) < 0) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); + conn->verify.sha256_server = NULL; + return -1; + } + } else { + wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); + conn->verify.sha256_server = NULL; + return -1; + } + conn->verify.sha256_server = NULL; + } else { +#endif /* CONFIG_TLSV12 */ + + hlen = MD5_MAC_LEN; + if (conn->verify.md5_server == NULL || + crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.md5_server = NULL; + crypto_hash_finish(conn->verify.sha1_server, NULL, NULL); + conn->verify.sha1_server = NULL; + return -1; + } + conn->verify.md5_server = NULL; + hlen = SHA1_MAC_LEN; + if (conn->verify.sha1_server == NULL || + crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN, + &hlen) < 0) { + conn->verify.sha1_server = NULL; + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha1_server = NULL; + hlen = MD5_MAC_LEN + SHA1_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ + + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + "server finished", hash, hlen, + verify_data, TLS_VERIFY_DATA_LEN)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECRYPT_ERROR); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)", + verify_data, TLS_VERIFY_DATA_LEN); + + if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { + wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data"); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received Finished"); + + *in_len = end - in_data; + + conn->state = (conn->session_resumed || conn->use_session_ticket) ? + CHANGE_CIPHER_SPEC : ACK_FINISHED; + return 0; +} + + +static int tls_process_application_data(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len, + u8 **out_data, size_t *out_len) +{ + const u8 *pos; + size_t left; + + if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Application Data; " + "received content type 0x%x", ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + wpa_hexdump(MSG_DEBUG, "TLSv1: Application Data included in Handshake", + pos, left); + + *out_data = os_malloc(left); + if (*out_data) { + os_memcpy(*out_data, pos, left); + *out_len = left; + } + + return 0; +} + + +int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct, + const u8 *buf, size_t *len, + u8 **out_data, size_t *out_len) +{ + if (ct == TLS_CONTENT_TYPE_ALERT) { + if (*len < 2) { + wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", + buf[0], buf[1]); + *len = 2; + conn->state = FAILED; + return -1; + } + + if (ct == TLS_CONTENT_TYPE_HANDSHAKE && *len >= 4 && + buf[0] == TLS_HANDSHAKE_TYPE_HELLO_REQUEST) { + size_t hr_len = WPA_GET_BE24(buf + 1); + if (hr_len > *len - 4) { + wpa_printf(MSG_DEBUG, "TLSv1: HelloRequest underflow"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + wpa_printf(MSG_DEBUG, "TLSv1: Ignored HelloRequest"); + *len = 4 + hr_len; + return 0; + } + + switch (conn->state) { + case SERVER_HELLO: + if (tls_process_server_hello(conn, ct, buf, len)) + return -1; + break; + case SERVER_CERTIFICATE: + if (tls_process_certificate(conn, ct, buf, len)) + return -1; + break; + case SERVER_KEY_EXCHANGE: + if (tls_process_server_key_exchange(conn, ct, buf, len)) + return -1; + break; + case SERVER_CERTIFICATE_REQUEST: + if (tls_process_certificate_request(conn, ct, buf, len)) + return -1; + break; + case SERVER_HELLO_DONE: + if (tls_process_server_hello_done(conn, ct, buf, len)) + return -1; + break; + case SERVER_CHANGE_CIPHER_SPEC: + if (tls_process_server_change_cipher_spec(conn, ct, buf, len)) + return -1; + break; + case SERVER_FINISHED: + if (tls_process_server_finished(conn, ct, buf, len)) { + printf("[debug] server finish process fall \n"); + return -1; + } + break; + case ACK_FINISHED: + if (out_data && + tls_process_application_data(conn, ct, buf, len, out_data, + out_len)) + return -1; + break; + default: + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d " + "while processing received message", + conn->state); + return -1; + } + + if (ct == TLS_CONTENT_TYPE_HANDSHAKE) { + tls_verify_hash_add(&conn->verify, buf, *len); + } + + return 0; +} diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_client_write.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_client_write.c new file mode 100644 index 0000000000..55644a046c --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_client_write.c @@ -0,0 +1,901 @@ +/* + * 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" + +#include "wpa2/eap_peer/eap_i.h" + +static size_t tls_client_cert_chain_der_len(struct tlsv1_client *conn) +{ + size_t len = 0; + struct x509_certificate *cert; + + if (conn->cred == NULL) + return 0; + + cert = conn->cred->cert; + while (cert) { + len += 3 + cert->cert_len; + if (x509_certificate_self_signed(cert)) + break; + cert = x509_certificate_get_subject(conn->cred->trusted_certs, + &cert->issuer); + } + + return len; +} + + +u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) +{ + u8 *hello, *end, *pos, *hs_length, *hs_start, *rhdr; + struct os_time now; + size_t len, i; + + wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello"); + *out_len = 0; + + os_get_time(&now); + WPA_PUT_BE32(conn->client_random, now.sec); + if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) { + wpa_printf(MSG_ERROR, "TLSv1: Could not generate " + "client_random"); + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random", + conn->client_random, TLS_RANDOM_LEN); + + len = 100 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len; + hello = os_malloc(len); + if (hello == NULL) + return NULL; + end = hello + len; + + rhdr = hello; + pos = rhdr + TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CLIENT_HELLO; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + /* body - ClientHello */ + /* ProtocolVersion client_version */ + WPA_PUT_BE16(pos, TLS_VERSION); + pos += 2; + /* Random random: uint32 gmt_unix_time, opaque random_bytes */ + os_memcpy(pos, conn->client_random, TLS_RANDOM_LEN); + pos += TLS_RANDOM_LEN; + /* SessionID session_id */ + *pos++ = conn->session_id_len; + os_memcpy(pos, conn->session_id, conn->session_id_len); + pos += conn->session_id_len; + /* CipherSuite cipher_suites<2..2^16-1> */ + WPA_PUT_BE16(pos, 2 * conn->num_cipher_suites); + pos += 2; + for (i = 0; i < conn->num_cipher_suites; i++) { + WPA_PUT_BE16(pos, conn->cipher_suites[i]); + pos += 2; + } + /* CompressionMethod compression_methods<1..2^8-1> */ + *pos++ = 1; + *pos++ = TLS_COMPRESSION_NULL; + + if (conn->client_hello_ext) { + os_memcpy(pos, conn->client_hello_ext, + conn->client_hello_ext_len); + pos += conn->client_hello_ext_len; + } + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, hs_start, pos - hs_start, + out_len) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(hello); + return NULL; + } + + conn->state = SERVER_HELLO; + + return hello; +} + + +static int tls_write_client_certificate(struct tlsv1_client *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start; + size_t rlen; + struct x509_certificate *cert; + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate"); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + /* body - Certificate */ + /* uint24 length (to be filled) */ + cert_start = pos; + pos += 3; + cert = conn->cred ? conn->cred->cert : NULL; + while (cert) { + if (pos + 3 + cert->cert_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space " + "for Certificate (cert_len=%lu left=%lu)", + (unsigned long) cert->cert_len, + (unsigned long) (end - pos)); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + WPA_PUT_BE24(pos, cert->cert_len); + pos += 3; + os_memcpy(pos, cert->cert_start, cert->cert_len); + pos += cert->cert_len; + + if (x509_certificate_self_signed(cert)) + break; + cert = x509_certificate_get_subject(conn->cred->trusted_certs, + &cert->issuer); + } + if (conn->cred == NULL || cert == conn->cred->cert || cert == NULL) { + /* + * Client was not configured with all the needed certificates + * to form a full certificate chain. The server may fail to + * validate the chain unless it is configured with all the + * missing CA certificates. + */ + wpa_printf(MSG_DEBUG, "TLSv1: Full client certificate chain " + "not configured - validation may fail"); + } + WPA_PUT_BE24(cert_start, pos - cert_start - 3); + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos = rhdr + rlen; + + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + +static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) +{ + /* ClientDiffieHellmanPublic */ + u8 *csecret, *csecret_start, *dh_yc, *shared; + size_t csecret_len, dh_yc_len, shared_len; + + csecret_len = conn->dh_p_len; + csecret = os_malloc(csecret_len); + if (csecret == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " + "memory for Yc (Diffie-Hellman)"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + if (random_get_bytes(csecret, csecret_len)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random " + "data for Diffie-Hellman"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + return -1; + } + + if (os_memcmp(csecret, conn->dh_p, csecret_len) > 0) + csecret[0] = 0; /* make sure Yc < p */ + + csecret_start = csecret; + while (csecret_len > 1 && *csecret_start == 0) { + csecret_start++; + csecret_len--; + } + wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH client's secret value", + csecret_start, csecret_len); + + /* Yc = g^csecret mod p */ + dh_yc_len = conn->dh_p_len; + dh_yc = os_malloc(dh_yc_len); + if (dh_yc == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " + "memory for Diffie-Hellman"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + return -1; + } + if (wpa2_crypto_funcs.crypto_mod_exp) { + if(wpa2_crypto_funcs.crypto_mod_exp(conn->dh_g, conn->dh_g_len, + csecret_start, csecret_len, + conn->dh_p, conn->dh_p_len, + dh_yc, &dh_yc_len)) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + os_free(dh_yc); + return -1; + } + } else { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + os_free(dh_yc); + wpa_printf(MSG_ERROR, "Fail to register crypto_mod_exp function!\r\n"); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)", + dh_yc, dh_yc_len); + + WPA_PUT_BE16(*pos, dh_yc_len); + *pos += 2; + if (*pos + dh_yc_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Not enough room in the " + "message buffer for Yc"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + os_free(dh_yc); + return -1; + } + os_memcpy(*pos, dh_yc, dh_yc_len); + *pos += dh_yc_len; + os_free(dh_yc); + + shared_len = conn->dh_p_len; + shared = os_malloc(shared_len); + if (shared == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for " + "DH"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + return -1; + } + + /* shared = Ys^csecret mod p */ + if (wpa2_crypto_funcs.crypto_mod_exp) { + if(wpa2_crypto_funcs.crypto_mod_exp(conn->dh_ys, conn->dh_ys_len, + csecret_start, csecret_len, + conn->dh_p, conn->dh_p_len, + shared, &shared_len)) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + os_free(shared); + return -1; + } + } else { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + os_free(shared); + wpa_printf(MSG_ERROR, "Fail to register crypto_mod_exp function!\r\n"); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange", + shared, shared_len); + + os_memset(csecret_start, 0, csecret_len); + os_free(csecret); + if (tls_derive_keys(conn, shared, shared_len)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(shared); + return -1; + } + os_memset(shared, 0, shared_len); + os_free(shared); + tlsv1_client_free_dh(conn); + return 0; +} + + +static int tlsv1_key_x_rsa(struct tlsv1_client *conn, u8 **pos, u8 *end) +{ + u8 pre_master_secret[TLS_PRE_MASTER_SECRET_LEN]; + size_t clen; + int res; + + if (tls_derive_pre_master_secret(pre_master_secret) < 0 || + tls_derive_keys(conn, pre_master_secret, + TLS_PRE_MASTER_SECRET_LEN)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + /* EncryptedPreMasterSecret */ + if (conn->server_rsa_key == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: No server RSA key to " + "use for encrypting pre-master secret"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + /* RSA encrypted value is encoded with PKCS #1 v1.5 block type 2. */ + *pos += 2; + clen = end - *pos; + res = crypto_public_key_encrypt_pkcs1_v15( + conn->server_rsa_key, + pre_master_secret, TLS_PRE_MASTER_SECRET_LEN, + *pos, &clen); + os_memset(pre_master_secret, 0, TLS_PRE_MASTER_SECRET_LEN); + if (res < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: RSA encryption failed"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + WPA_PUT_BE16(*pos - 2, clen); + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Encrypted pre_master_secret", + *pos, clen); + *pos += clen; + + return 0; +} + + +static int tls_write_client_key_exchange(struct tlsv1_client *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr, *hs_start, *hs_length; + size_t rlen; + tls_key_exchange keyx; + const struct tls_cipher_suite *suite; + + suite = tls_get_cipher_suite(conn->rl.cipher_suite); + if (suite == NULL) + keyx = TLS_KEY_X_NULL; + else + keyx = suite->key_exchange; + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send ClientKeyExchange"); + + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + /* body - ClientKeyExchange */ + if (keyx == TLS_KEY_X_DH_anon) { + if (tlsv1_key_x_anon_dh(conn, &pos, end) < 0) + return -1; + } else { + if (tlsv1_key_x_rsa(conn, &pos, end) < 0) + return -1; + } + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos = rhdr + rlen; + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + +static int tls_write_client_certificate_verify(struct tlsv1_client *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start; + size_t rlen, hlen, clen; + u8 hash[100], *hpos; + enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA; + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateVerify"); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + + /* + * RFC 2246: 7.4.3 and 7.4.8: + * Signature signature + * + * RSA: + * digitally-signed struct { + * opaque md5_hash[16]; + * opaque sha_hash[20]; + * }; + * + * DSA: + * digitally-signed struct { + * opaque sha_hash[20]; + * }; + * + * The hash values are calculated over all handshake messages sent or + * received starting at ClientHello up to, but not including, this + * CertificateVerify message, including the type and length fields of + * the handshake messages. + */ + + hpos = hash; + +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version == TLS_VERSION_1_2) { + hlen = SHA256_MAC_LEN; + if (wpa2_crypto_funcs.crypto_hash_finish) { + if (conn->verify.sha256_cert == NULL || + wpa2_crypto_funcs.crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) < + 0) { + conn->verify.sha256_cert = NULL; + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + } else { + conn->verify.sha256_cert = NULL; + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__); + return -1; + } + conn->verify.sha256_cert = NULL; + + /* + * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5 + * + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithm, + * digest OCTET STRING + * } + * + * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11} + * + * DER encoded DigestInfo for SHA256 per RFC 3447: + * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || + * H + */ + os_memmove(hash + 19, hash, hlen); + hlen += 19; + os_memcpy(hash, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65" + "\x03\x04\x02\x01\x05\x00\x04\x20", 19); + } else { +#endif /* CONFIG_TLSV12 */ + + if (alg == SIGN_ALG_RSA) { + hlen = MD5_MAC_LEN; + if (conn->verify.md5_cert == NULL || + crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) + { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.md5_cert = NULL; + crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL); + conn->verify.sha1_cert = NULL; + return -1; + } + hpos += MD5_MAC_LEN; + } else + crypto_hash_finish(conn->verify.md5_cert, NULL, NULL); + + conn->verify.md5_cert = NULL; + hlen = SHA1_MAC_LEN; + if (conn->verify.sha1_cert == NULL || + crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) { + conn->verify.sha1_cert = NULL; + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha1_cert = NULL; + + if (alg == SIGN_ALG_RSA) + hlen += MD5_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen); + +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + /* + * RFC 5246, 4.7: + * TLS v1.2 adds explicit indication of the used signature and + * hash algorithms. + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + */ + *pos++ = TLS_HASH_ALG_SHA256; + *pos++ = TLS_SIGN_ALG_RSA; + } +#endif /* CONFIG_TLSV12 */ + + /* + * RFC 2246, 4.7: + * In digital signing, one-way hash functions are used as input for a + * signing algorithm. A digitally-signed element is encoded as an + * opaque vector <0..2^16-1>, where the length is specified by the + * signing algorithm and key. + * + * In RSA signing, a 36-byte structure of two hashes (one SHA and one + * MD5) is signed (encrypted with the private key). It is encoded with + * PKCS #1 block type 0 or type 1 as described in [PKCS1]. + */ + signed_start = pos; /* length to be filled */ + pos += 2; + clen = end - pos; + if (conn->cred == NULL || + crypto_private_key_sign_pkcs1(conn->cred->key, hash, hlen, + pos, &clen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to sign hash (PKCS #1)"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + WPA_PUT_BE16(signed_start, clen); + + pos += clen; + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos = rhdr + rlen; + + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + +static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn, + u8 **msgpos, u8 *end) +{ + size_t rlen; + u8 payload[1]; + + wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec"); + + payload[0] = TLS_CHANGE_CIPHER_SPEC; + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC, + *msgpos, end - *msgpos, payload, sizeof(payload), + &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + if (tlsv1_record_change_write_cipher(&conn->rl) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for " + "record layer"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + *msgpos += rlen; + + return 0; +} + + +static int tls_write_client_finished(struct tlsv1_client *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *hs_start; + size_t rlen, hlen; + u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN]; + u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; + + wpa_printf(MSG_DEBUG, "TLSv1: Send Finished"); + + /* Encrypted Handshake Message: Finished */ + +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + hlen = SHA256_MAC_LEN; + if (wpa2_crypto_funcs.crypto_hash_finish) { + if (conn->verify.sha256_client == NULL || + wpa2_crypto_funcs.crypto_hash_finish(conn->verify.sha256_client, hash, &hlen) + < 0) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.sha256_client = NULL; + return -1; + } + } else { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.sha256_client = NULL; + wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__); + return -1; + } + conn->verify.sha256_client = NULL; + } else { +#endif /* CONFIG_TLSV12 */ + + hlen = MD5_MAC_LEN; + if (conn->verify.md5_client == NULL || + crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.md5_client = NULL; + crypto_hash_finish(conn->verify.sha1_client, NULL, NULL); + conn->verify.sha1_client = NULL; + return -1; + } + conn->verify.md5_client = NULL; + hlen = SHA1_MAC_LEN; + if (conn->verify.sha1_client == NULL || + crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN, + &hlen) < 0) { + conn->verify.sha1_client = NULL; + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha1_client = NULL; + hlen = MD5_MAC_LEN + SHA1_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ + + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + "client finished", hash, hlen, + verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)", + verify_data + 1 + 3, TLS_VERIFY_DATA_LEN); + + /* Handshake */ + pos = hs_start = verify_data; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_FINISHED; + /* uint24 length */ + WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN); + pos += 3; + pos += TLS_VERIFY_DATA_LEN; /* verify_data already in place */ + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + *msgpos, end - *msgpos, hs_start, pos - hs_start, + &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + *msgpos += rlen; + + return 0; +} + + +static u8 * tls_send_client_key_exchange(struct tlsv1_client *conn, + size_t *out_len) +{ + u8 *msg, *end, *pos; + size_t msglen; + + *out_len = 0; + + msglen = 2000; + if (conn->certificate_requested) + msglen += tls_client_cert_chain_der_len(conn); + + msg = os_malloc(msglen); + if (msg == NULL) + return NULL; + + pos = msg; + end = msg + msglen; + + if (conn->certificate_requested) { + if (tls_write_client_certificate(conn, &pos, end) < 0) { + os_free(msg); + return NULL; + } + } + + if (tls_write_client_key_exchange(conn, &pos, end) < 0 || + (conn->certificate_requested && conn->cred && conn->cred->key && + tls_write_client_certificate_verify(conn, &pos, end) < 0) || + tls_write_client_change_cipher_spec(conn, &pos, end) < 0 || + tls_write_client_finished(conn, &pos, end) < 0) { + os_free(msg); + return NULL; + } + + *out_len = pos - msg; + conn->state = SERVER_CHANGE_CIPHER_SPEC; + + return msg; +} + + +static u8 * tls_send_change_cipher_spec(struct tlsv1_client *conn, + size_t *out_len) +{ + u8 *msg, *end, *pos; + + *out_len = 0; + + msg = os_malloc(1000); + if (msg == NULL) + return NULL; + + pos = msg; + end = msg + 1000; + + if (tls_write_client_change_cipher_spec(conn, &pos, end) < 0 || + tls_write_client_finished(conn, &pos, end) < 0) { + os_free(msg); + return NULL; + } + + *out_len = pos - msg; + + wpa_printf(MSG_DEBUG, "TLSv1: Session resumption completed " + "successfully"); + + conn->state = ESTABLISHED; + + return msg; +} + + +u8 * tlsv1_client_handshake_write(struct tlsv1_client *conn, size_t *out_len, + int no_appl_data) +{ + switch (conn->state) { + case CLIENT_KEY_EXCHANGE: + return tls_send_client_key_exchange(conn, out_len); + case CHANGE_CIPHER_SPEC: + return tls_send_change_cipher_spec(conn, out_len); + case ACK_FINISHED: + wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed " + "successfully"); + conn->state = ESTABLISHED; + *out_len = 0; + if (no_appl_data) { + /* Need to return something to get final TLS ACK. */ + return os_malloc(1); + } + return NULL; + default: + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while " + "generating reply", conn->state); + return NULL; + } +} + + +u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level, + u8 description, size_t *out_len) +{ + u8 *alert, *pos, *length; + + wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description); + *out_len = 0; + + alert = os_malloc(10); + if (alert == NULL) + return NULL; + + pos = alert; + + /* TLSPlaintext */ + /* ContentType type */ + *pos++ = TLS_CONTENT_TYPE_ALERT; + /* ProtocolVersion version */ + WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version : + TLS_VERSION); + pos += 2; + /* uint16 length (to be filled) */ + length = pos; + pos += 2; + /* opaque fragment[TLSPlaintext.length] */ + + /* Alert */ + /* AlertLevel level */ + *pos++ = level; + /* AlertDescription description */ + *pos++ = description; + + WPA_PUT_BE16(length, pos - length - 2); + *out_len = pos - alert; + + return alert; +} diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_common.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_common.c new file mode 100644 index 0000000000..9d17928dbc --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_common.c @@ -0,0 +1,337 @@ +/* + * 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" +#include "wpa2/eap_peer/eap_i.h" + + +/* + * TODO: + * RFC 2246 Section 9: Mandatory to implement TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA + * Add support for commonly used cipher suites; don't bother with exportable + * suites. + */ + +static const struct tls_cipher_suite tls_cipher_suites[] = { + { TLS_NULL_WITH_NULL_NULL, TLS_KEY_X_NULL, TLS_CIPHER_NULL, + TLS_HASH_NULL }, + { TLS_RSA_WITH_RC4_128_MD5, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128, + TLS_HASH_MD5 }, + { TLS_RSA_WITH_RC4_128_SHA, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128, + TLS_HASH_SHA }, + { TLS_RSA_WITH_DES_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_DES_CBC, + TLS_HASH_SHA }, + { TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_RSA, + TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA }, + { TLS_DH_anon_WITH_RC4_128_MD5, TLS_KEY_X_DH_anon, + TLS_CIPHER_RC4_128, TLS_HASH_MD5 }, + { TLS_DH_anon_WITH_DES_CBC_SHA, TLS_KEY_X_DH_anon, + TLS_CIPHER_DES_CBC, TLS_HASH_SHA }, + { TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_DH_anon, + TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA }, + { TLS_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_128_CBC, + TLS_HASH_SHA }, + { TLS_DH_anon_WITH_AES_128_CBC_SHA, TLS_KEY_X_DH_anon, + TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA }, + { TLS_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC, + TLS_HASH_SHA }, + { TLS_DH_anon_WITH_AES_256_CBC_SHA, TLS_KEY_X_DH_anon, + TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA }, + { TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_RSA, + TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 }, + { TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_RSA, + TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 }, + { TLS_DH_anon_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DH_anon, + TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 }, + { TLS_DH_anon_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DH_anon, + TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 } +}; + +#define NUM_ELEMS(a) (sizeof(a) / sizeof((a)[0])) +#define NUM_TLS_CIPHER_SUITES NUM_ELEMS(tls_cipher_suites) + + +static const struct tls_cipher_data tls_ciphers[] = { + { TLS_CIPHER_NULL, TLS_CIPHER_STREAM, 0, 0, 0, + CRYPTO_CIPHER_NULL }, + { TLS_CIPHER_IDEA_CBC, TLS_CIPHER_BLOCK, 16, 16, 8, + CRYPTO_CIPHER_NULL }, + { TLS_CIPHER_RC2_CBC_40, TLS_CIPHER_BLOCK, 5, 16, 0, + CRYPTO_CIPHER_ALG_RC2 }, + { TLS_CIPHER_RC4_40, TLS_CIPHER_STREAM, 5, 16, 0, + CRYPTO_CIPHER_ALG_RC4 }, + { TLS_CIPHER_RC4_128, TLS_CIPHER_STREAM, 16, 16, 0, + CRYPTO_CIPHER_ALG_RC4 }, + { TLS_CIPHER_DES40_CBC, TLS_CIPHER_BLOCK, 5, 8, 8, + CRYPTO_CIPHER_ALG_DES }, + { TLS_CIPHER_DES_CBC, TLS_CIPHER_BLOCK, 8, 8, 8, + CRYPTO_CIPHER_ALG_DES }, + { TLS_CIPHER_3DES_EDE_CBC, TLS_CIPHER_BLOCK, 24, 24, 8, + CRYPTO_CIPHER_ALG_3DES }, + { TLS_CIPHER_AES_128_CBC, TLS_CIPHER_BLOCK, 16, 16, 16, + CRYPTO_CIPHER_ALG_AES }, + { TLS_CIPHER_AES_256_CBC, TLS_CIPHER_BLOCK, 32, 32, 16, + CRYPTO_CIPHER_ALG_AES } +}; + +#define NUM_TLS_CIPHER_DATA NUM_ELEMS(tls_ciphers) + + +/** + * tls_get_cipher_suite - Get TLS cipher suite + * @suite: Cipher suite identifier + * Returns: Pointer to the cipher data or %NULL if not found + */ +const struct tls_cipher_suite * tls_get_cipher_suite(u16 suite) +{ + size_t i; + for (i = 0; i < NUM_TLS_CIPHER_SUITES; i++) + if (tls_cipher_suites[i].suite == suite) + return &tls_cipher_suites[i]; + return NULL; +} + + +const struct tls_cipher_data * tls_get_cipher_data(tls_cipher cipher) +{ + size_t i; + for (i = 0; i < NUM_TLS_CIPHER_DATA; i++) + if (tls_ciphers[i].cipher == cipher) + return &tls_ciphers[i]; + return NULL; +} + + +int tls_server_key_exchange_allowed(tls_cipher cipher) +{ + const struct tls_cipher_suite *suite; + + /* RFC 2246, Section 7.4.3 */ + suite = tls_get_cipher_suite(cipher); + if (suite == NULL) + return 0; + + switch (suite->key_exchange) { + case TLS_KEY_X_DHE_DSS: + case TLS_KEY_X_DHE_DSS_EXPORT: + case TLS_KEY_X_DHE_RSA: + case TLS_KEY_X_DHE_RSA_EXPORT: + case TLS_KEY_X_DH_anon_EXPORT: + case TLS_KEY_X_DH_anon: + return 1; + case TLS_KEY_X_RSA_EXPORT: + return 1 /* FIX: public key len > 512 bits */; + default: + return 0; + } +} + + +/** + * tls_parse_cert - Parse DER encoded X.509 certificate and get public key + * @buf: ASN.1 DER encoded certificate + * @len: Length of the buffer + * @pk: Buffer for returning the allocated public key + * Returns: 0 on success, -1 on failure + * + * This functions parses an ASN.1 DER encoded X.509 certificate and retrieves + * the public key from it. The caller is responsible for freeing the public key + * by calling crypto_public_key_free(). + */ +int tls_parse_cert(const u8 *buf, size_t len, struct crypto_public_key **pk) +{ + struct x509_certificate *cert; + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Parse ASN.1 DER certificate", + buf, len); + + *pk = crypto_public_key_from_cert(buf, len); + if (*pk) + return 0; + + cert = x509_certificate_parse(buf, len); + if (cert == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse X.509 " + "certificate"); + return -1; + } + + /* TODO + * verify key usage (must allow encryption) + * + * All certificate profiles, key and cryptographic formats are + * defined by the IETF PKIX working group [PKIX]. When a key + * usage extension is present, the digitalSignature bit must be + * set for the key to be eligible for signing, as described + * above, and the keyEncipherment bit must be present to allow + * encryption, as described above. The keyAgreement bit must be + * set on Diffie-Hellman certificates. (PKIX: RFC 3280) + */ + + *pk = crypto_public_key_import(cert->public_key, cert->public_key_len); + x509_certificate_free(cert); + + if (*pk == NULL) { + wpa_printf(MSG_ERROR, "TLSv1: Failed to import " + "server public key"); + return -1; + } + + return 0; +} + + +int tls_verify_hash_init(struct tls_verify_hash *verify) +{ + tls_verify_hash_free(verify); + verify->md5_client = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0); + verify->md5_server = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0); + verify->md5_cert = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0); + verify->sha1_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0); + verify->sha1_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0); + verify->sha1_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0); + if (verify->md5_client == NULL || verify->md5_server == NULL || + verify->md5_cert == NULL || verify->sha1_client == NULL || + verify->sha1_server == NULL || verify->sha1_cert == NULL) { + tls_verify_hash_free(verify); + return -1; + } +#ifdef CONFIG_TLSV12 + if (wpa2_crypto_funcs.crypto_hash_init) { + verify->sha256_client = wpa2_crypto_funcs.crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0); + verify->sha256_server = wpa2_crypto_funcs.crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0); + verify->sha256_cert = wpa2_crypto_funcs.crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0); + } else { + wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash init function!\r\n", __FUNCTION__); + return -1; + } + if (verify->sha256_client == NULL || + verify->sha256_server == NULL || + verify->sha256_cert == NULL) { + tls_verify_hash_free(verify); + return -1; + } +#endif /* CONFIG_TLSV12 */ + return 0; +} + + +void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf, + size_t len) +{ + if (verify->md5_client && verify->sha1_client) { + crypto_hash_update(verify->md5_client, buf, len); + crypto_hash_update(verify->sha1_client, buf, len); + } + if (verify->md5_server && verify->sha1_server) { + crypto_hash_update(verify->md5_server, buf, len); + crypto_hash_update(verify->sha1_server, buf, len); + } + if (verify->md5_cert && verify->sha1_cert) { + crypto_hash_update(verify->md5_cert, buf, len); + crypto_hash_update(verify->sha1_cert, buf, len); + } +#ifdef CONFIG_TLSV12 + if (wpa2_crypto_funcs.crypto_hash_update) { + if (verify->sha256_client) + wpa2_crypto_funcs.crypto_hash_update(verify->sha256_client, buf, len); + if (verify->sha256_server) + wpa2_crypto_funcs.crypto_hash_update(verify->sha256_server, buf, len); + if (verify->sha256_cert) + wpa2_crypto_funcs.crypto_hash_update(verify->sha256_cert, buf, len); + } else { + wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash update function!\r\n", __FUNCTION__); + return; + } +#endif /* CONFIG_TLSV12 */ +} + + +void tls_verify_hash_free(struct tls_verify_hash *verify) +{ + crypto_hash_finish(verify->md5_client, NULL, NULL); + crypto_hash_finish(verify->md5_server, NULL, NULL); + crypto_hash_finish(verify->md5_cert, NULL, NULL); + crypto_hash_finish(verify->sha1_client, NULL, NULL); + crypto_hash_finish(verify->sha1_server, NULL, NULL); + crypto_hash_finish(verify->sha1_cert, NULL, NULL); + verify->md5_client = NULL; + verify->md5_server = NULL; + verify->md5_cert = NULL; + verify->sha1_client = NULL; + verify->sha1_server = NULL; + verify->sha1_cert = NULL; +#ifdef CONFIG_TLSV12 + if (wpa2_crypto_funcs.crypto_hash_finish) { + wpa2_crypto_funcs.crypto_hash_finish(verify->sha256_client, NULL, NULL); + wpa2_crypto_funcs.crypto_hash_finish(verify->sha256_server, NULL, NULL); + wpa2_crypto_funcs.crypto_hash_finish(verify->sha256_cert, NULL, NULL); + verify->sha256_client = NULL; + verify->sha256_server = NULL; + verify->sha256_cert = NULL; + } else { + wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash finish function!\r\n", __FUNCTION__); + return; + } +#endif /* CONFIG_TLSV12 */ +} + + +int tls_version_ok(u16 ver) +{ + if (ver == TLS_VERSION_1) + return 1; +#ifdef CONFIG_TLSV11 + if (ver == TLS_VERSION_1_1) + return 1; +#endif /* CONFIG_TLSV11 */ +#ifdef CONFIG_TLSV12 + if (ver == TLS_VERSION_1_2) + return 1; +#endif /* CONFIG_TLSV12 */ + + return 0; +} + + +const char * tls_version_str(u16 ver) +{ + switch (ver) { + case TLS_VERSION_1: + return "1.0"; + case TLS_VERSION_1_1: + return "1.1"; + case TLS_VERSION_1_2: + return "1.2"; + } + + return "?"; +} + + +int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label, + const u8 *seed, size_t seed_len, u8 *out, size_t outlen) +{ +#ifdef CONFIG_TLSV12 + if (ver >= TLS_VERSION_1_2) { + tls_prf_sha256(secret, secret_len, label, seed, seed_len, + out, outlen); + return 0; + } +#endif /* CONFIG_TLSV12 */ + + return tls_prf_sha1_md5(secret, secret_len, label, seed, seed_len, out, + outlen); +} diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_cred.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_cred.c new file mode 100644 index 0000000000..fd359a2cc5 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_cred.c @@ -0,0 +1,505 @@ +/* + * 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" + +struct tlsv1_credentials * tlsv1_cred_alloc(void) +{ + struct tlsv1_credentials *cred; + cred = (struct tlsv1_credentials *)os_zalloc(sizeof(*cred)); + return cred; +} + + +void tlsv1_cred_free(struct tlsv1_credentials *cred) +{ + if (cred == NULL) + return; + + x509_certificate_chain_free(cred->trusted_certs); + x509_certificate_chain_free(cred->cert); + crypto_private_key_free(cred->key); + os_free(cred->dh_p); + os_free(cred->dh_g); + os_free(cred); +} + + +static int tlsv1_add_cert_der(struct x509_certificate **chain, + const u8 *buf, size_t len) +{ + struct x509_certificate *cert, *p; + char name[128]; + + cert = x509_certificate_parse(buf, len); + if (cert == NULL) { + wpa_printf(MSG_INFO, "TLSv1: %s - failed to parse certificate", + __func__); + return -1; + } + + p = *chain; + while (p && p->next) + p = p->next; + if (p && x509_name_compare(&cert->subject, &p->issuer) == 0) { + /* + * The new certificate is the issuer of the last certificate in + * the chain - add the new certificate to the end. + */ + p->next = cert; + } else { + /* Add to the beginning of the chain */ + cert->next = *chain; + *chain = cert; + } + + x509_name_string(&cert->subject, name, sizeof(name)); + wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name); + + return 0; +} + + +static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----"; +static const char *pem_cert_end = "-----END CERTIFICATE-----"; +static const char *pem_key_begin = "-----BEGIN RSA PRIVATE KEY-----"; +static const char *pem_key_end = "-----END RSA PRIVATE KEY-----"; +static const char *pem_key2_begin = "-----BEGIN PRIVATE KEY-----"; +static const char *pem_key2_end = "-----END PRIVATE KEY-----"; +static const char *pem_key_enc_begin = "-----BEGIN ENCRYPTED PRIVATE KEY-----"; +static const char *pem_key_enc_end = "-----END ENCRYPTED PRIVATE KEY-----"; + + +static const u8 * search_tag(const char *tag, const u8 *buf, size_t len) +{ + size_t i, plen; + + plen = os_strlen(tag); + if (len < plen) + return NULL; + + for (i = 0; i < len - plen; i++) { + if (os_memcmp(buf + i, tag, plen) == 0) + return buf + i; + } + + return NULL; +} + + +static int tlsv1_add_cert(struct x509_certificate **chain, + const u8 *buf, size_t len) +{ + const u8 *pos, *end; + unsigned char *der; + size_t der_len; + + pos = search_tag(pem_cert_begin, buf, len); + if (!pos) { + wpa_printf(MSG_DEBUG, "TLSv1: No PEM certificate tag found - " + "assume DER format"); + return tlsv1_add_cert_der(chain, buf, len); + } + + wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format certificate into " + "DER format"); + + while (pos) { + pos += os_strlen(pem_cert_begin); + end = search_tag(pem_cert_end, pos, buf + len - pos); + if (end == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Could not find PEM " + "certificate end tag (%s)", pem_cert_end); + return -1; + } + + der = base64_decode(pos, end - pos, &der_len); + if (der == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM " + "certificate"); + return -1; + } + + if (tlsv1_add_cert_der(chain, der, der_len) < 0) { + wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM " + "certificate after DER conversion"); + os_free(der); + return -1; + } + + os_free(der); + + end += os_strlen(pem_cert_end); + pos = search_tag(pem_cert_begin, end, buf + len - end); + } + + return 0; +} + + +static int tlsv1_set_cert_chain(struct x509_certificate **chain, + const char *cert, const u8 *cert_blob, + size_t cert_blob_len) +{ + if (cert_blob) + return tlsv1_add_cert(chain, cert_blob, cert_blob_len); + + if (cert) { + u8 *buf = NULL; + size_t len; + int ret; + + //buf = (u8 *) os_readfile(cert, &len); + if (buf == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", + cert); + return -1; + } + + ret = tlsv1_add_cert(chain, buf, len); + os_free(buf); + return ret; + } + + return 0; +} + + +/** + * tlsv1_set_ca_cert - Set trusted CA certificate(s) + * @cred: TLSv1 credentials from tlsv1_cred_alloc() + * @cert: File or reference name for X.509 certificate in PEM or DER format + * @cert_blob: cert as inlined data or %NULL if not used + * @cert_blob_len: ca_cert_blob length + * @path: Path to CA certificates (not yet supported) + * Returns: 0 on success, -1 on failure + */ +int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert, + const u8 *cert_blob, size_t cert_blob_len, + const char *path) +{ + if (tlsv1_set_cert_chain(&cred->trusted_certs, cert, + cert_blob, cert_blob_len) < 0) + return -1; + + if (path) { + /* TODO: add support for reading number of certificate files */ + wpa_printf(MSG_INFO, "TLSv1: Use of CA certificate directory " + "not yet supported"); + return -1; + } + + return 0; +} + + +/** + * tlsv1_set_cert - Set certificate + * @cred: TLSv1 credentials from tlsv1_cred_alloc() + * @cert: File or reference name for X.509 certificate in PEM or DER format + * @cert_blob: cert as inlined data or %NULL if not used + * @cert_blob_len: cert_blob length + * Returns: 0 on success, -1 on failure + */ +int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert, + const u8 *cert_blob, size_t cert_blob_len) +{ + return tlsv1_set_cert_chain(&cred->cert, cert, + cert_blob, cert_blob_len); +} + + +static struct crypto_private_key * tlsv1_set_key_pem(const u8 *key, size_t len) +{ + const u8 *pos, *end; + unsigned char *der; + size_t der_len; + struct crypto_private_key *pkey; + + pos = search_tag(pem_key_begin, key, len); + if (!pos) { + pos = search_tag(pem_key2_begin, key, len); + if (!pos) + return NULL; + pos += os_strlen(pem_key2_begin); + end = search_tag(pem_key2_end, pos, key + len - pos); + if (!end) + return NULL; + } else { + const u8 *pos2; + pos += os_strlen(pem_key_begin); + end = search_tag(pem_key_end, pos, key + len - pos); + if (!end) + return NULL; + pos2 = search_tag("Proc-Type: 4,ENCRYPTED", pos, end - pos); + if (pos2) { + wpa_printf(MSG_DEBUG, "TLSv1: Unsupported private key " + "format (Proc-Type/DEK-Info)"); + return NULL; + } + } + + der = base64_decode(pos, end - pos, &der_len); + if (!der) + return NULL; + pkey = crypto_private_key_import(der, der_len, NULL); + os_free(der); + return pkey; +} + + +static struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key, + size_t len, + const char *passwd) +{ + const u8 *pos, *end; + unsigned char *der; + size_t der_len; + struct crypto_private_key *pkey; + + if (passwd == NULL) + return NULL; + pos = search_tag(pem_key_enc_begin, key, len); + if (!pos) + return NULL; + pos += os_strlen(pem_key_enc_begin); + end = search_tag(pem_key_enc_end, pos, key + len - pos); + if (!end) + return NULL; + + der = base64_decode(pos, end - pos, &der_len); + if (!der) + return NULL; + pkey = crypto_private_key_import(der, der_len, passwd); + os_free(der); + return pkey; +} + + +static int tlsv1_set_key(struct tlsv1_credentials *cred, + const u8 *key, size_t len, const char *passwd) +{ + cred->key = crypto_private_key_import(key, len, passwd); + if (cred->key == NULL) + cred->key = tlsv1_set_key_pem(key, len); + if (cred->key == NULL) + cred->key = tlsv1_set_key_enc_pem(key, len, passwd); + if (cred->key == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key"); + return -1; + } + return 0; +} + + +/** + * tlsv1_set_private_key - Set private key + * @cred: TLSv1 credentials from tlsv1_cred_alloc() + * @private_key: File or reference name for the key in PEM or DER format + * @private_key_passwd: Passphrase for decrypted private key, %NULL if no + * passphrase is used. + * @private_key_blob: private_key as inlined data or %NULL if not used + * @private_key_blob_len: private_key_blob length + * Returns: 0 on success, -1 on failure + */ +int tlsv1_set_private_key(struct tlsv1_credentials *cred, + const char *private_key, + const char *private_key_passwd, + const u8 *private_key_blob, + size_t private_key_blob_len) +{ + crypto_private_key_free(cred->key); + cred->key = NULL; + + if (private_key_blob) + return tlsv1_set_key(cred, private_key_blob, + private_key_blob_len, + private_key_passwd); + + if (private_key) { + u8 *buf = NULL; + size_t len; + int ret; + + //buf = (u8 *) os_readfile(private_key, &len); + if (buf == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", + private_key); + return -1; + } + + ret = tlsv1_set_key(cred, buf, len, private_key_passwd); + os_free(buf); + return ret; + } + + return 0; +} + + +static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred, + const u8 *dh, size_t len) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + + pos = dh; + end = dh + len; + + /* + * DHParameter ::= SEQUENCE { + * prime INTEGER, -- p + * base INTEGER, -- g + * privateValueLength INTEGER OPTIONAL } + */ + + /* DHParamer ::= SEQUENCE */ + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "DH: DH parameters did not start with a " + "valid SEQUENCE - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + + /* prime INTEGER */ + if (asn1_get_next(pos, end - pos, &hdr) < 0) + return -1; + + if (hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for p; " + "class=%d tag=0x%x", hdr.class, hdr.tag); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "DH: prime (p)", hdr.payload, hdr.length); + if (hdr.length == 0) + return -1; + os_free(cred->dh_p); + cred->dh_p = os_malloc(hdr.length); + if (cred->dh_p == NULL) + return -1; + os_memcpy(cred->dh_p, hdr.payload, hdr.length); + cred->dh_p_len = hdr.length; + pos = hdr.payload + hdr.length; + + /* base INTEGER */ + if (asn1_get_next(pos, end - pos, &hdr) < 0) + return -1; + + if (hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for g; " + "class=%d tag=0x%x", hdr.class, hdr.tag); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "DH: base (g)", hdr.payload, hdr.length); + if (hdr.length == 0) + return -1; + os_free(cred->dh_g); + cred->dh_g = os_malloc(hdr.length); + if (cred->dh_g == NULL) + return -1; + os_memcpy(cred->dh_g, hdr.payload, hdr.length); + cred->dh_g_len = hdr.length; + + return 0; +} + + +static const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----"; +static const char *pem_dhparams_end = "-----END DH PARAMETERS-----"; + + +static int tlsv1_set_dhparams_blob(struct tlsv1_credentials *cred, + const u8 *buf, size_t len) +{ + const u8 *pos, *end; + unsigned char *der; + size_t der_len; + + pos = search_tag(pem_dhparams_begin, buf, len); + if (!pos) { + wpa_printf(MSG_DEBUG, "TLSv1: No PEM dhparams tag found - " + "assume DER format"); + return tlsv1_set_dhparams_der(cred, buf, len); + } + + wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format dhparams into DER " + "format"); + + pos += os_strlen(pem_dhparams_begin); + end = search_tag(pem_dhparams_end, pos, buf + len - pos); + if (end == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Could not find PEM dhparams end " + "tag (%s)", pem_dhparams_end); + return -1; + } + + der = base64_decode(pos, end - pos, &der_len); + if (der == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams"); + return -1; + } + + if (tlsv1_set_dhparams_der(cred, der, der_len) < 0) { + wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM dhparams " + "DER conversion"); + os_free(der); + return -1; + } + + os_free(der); + + return 0; +} + + +/** + * tlsv1_set_dhparams - Set Diffie-Hellman parameters + * @cred: TLSv1 credentials from tlsv1_cred_alloc() + * @dh_file: File or reference name for the DH params in PEM or DER format + * @dh_blob: DH params as inlined data or %NULL if not used + * @dh_blob_len: dh_blob length + * Returns: 0 on success, -1 on failure + */ +int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file, + const u8 *dh_blob, size_t dh_blob_len) +{ + if (dh_blob) + return tlsv1_set_dhparams_blob(cred, dh_blob, dh_blob_len); + + if (dh_file) { + u8 *buf = NULL; + size_t len; + int ret; + + //buf = (u8 *) os_readfile(dh_file, &len); + if (buf == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", + dh_file); + return -1; + } + + ret = tlsv1_set_dhparams_blob(cred, buf, len); + os_free(buf); + return ret; + } + + return 0; +} diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_record.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_record.c new file mode 100644 index 0000000000..879d9f5454 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_record.c @@ -0,0 +1,553 @@ +/* + * 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" + +#include "wpa2/eap_peer/eap_i.h" + +/** + * tlsv1_record_set_cipher_suite - TLS record layer: Set cipher suite + * @rl: Pointer to TLS record layer data + * @cipher_suite: New cipher suite + * Returns: 0 on success, -1 on failure + * + * This function is used to prepare TLS record layer for cipher suite change. + * tlsv1_record_change_write_cipher() and + * tlsv1_record_change_read_cipher() functions can then be used to change the + * currently used ciphers. + */ +int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl, + u16 cipher_suite) +{ + const struct tls_cipher_suite *suite; + const struct tls_cipher_data *data; + + wpa_printf(MSG_DEBUG, "TLSv1: Selected cipher suite: 0x%04x", + cipher_suite); + rl->cipher_suite = cipher_suite; + + suite = tls_get_cipher_suite(cipher_suite); + if (suite == NULL) + return -1; + + if (suite->hash == TLS_HASH_MD5) { + rl->hash_alg = CRYPTO_HASH_ALG_HMAC_MD5; + rl->hash_size = MD5_MAC_LEN; + } else if (suite->hash == TLS_HASH_SHA) { + rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA1; + rl->hash_size = SHA1_MAC_LEN; + } else if (suite->hash == TLS_HASH_SHA256) { + rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA256; + rl->hash_size = SHA256_MAC_LEN; + } + + data = tls_get_cipher_data(suite->cipher); + if (data == NULL) + return -1; + + rl->key_material_len = data->key_material; + rl->iv_size = data->block_size; + rl->cipher_alg = data->alg; + + return 0; +} + + +/** + * tlsv1_record_change_write_cipher - TLS record layer: Change write cipher + * @rl: Pointer to TLS record layer data + * Returns: 0 on success (cipher changed), -1 on failure + * + * This function changes TLS record layer to use the new cipher suite + * configured with tlsv1_record_set_cipher_suite() for writing. + */ +int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl) +{ + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New write cipher suite " + "0x%04x", rl->cipher_suite); + rl->write_cipher_suite = rl->cipher_suite; + os_memset(rl->write_seq_num, 0, TLS_SEQ_NUM_LEN); + + if (rl->write_cbc) { + if (wpa2_crypto_funcs.crypto_cipher_deinit) { + wpa2_crypto_funcs.crypto_cipher_deinit(rl->write_cbc); + rl->write_cbc = NULL; + } else { + wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n"); + return -1; + } + } + if (rl->cipher_alg != CRYPTO_CIPHER_NULL) { + if (wpa2_crypto_funcs.crypto_cipher_init) { + rl->write_cbc = wpa2_crypto_funcs.crypto_cipher_init(rl->cipher_alg, + rl->write_iv, rl->write_key, + rl->key_material_len); + } else { + wpa_printf(MSG_ERROR, "Fail to register crypto_cipher_init function!\r\n"); + return -1; + } + + if (rl->write_cbc == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize " + "cipher"); + return -1; + } + } + + return 0; +} + + +/** + * tlsv1_record_change_read_cipher - TLS record layer: Change read cipher + * @rl: Pointer to TLS record layer data + * Returns: 0 on success (cipher changed), -1 on failure + * + * This function changes TLS record layer to use the new cipher suite + * configured with tlsv1_record_set_cipher_suite() for reading. + */ +int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl) +{ + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New read cipher suite " + "0x%04x \n", rl->cipher_suite); + rl->read_cipher_suite = rl->cipher_suite; + os_memset(rl->read_seq_num, 0, TLS_SEQ_NUM_LEN); + + if (rl->read_cbc) { + if (wpa2_crypto_funcs.crypto_cipher_deinit) { + wpa2_crypto_funcs.crypto_cipher_deinit(rl->read_cbc); + rl->read_cbc = NULL; + } else { + wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n"); + return -1; + } + } + + if (rl->cipher_alg != CRYPTO_CIPHER_NULL) { + if(wpa2_crypto_funcs.crypto_cipher_init) { + rl->read_cbc = wpa2_crypto_funcs.crypto_cipher_init(rl->cipher_alg, + rl->read_iv, rl->read_key, + rl->key_material_len); + } else { + wpa_printf(MSG_ERROR, "Fail to register crypto_cipher_init function!\r\n"); + return -1; + } + if (rl->read_cbc == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize " + "cipher"); + return -1; + } + } + + return 0; +} + + +/** + * tlsv1_record_send - TLS record layer: Send a message + * @rl: Pointer to TLS record layer data + * @content_type: Content type (TLS_CONTENT_TYPE_*) + * @buf: Buffer for the generated TLS message (needs to have extra space for + * header, IV (TLS v1.1), and HMAC) + * @buf_size: Maximum buf size + * @payload: Payload to be sent + * @payload_len: Length of the payload + * @out_len: Buffer for returning the used buf length + * Returns: 0 on success, -1 on failure + * + * This function fills in the TLS record layer header, adds HMAC, and encrypts + * the data using the current write cipher. + */ +int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, + size_t buf_size, const u8 *payload, size_t payload_len, + size_t *out_len) +{ + u8 *pos, *ct_start, *length, *cpayload; + struct crypto_hash *hmac = NULL; + size_t clen; + int explicit_iv; + + pos = buf; + if (pos + TLS_RECORD_HEADER_LEN > buf + buf_size) + return -1; + + /* ContentType type */ + ct_start = pos; + *pos++ = content_type; + /* ProtocolVersion version */ + WPA_PUT_BE16(pos, rl->tls_version); + pos += 2; + /* uint16 length */ + length = pos; + WPA_PUT_BE16(length, payload_len); + pos += 2; + + cpayload = pos; + explicit_iv = rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL && + rl->iv_size && rl->tls_version >= TLS_VERSION_1_1; + if (explicit_iv) { + /* opaque IV[Cipherspec.block_length] */ + if (pos + rl->iv_size > buf + buf_size) + return -1; + + /* + * Use random number R per the RFC 4346, 6.2.3.2 CBC Block + * Cipher option 2a. + */ + + if (os_get_random(pos, rl->iv_size)) + return -1; + pos += rl->iv_size; + } + + /* + * opaque fragment[TLSPlaintext.length] + * (opaque content[TLSCompressed.length] in GenericBlockCipher) + */ + if (pos + payload_len > buf + buf_size) + return -1; + os_memmove(pos, payload, payload_len); + pos += payload_len; + + if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) { + /* + * MAC calculated over seq_num + TLSCompressed.type + + * TLSCompressed.version + TLSCompressed.length + + * TLSCompressed.fragment + */ + if (wpa2_crypto_funcs.crypto_hash_init) { + hmac = wpa2_crypto_funcs.crypto_hash_init(rl->hash_alg, rl->write_mac_secret, rl->hash_size); + } else { + wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash init!\r\n", __FUNCTION__); + return -1; + } + if (hmac == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " + "to initialize HMAC"); + return -1; + } + if (wpa2_crypto_funcs.crypto_hash_update) { + wpa2_crypto_funcs.crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN); + /* type + version + length + fragment */ + wpa2_crypto_funcs.crypto_hash_update(hmac, ct_start, TLS_RECORD_HEADER_LEN); + wpa2_crypto_funcs.crypto_hash_update(hmac, payload, payload_len); + } else { + wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash update!\r\n", __FUNCTION__); + return -1; + } + clen = buf + buf_size - pos; + if (clen < rl->hash_size) { + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not " + "enough room for MAC"); + if (wpa2_crypto_funcs.crypto_hash_finish) { + wpa2_crypto_funcs.crypto_hash_finish(hmac, NULL, NULL); + } else { + wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash finish function!\r\n", __FUNCTION__); + return -1; + } + + return -1; + } + + if (wpa2_crypto_funcs.crypto_hash_finish) { + if ((int)wpa2_crypto_funcs.crypto_hash_finish(hmac, pos, (int *)&clen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed to calculate HMAC"); + return -1; + } + } else { + wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n",__FUNCTION__); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Write HMAC", + pos, clen); + pos += clen; + if (rl->iv_size) { + size_t len = pos - cpayload; + size_t pad; + pad = (len + 1) % rl->iv_size; + if (pad) + pad = rl->iv_size - pad; + if (pos + pad + 1 > buf + buf_size) { + wpa_printf(MSG_DEBUG, "TLSv1: No room for " + "block cipher padding"); + return -1; + } + os_memset(pos, pad, pad + 1); + pos += pad + 1; + } + + if (wpa2_crypto_funcs.crypto_cipher_encrypt) { + if ((int)wpa2_crypto_funcs.crypto_cipher_encrypt(rl->write_cbc, cpayload, + cpayload, pos - cpayload) < 0) + return -1; + } else { + wpa_printf(MSG_ERROR, "Fail to register crypto_cipher_encrypt function!\r\n"); + return -1; + } + } + + WPA_PUT_BE16(length, pos - length - 2); + inc_byte_array(rl->write_seq_num, TLS_SEQ_NUM_LEN); + + *out_len = pos - buf; + + return 0; +} + + +/** + * tlsv1_record_receive - TLS record layer: Process a received message + * @rl: Pointer to TLS record layer data + * @in_data: Received data + * @in_len: Length of the received data + * @out_data: Buffer for output data (must be at least as long as in_data) + * @out_len: Set to maximum out_data length by caller; used to return the + * length of the used data + * @alert: Buffer for returning an alert value on failure + * Returns: Number of bytes used from in_data on success, 0 if record was not + * complete (more data needed), or -1 on failure + * + * This function decrypts the received message, verifies HMAC and TLS record + * layer header. + */ +int tlsv1_record_receive(struct tlsv1_record_layer *rl, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t *out_len, u8 *alert) +{ + size_t i, rlen, hlen; + u8 padlen; + struct crypto_hash *hmac = NULL; + u8 len[2], hash[100]; + int force_mac_error = 0; + u8 ct; + + if (in_len < TLS_RECORD_HEADER_LEN) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu) - " + "need more data", + (unsigned long) in_len); + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received", + in_data, in_len); + return 0; + } + + ct = in_data[0]; + rlen = WPA_GET_BE16(in_data + 3); + wpa_printf(MSG_DEBUG, "TLSv1: Received content type %d version %d.%d " + "length %d", ct, in_data[1], in_data[2], (int) rlen); + + /* + * TLS v1.0 and v1.1 RFCs were not exactly clear on the use of the + * protocol version in record layer. As such, accept any {03,xx} value + * to remain compatible with existing implementations. + */ + if (in_data[1] != 0x03) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version " + "%u.%u", in_data[1], in_data[2]); + *alert = TLS_ALERT_PROTOCOL_VERSION; + return -1; + } + + /* TLSCiphertext must not be more than 2^14+2048 bytes */ + if (TLS_RECORD_HEADER_LEN + rlen > 18432) { + wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)", + (unsigned long) (TLS_RECORD_HEADER_LEN + rlen)); + *alert = TLS_ALERT_RECORD_OVERFLOW; + return -1; + } + + in_data += TLS_RECORD_HEADER_LEN; + in_len -= TLS_RECORD_HEADER_LEN; + + if (rlen > in_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Not all record data included " + "(rlen=%lu > in_len=%lu)", + (unsigned long) rlen, (unsigned long) in_len); + return 0; + } + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received", + in_data, rlen); + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE && + ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC && + ct != TLS_CONTENT_TYPE_ALERT && + ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { + wpa_printf(MSG_DEBUG, "TLSv1: Ignore record with unknown " + "content type 0x%x", ct); + *alert = TLS_ALERT_UNEXPECTED_MESSAGE; + return -1; + } + + in_len = rlen; + + if (*out_len < in_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Not enough output buffer for " + "processing received record"); + *alert = TLS_ALERT_INTERNAL_ERROR; + return -1; + } + + if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) { + size_t plen; + if (wpa2_crypto_funcs.crypto_cipher_decrypt) { + if ((int)wpa2_crypto_funcs.crypto_cipher_decrypt(rl->read_cbc, in_data, + out_data, in_len) < 0) { + *alert = TLS_ALERT_DECRYPTION_FAILED; + return -1; + } + } else { + wpa_printf(MSG_ERROR, "Fail to register crypto cipher decrypt function. \r\n"); + *alert = TLS_ALERT_DECRYPTION_FAILED; + return -1; + } + plen = in_len; + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted " + "data", out_data, plen); + + if (rl->iv_size) { + /* + * TLS v1.0 defines different alert values for various + * failures. That may information to aid in attacks, so + * use the same bad_record_mac alert regardless of the + * issues. + * + * In addition, instead of returning immediately on + * error, run through the MAC check to make timing + * attacks more difficult. + */ + + if (rl->tls_version >= TLS_VERSION_1_1) { + /* Remove opaque IV[Cipherspec.block_length] */ + if (plen < rl->iv_size) { + wpa_printf(MSG_DEBUG, "TLSv1.1: Not " + "enough room for IV"); + force_mac_error = 1; + goto check_mac; + } + os_memmove(out_data, out_data + rl->iv_size, + plen - rl->iv_size); + plen -= rl->iv_size; + } + + /* Verify and remove padding */ + if (plen == 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short record" + " (no pad)"); + force_mac_error = 1; + goto check_mac; + } + padlen = out_data[plen - 1]; + if (padlen >= plen) { + wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad " + "length (%u, plen=%lu) in " + "received record", + padlen, (unsigned long) plen); + force_mac_error = 1; + goto check_mac; + } + for (i = plen - padlen - 1; i < plen - 1; i++) { + if (out_data[i] != padlen) { + wpa_hexdump(MSG_DEBUG, + "TLSv1: Invalid pad in " + "received record", + out_data + plen - padlen - + 1, padlen + 1); + force_mac_error = 1; + goto check_mac; + } + } + + plen -= padlen + 1; + + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - " + "Decrypted data with IV and padding " + "removed", out_data, plen); + } + + check_mac: + if (plen < rl->hash_size) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no " + "hash value"); + *alert = TLS_ALERT_BAD_RECORD_MAC; + return -1; + } + + plen -= rl->hash_size; + + if (wpa2_crypto_funcs.crypto_hash_init) { + hmac = wpa2_crypto_funcs.crypto_hash_init(rl->hash_alg, rl->read_mac_secret, rl->hash_size); + } else { + wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_init function!\r\n", __FUNCTION__); + return -1; + } + + if (hmac == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " + "to initialize HMAC"); + *alert = TLS_ALERT_INTERNAL_ERROR; + return -1; + } + + if (wpa2_crypto_funcs.crypto_hash_update) { + wpa2_crypto_funcs.crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN); + /* type + version + length + fragment */ + wpa2_crypto_funcs.crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3); + WPA_PUT_BE16(len, plen); + wpa2_crypto_funcs.crypto_hash_update(hmac, len, 2); + wpa2_crypto_funcs.crypto_hash_update(hmac, out_data, plen); + } else { + wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash update function!\r\n", __FUNCTION__); + return -1; + } + hlen = sizeof(hash); + if (wpa2_crypto_funcs.crypto_hash_finish) { + if ((int)wpa2_crypto_funcs.crypto_hash_finish(hmac, hash, (int *)&hlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed to calculate HMAC"); + *alert = TLS_ALERT_INTERNAL_ERROR; + return -1; + } + } else { + wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__); + *alert = TLS_ALERT_INTERNAL_ERROR; + return -1; + } + if (hlen != rl->hash_size || + os_memcmp(hash, out_data + plen, hlen) != 0 || + force_mac_error) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in " + "received message (force_mac_error=%d)", + force_mac_error); + *alert = TLS_ALERT_BAD_RECORD_MAC; + return -1; + } + + *out_len = plen; + } else { + os_memcpy(out_data, in_data, in_len); + *out_len = in_len; + } + + /* TLSCompressed must not be more than 2^14+1024 bytes */ + if (TLS_RECORD_HEADER_LEN + *out_len > 17408) { + wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)", + (unsigned long) (TLS_RECORD_HEADER_LEN + *out_len)); + *alert = TLS_ALERT_RECORD_OVERFLOW; + return -1; + } + + inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN); + + return TLS_RECORD_HEADER_LEN + rlen; +} diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_server.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_server.c new file mode 100644 index 0000000000..642b09c7b7 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_server.c @@ -0,0 +1,656 @@ +/* + * 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" + +/* TODO: + * Support for a message fragmented across several records (RFC 2246, 6.2.1) + */ + + +void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description) +{ + conn->alert_level = level; + conn->alert_description = description; +} + + +int tlsv1_server_derive_keys(struct tlsv1_server *conn, + const u8 *pre_master_secret, + size_t pre_master_secret_len) +{ + u8 seed[2 * TLS_RANDOM_LEN]; + u8 key_block[TLS_MAX_KEY_BLOCK_LEN]; + u8 *pos; + size_t key_block_len; + + if (pre_master_secret) { + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret", + pre_master_secret, pre_master_secret_len); + os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random, + TLS_RANDOM_LEN); + if (tls_prf(conn->rl.tls_version, + pre_master_secret, pre_master_secret_len, + "master secret", seed, 2 * TLS_RANDOM_LEN, + conn->master_secret, TLS_MASTER_SECRET_LEN)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive " + "master_secret"); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret", + conn->master_secret, TLS_MASTER_SECRET_LEN); + } + + os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN); + key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len + + conn->rl.iv_size); + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + "key expansion", seed, 2 * TLS_RANDOM_LEN, + key_block, key_block_len)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block"); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block", + key_block, key_block_len); + + pos = key_block; + + /* client_write_MAC_secret */ + os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size); + pos += conn->rl.hash_size; + /* server_write_MAC_secret */ + os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size); + pos += conn->rl.hash_size; + + /* client_write_key */ + os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len); + pos += conn->rl.key_material_len; + /* server_write_key */ + os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len); + pos += conn->rl.key_material_len; + + /* client_write_IV */ + os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size); + pos += conn->rl.iv_size; + /* server_write_IV */ + os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size); + pos += conn->rl.iv_size; + + return 0; +} + + +/** + * tlsv1_server_handshake - Process TLS handshake + * @conn: TLSv1 server connection data from tlsv1_server_init() + * @in_data: Input data from TLS peer + * @in_len: Input data length + * @out_len: Length of the output buffer. + * Returns: Pointer to output data, %NULL on failure + */ +u8 * tlsv1_server_handshake(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, + size_t *out_len) +{ + const u8 *pos, *end; + u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct; + size_t in_msg_len; + int used; + + if (in_data == NULL || in_len == 0) { + wpa_printf(MSG_DEBUG, "TLSv1: No input data to server"); + return NULL; + } + + pos = in_data; + end = in_data + in_len; + in_msg = os_malloc(in_len); + if (in_msg == NULL) + return NULL; + + /* Each received packet may include multiple records */ + while (pos < end) { + in_msg_len = in_len; + used = tlsv1_record_receive(&conn->rl, pos, end - pos, + in_msg, &in_msg_len, &alert); + if (used < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Processing received " + "record failed"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + goto failed; + } + if (used == 0) { + /* need more data */ + wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not " + "yet supported"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + goto failed; + } + ct = pos[0]; + + in_pos = in_msg; + in_end = in_msg + in_msg_len; + + /* Each received record may include multiple messages of the + * same ContentType. */ + while (in_pos < in_end) { + in_msg_len = in_end - in_pos; + if (tlsv1_server_process_handshake(conn, ct, in_pos, + &in_msg_len) < 0) + goto failed; + in_pos += in_msg_len; + } + + pos += used; + } + + os_free(in_msg); + in_msg = NULL; + + msg = tlsv1_server_handshake_write(conn, out_len); + +failed: + os_free(in_msg); + if (conn->alert_level) { + if (conn->state == FAILED) { + /* Avoid alert loops */ + wpa_printf(MSG_DEBUG, "TLSv1: Drop alert loop"); + os_free(msg); + return NULL; + } + conn->state = FAILED; + os_free(msg); + msg = tlsv1_server_send_alert(conn, conn->alert_level, + conn->alert_description, + out_len); + } + + return msg; +} + + +/** + * tlsv1_server_encrypt - Encrypt data into TLS tunnel + * @conn: TLSv1 server connection data from tlsv1_server_init() + * @in_data: Pointer to plaintext data to be encrypted + * @in_len: Input buffer length + * @out_data: Pointer to output buffer (encrypted TLS data) + * @out_len: Maximum out_data length + * Returns: Number of bytes written to out_data, -1 on failure + * + * This function is used after TLS handshake has been completed successfully to + * send data in the encrypted tunnel. + */ +int tlsv1_server_encrypt(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len) +{ + size_t rlen; + + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData", + in_data, in_len); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA, + out_data, out_len, in_data, in_len, &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + return rlen; +} + + +/** + * tlsv1_server_decrypt - Decrypt data from TLS tunnel + * @conn: TLSv1 server connection data from tlsv1_server_init() + * @in_data: Pointer to input buffer (encrypted TLS data) + * @in_len: Input buffer length + * @out_data: Pointer to output buffer (decrypted data from TLS tunnel) + * @out_len: Maximum out_data length + * Returns: Number of bytes written to out_data, -1 on failure + * + * This function is used after TLS handshake has been completed successfully to + * receive data from the encrypted tunnel. + */ +int tlsv1_server_decrypt(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len) +{ + const u8 *in_end, *pos; + int used; + u8 alert, *out_end, *out_pos, ct; + size_t olen; + + pos = in_data; + in_end = in_data + in_len; + out_pos = out_data; + out_end = out_data + out_len; + + while (pos < in_end) { + ct = pos[0]; + olen = out_end - out_pos; + used = tlsv1_record_receive(&conn->rl, pos, in_end - pos, + out_pos, &olen, &alert); + if (used < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing " + "failed"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + return -1; + } + if (used == 0) { + /* need more data */ + wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not " + "yet supported"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + return -1; + } + + if (ct == TLS_CONTENT_TYPE_ALERT) { + if (olen < 2) { + wpa_printf(MSG_DEBUG, "TLSv1: Alert " + "underflow"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", + out_pos[0], out_pos[1]); + if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) { + /* Continue processing */ + pos += used; + continue; + } + + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + out_pos[1]); + return -1; + } + + if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type " + "0x%x", pos[0]); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + out_pos += olen; + if (out_pos > out_end) { + wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough " + "for processing the received record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + pos += used; + } + + return out_pos - out_data; +} + + +/** + * tlsv1_server_global_init - Initialize TLSv1 server + * Returns: 0 on success, -1 on failure + * + * This function must be called before using any other TLSv1 server functions. + */ +int tlsv1_server_global_init(void) +{ + return crypto_global_init(); +} + + +/** + * tlsv1_server_global_deinit - Deinitialize TLSv1 server + * + * This function can be used to deinitialize the TLSv1 server that was + * initialized by calling tlsv1_server_global_init(). No TLSv1 server functions + * can be called after this before calling tlsv1_server_global_init() again. + */ +void tlsv1_server_global_deinit(void) +{ + crypto_global_deinit(); +} + + +/** + * tlsv1_server_init - Initialize TLSv1 server connection + * @cred: Pointer to server credentials from tlsv1_server_cred_alloc() + * Returns: Pointer to TLSv1 server connection data or %NULL on failure + */ +struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred) +{ + struct tlsv1_server *conn; + size_t count; + u16 *suites; + + conn = (struct tlsv1_server *)os_zalloc(sizeof(*conn)); + if (conn == NULL) + return NULL; + + conn->cred = cred; + + conn->state = CLIENT_HELLO; + + if (tls_verify_hash_init(&conn->verify) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify " + "hash"); + os_free(conn); + return NULL; + } + + count = 0; + suites = conn->cipher_suites; + suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; + suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; + suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; + suites[count++] = TLS_RSA_WITH_RC4_128_SHA; + suites[count++] = TLS_RSA_WITH_RC4_128_MD5; + conn->num_cipher_suites = count; + + return conn; +} + + +static void tlsv1_server_clear_data(struct tlsv1_server *conn) +{ + tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL); + tlsv1_record_change_write_cipher(&conn->rl); + tlsv1_record_change_read_cipher(&conn->rl); + tls_verify_hash_free(&conn->verify); + + crypto_public_key_free(conn->client_rsa_key); + conn->client_rsa_key = NULL; + + os_free(conn->session_ticket); + conn->session_ticket = NULL; + conn->session_ticket_len = 0; + conn->use_session_ticket = 0; + + os_free(conn->dh_secret); + conn->dh_secret = NULL; + conn->dh_secret_len = 0; +} + + +/** + * tlsv1_server_deinit - Deinitialize TLSv1 server connection + * @conn: TLSv1 server connection data from tlsv1_server_init() + */ +void tlsv1_server_deinit(struct tlsv1_server *conn) +{ + tlsv1_server_clear_data(conn); + os_free(conn); +} + + +/** + * tlsv1_server_established - Check whether connection has been established + * @conn: TLSv1 server connection data from tlsv1_server_init() + * Returns: 1 if connection is established, 0 if not + */ +int tlsv1_server_established(struct tlsv1_server *conn) +{ + return conn->state == ESTABLISHED; +} + + +/** + * tlsv1_server_prf - Use TLS-PRF to derive keying material + * @conn: TLSv1 server connection data from tlsv1_server_init() + * @label: Label (e.g., description of the key) for PRF + * @server_random_first: seed is 0 = client_random|server_random, + * 1 = server_random|client_random + * @out: Buffer for output data from TLS-PRF + * @out_len: Length of the output buffer + * Returns: 0 on success, -1 on failure + */ +int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, + int server_random_first, u8 *out, size_t out_len) +{ + u8 seed[2 * TLS_RANDOM_LEN]; + + if (conn->state != ESTABLISHED) + return -1; + + if (server_random_first) { + os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, + TLS_RANDOM_LEN); + } else { + os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random, + TLS_RANDOM_LEN); + } + + return tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + label, seed, 2 * TLS_RANDOM_LEN, out, out_len); +} + + +/** + * tlsv1_server_get_cipher - Get current cipher name + * @conn: TLSv1 server connection data from tlsv1_server_init() + * @buf: Buffer for the cipher name + * @buflen: buf size + * Returns: 0 on success, -1 on failure + * + * Get the name of the currently used cipher. + */ +int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf, + size_t buflen) +{ +#ifndef ESPRESSIF_USE + char *cipher; + + switch (conn->rl.cipher_suite) { + case TLS_RSA_WITH_RC4_128_MD5: + cipher = "RC4-MD5"; + break; + case TLS_RSA_WITH_RC4_128_SHA: + cipher = "RC4-SHA"; + break; + case TLS_RSA_WITH_DES_CBC_SHA: + cipher = "DES-CBC-SHA"; + break; + case TLS_RSA_WITH_3DES_EDE_CBC_SHA: + cipher = "DES-CBC3-SHA"; + break; + case TLS_DH_anon_WITH_AES_128_CBC_SHA: + cipher = "ADH-AES-128-SHA"; + break; + case TLS_RSA_WITH_AES_256_CBC_SHA: + cipher = "AES-256-SHA"; + break; + case TLS_RSA_WITH_AES_128_CBC_SHA: + cipher = "AES-128-SHA"; + break; + default: + return -1; + } + + //if (os_strlcpy(buf, cipher, buflen) >= buflen) + // return -1; + + os_memcpy((u8 *)buf, (u8 *)cipher, buflen); + + return 0; +#else + char cipher[20]; + + switch (conn->rl.cipher_suite) { + case TLS_RSA_WITH_RC4_128_MD5: + strcpy(cipher, "RC4-MD5"); + break; + case TLS_RSA_WITH_RC4_128_SHA: + strcpy(cipher, "RC4-SHA"); + break; + case TLS_RSA_WITH_DES_CBC_SHA: + strcpy(cipher, "DES-CBC-SHA"); + break; + case TLS_RSA_WITH_3DES_EDE_CBC_SHA: + strcpy(cipher, "DES-CBC3-SHA"); + break; + case TLS_DH_anon_WITH_AES_128_CBC_SHA: + strcpy(cipher, "ADH-AES-128-SHA"); + break; + case TLS_RSA_WITH_AES_256_CBC_SHA: + strcpy(cipher, "AES-256-SHA"); + break; + case TLS_RSA_WITH_AES_128_CBC_SHA: + strcpy(cipher, "AES-128-SHA"); + break; + default: + return -1; + } + os_memcpy((u8 *)buf, (u8 *)cipher, buflen); + + return 0; +#endif +} + + +/** + * tlsv1_server_shutdown - Shutdown TLS connection + * @conn: TLSv1 server connection data from tlsv1_server_init() + * Returns: 0 on success, -1 on failure + */ +int tlsv1_server_shutdown(struct tlsv1_server *conn) +{ + conn->state = CLIENT_HELLO; + + if (tls_verify_hash_init(&conn->verify) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify " + "hash"); + return -1; + } + + tlsv1_server_clear_data(conn); + + return 0; +} + + +/** + * tlsv1_server_resumed - Was session resumption used + * @conn: TLSv1 server connection data from tlsv1_server_init() + * Returns: 1 if current session used session resumption, 0 if not + */ +int tlsv1_server_resumed(struct tlsv1_server *conn) +{ + return 0; +} + + +/** + * tlsv1_server_get_keys - Get master key and random data from TLS connection + * @conn: TLSv1 server connection data from tlsv1_server_init() + * @keys: Structure of key/random data (filled on success) + * Returns: 0 on success, -1 on failure + */ +int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys) +{ + os_memset(keys, 0, sizeof(*keys)); + if (conn->state == CLIENT_HELLO) + return -1; + + keys->client_random = conn->client_random; + keys->client_random_len = TLS_RANDOM_LEN; + + if (conn->state != SERVER_HELLO) { + keys->server_random = conn->server_random; + keys->server_random_len = TLS_RANDOM_LEN; + keys->master_key = conn->master_secret; + keys->master_key_len = TLS_MASTER_SECRET_LEN; + } + + return 0; +} + + +/** + * tlsv1_server_get_keyblock_size - Get TLS key_block size + * @conn: TLSv1 server connection data from tlsv1_server_init() + * Returns: Size of the key_block for the negotiated cipher suite or -1 on + * failure + */ +int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn) +{ + if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO) + return -1; + + return 2 * (conn->rl.hash_size + conn->rl.key_material_len + + conn->rl.iv_size); +} + + +/** + * tlsv1_server_set_cipher_list - Configure acceptable cipher suites + * @conn: TLSv1 server connection data from tlsv1_server_init() + * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers + * (TLS_CIPHER_*). + * Returns: 0 on success, -1 on failure + */ +int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers) +{ + size_t count; + u16 *suites; + + /* TODO: implement proper configuration of cipher suites */ + if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) { + count = 0; + suites = conn->cipher_suites; + suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; + suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; + suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; + suites[count++] = TLS_RSA_WITH_RC4_128_SHA; + suites[count++] = TLS_RSA_WITH_RC4_128_MD5; + suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA; + suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA; + suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA; + suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5; + suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA; + conn->num_cipher_suites = count; + } + + return 0; +} + + +int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer) +{ + conn->verify_peer = verify_peer; + return 0; +} + + +void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn, + tlsv1_server_session_ticket_cb cb, + void *ctx) +{ + wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)", + cb, ctx); + conn->session_ticket_cb = cb; + conn->session_ticket_cb_ctx = ctx; +} diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_server_read.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_server_read.c new file mode 100644 index 0000000000..ee477c98a0 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_server_read.c @@ -0,0 +1,1278 @@ +/* + * 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" + +#include "wpa2/eap_peer/eap_i.h" + +static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, + const u8 *in_data, size_t *in_len); +static int tls_process_change_cipher_spec(struct tlsv1_server *conn, + u8 ct, const u8 *in_data, + size_t *in_len); + + +static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end, *c; + size_t left, len, i, j; + u16 cipher_suite; + u16 num_suites; + int compr_null_found; + u16 ext_type, ext_len; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) + goto decode_error; + + /* HandshakeType msg_type */ + if (*pos != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected ClientHello)", *pos); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + wpa_printf(MSG_DEBUG, "TLSv1: Received ClientHello"); + pos++; + /* uint24 length */ + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) + goto decode_error; + + /* body - ClientHello */ + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello", pos, len); + end = pos + len; + + /* ProtocolVersion client_version */ + if (end - pos < 2) + goto decode_error; + conn->client_version = WPA_GET_BE16(pos); + wpa_printf(MSG_DEBUG, "TLSv1: Client version %d.%d", + conn->client_version >> 8, conn->client_version & 0xff); + if (conn->client_version < TLS_VERSION_1) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " + "ClientHello %u.%u", + conn->client_version >> 8, + conn->client_version & 0xff); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_PROTOCOL_VERSION); + return -1; + } + pos += 2; + + if (TLS_VERSION == TLS_VERSION_1) + conn->rl.tls_version = TLS_VERSION_1; +#ifdef CONFIG_TLSV12 + else if (conn->client_version >= TLS_VERSION_1_2) + conn->rl.tls_version = TLS_VERSION_1_2; +#endif /* CONFIG_TLSV12 */ + else if (conn->client_version > TLS_VERSION_1_1) + conn->rl.tls_version = TLS_VERSION_1_1; + else + conn->rl.tls_version = conn->client_version; + wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s", + tls_version_str(conn->rl.tls_version)); + + /* Random random */ + if (end - pos < TLS_RANDOM_LEN) + goto decode_error; + + os_memcpy(conn->client_random, pos, TLS_RANDOM_LEN); + pos += TLS_RANDOM_LEN; + wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random", + conn->client_random, TLS_RANDOM_LEN); + + /* SessionID session_id */ + if (end - pos < 1) + goto decode_error; + if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN) + goto decode_error; + wpa_hexdump(MSG_MSGDUMP, "TLSv1: client session_id", pos + 1, *pos); + pos += 1 + *pos; + /* TODO: add support for session resumption */ + + /* CipherSuite cipher_suites<2..2^16-1> */ + if (end - pos < 2) + goto decode_error; + num_suites = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < num_suites) + goto decode_error; + wpa_hexdump(MSG_MSGDUMP, "TLSv1: client cipher suites", + pos, num_suites); + if (num_suites & 1) + goto decode_error; + num_suites /= 2; + + cipher_suite = 0; + for (i = 0; !cipher_suite && i < conn->num_cipher_suites; i++) { + c = pos; + for (j = 0; j < num_suites; j++) { + u16 tmp = WPA_GET_BE16(c); + c += 2; + if (!cipher_suite && tmp == conn->cipher_suites[i]) { + cipher_suite = tmp; + break; + } + } + } + pos += num_suites * 2; + if (!cipher_suite) { + wpa_printf(MSG_INFO, "TLSv1: No supported cipher suite " + "available"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_ILLEGAL_PARAMETER); + return -1; + } + + if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for " + "record layer"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + conn->cipher_suite = cipher_suite; + + /* CompressionMethod compression_methods<1..2^8-1> */ + if (end - pos < 1) + goto decode_error; + num_suites = *pos++; + if (end - pos < num_suites) + goto decode_error; + wpa_hexdump(MSG_MSGDUMP, "TLSv1: client compression_methods", + pos, num_suites); + compr_null_found = 0; + for (i = 0; i < num_suites; i++) { + if (*pos++ == TLS_COMPRESSION_NULL) + compr_null_found = 1; + } + if (!compr_null_found) { + wpa_printf(MSG_INFO, "TLSv1: Client does not accept NULL " + "compression"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_ILLEGAL_PARAMETER); + return -1; + } + + if (end - pos == 1) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected extra octet in the " + "end of ClientHello: 0x%02x", *pos); + goto decode_error; + } + + if (end - pos >= 2) { + /* Extension client_hello_extension_list<0..2^16-1> */ + ext_len = WPA_GET_BE16(pos); + pos += 2; + + wpa_printf(MSG_DEBUG, "TLSv1: %u bytes of ClientHello " + "extensions", ext_len); + if (end - pos != ext_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientHello " + "extension list length %u (expected %u)", + ext_len, (unsigned int) (end - pos)); + goto decode_error; + } + + /* + * struct { + * ExtensionType extension_type (0..65535) + * opaque extension_data<0..2^16-1> + * } Extension; + */ + + while (pos < end) { + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid " + "extension_type field"); + goto decode_error; + } + + ext_type = WPA_GET_BE16(pos); + pos += 2; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid " + "extension_data length field"); + goto decode_error; + } + + ext_len = WPA_GET_BE16(pos); + pos += 2; + + if (end - pos < ext_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid " + "extension_data field"); + goto decode_error; + } + + wpa_printf(MSG_DEBUG, "TLSv1: ClientHello Extension " + "type %u", ext_type); + wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello " + "Extension data", pos, ext_len); + + if (ext_type == TLS_EXT_SESSION_TICKET) { + os_free(conn->session_ticket); + conn->session_ticket = os_malloc(ext_len); + if (conn->session_ticket) { + os_memcpy(conn->session_ticket, pos, + ext_len); + conn->session_ticket_len = ext_len; + } + } + + pos += ext_len; + } + } + + *in_len = end - in_data; + + wpa_printf(MSG_DEBUG, "TLSv1: ClientHello OK - proceed to " + "ServerHello"); + conn->state = SERVER_HELLO; + + return 0; + +decode_error: + wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ClientHello"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; +} + + +static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len, list_len, cert_len, idx; + u8 type; + struct x509_certificate *chain = NULL, *last = NULL, *cert; + int reason; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message " + "(len=%lu)", (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message " + "length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + if (type == TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) { + if (conn->verify_peer) { + wpa_printf(MSG_DEBUG, "TLSv1: Client did not include " + "Certificate"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + return tls_process_client_key_exchange(conn, ct, in_data, + in_len); + } + if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected Certificate/" + "ClientKeyExchange)", type); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received Certificate (certificate_list len %lu)", + (unsigned long) len); + + /* + * opaque ASN.1Cert<2^24-1>; + * + * struct { + * ASN.1Cert certificate_list<1..2^24-1>; + * } Certificate; + */ + + end = pos + len; + + if (end - pos < 3) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate " + "(left=%lu)", (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + list_len = WPA_GET_BE24(pos); + pos += 3; + + if ((size_t) (end - pos) != list_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list " + "length (len=%lu left=%lu)", + (unsigned long) list_len, + (unsigned long) (end - pos)); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + idx = 0; + while (pos < end) { + if (end - pos < 3) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " + "certificate_list"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + x509_certificate_chain_free(chain); + return -1; + } + + cert_len = WPA_GET_BE24(pos); + pos += 3; + + if ((size_t) (end - pos) < cert_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate " + "length (len=%lu left=%lu)", + (unsigned long) cert_len, + (unsigned long) (end - pos)); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + x509_certificate_chain_free(chain); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)", + (unsigned long) idx, (unsigned long) cert_len); + + if (idx == 0) { + crypto_public_key_free(conn->client_rsa_key); + if (tls_parse_cert(pos, cert_len, + &conn->client_rsa_key)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " + "the certificate"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + } + + cert = x509_certificate_parse(pos, cert_len); + if (cert == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " + "the certificate"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + + if (last == NULL) + chain = cert; + else + last->next = cert; + last = cert; + + idx++; + pos += cert_len; + } + + if (x509_certificate_chain_validate(conn->cred->trusted_certs, chain, + &reason, 0) < 0) { + int tls_reason; + wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " + "validation failed (reason=%d)", reason); + switch (reason) { + case X509_VALIDATE_BAD_CERTIFICATE: + tls_reason = TLS_ALERT_BAD_CERTIFICATE; + break; + case X509_VALIDATE_UNSUPPORTED_CERTIFICATE: + tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE; + break; + case X509_VALIDATE_CERTIFICATE_REVOKED: + tls_reason = TLS_ALERT_CERTIFICATE_REVOKED; + break; + case X509_VALIDATE_CERTIFICATE_EXPIRED: + tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED; + break; + case X509_VALIDATE_CERTIFICATE_UNKNOWN: + tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN; + break; + case X509_VALIDATE_UNKNOWN_CA: + tls_reason = TLS_ALERT_UNKNOWN_CA; + break; + default: + tls_reason = TLS_ALERT_BAD_CERTIFICATE; + break; + } + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason); + x509_certificate_chain_free(chain); + return -1; + } + + x509_certificate_chain_free(chain); + + *in_len = end - in_data; + + conn->state = CLIENT_KEY_EXCHANGE; + + return 0; +} + + +static int tls_process_client_key_exchange_rsa( + struct tlsv1_server *conn, const u8 *pos, const u8 *end) +{ + u8 *out; + size_t outlen, outbuflen; + u16 encr_len; + int res; + int use_random = 0; + + if (end - pos < 2) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + encr_len = WPA_GET_BE16(pos); + pos += 2; + if (pos + encr_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientKeyExchange " + "format: encr_len=%u left=%u", + encr_len, (unsigned int) (end - pos)); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + outbuflen = outlen = end - pos; + out = os_malloc(outlen >= TLS_PRE_MASTER_SECRET_LEN ? + outlen : TLS_PRE_MASTER_SECRET_LEN); + if (out == NULL) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + /* + * struct { + * ProtocolVersion client_version; + * opaque random[46]; + * } PreMasterSecret; + * + * struct { + * public-key-encrypted PreMasterSecret pre_master_secret; + * } EncryptedPreMasterSecret; + */ + + /* + * Note: To avoid Bleichenbacher attack, we do not report decryption or + * parsing errors from EncryptedPreMasterSecret processing to the + * client. Instead, a random pre-master secret is used to force the + * handshake to fail. + */ + + if (crypto_private_key_decrypt_pkcs1_v15(conn->cred->key, + pos, encr_len, + out, &outlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt " + "PreMasterSecret (encr_len=%u outlen=%lu)", + encr_len, (unsigned long) outlen); + use_random = 1; + } + + if (!use_random && outlen != TLS_PRE_MASTER_SECRET_LEN) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected PreMasterSecret " + "length %lu", (unsigned long) outlen); + use_random = 1; + } + + if (!use_random && WPA_GET_BE16(out) != conn->client_version) { + wpa_printf(MSG_DEBUG, "TLSv1: Client version in " + "ClientKeyExchange does not match with version in " + "ClientHello"); + use_random = 1; + } + + if (use_random) { + wpa_printf(MSG_DEBUG, "TLSv1: Using random premaster secret " + "to avoid revealing information about private key"); + outlen = TLS_PRE_MASTER_SECRET_LEN; + if (os_get_random(out, outlen)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random " + "data"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(out); + return -1; + } + } + + res = tlsv1_server_derive_keys(conn, out, outlen); + + /* Clear the pre-master secret since it is not needed anymore */ + os_memset(out, 0, outbuflen); + os_free(out); + + if (res) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + return 0; +} + + +static int tls_process_client_key_exchange_dh_anon( + struct tlsv1_server *conn, const u8 *pos, const u8 *end) +{ + const u8 *dh_yc; + u16 dh_yc_len; + u8 *shared; + size_t shared_len; + int res; + + /* + * struct { + * select (PublicValueEncoding) { + * case implicit: struct { }; + * case explicit: opaque dh_Yc<1..2^16-1>; + * } dh_public; + * } ClientDiffieHellmanPublic; + */ + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientDiffieHellmanPublic", + pos, end - pos); + + if (end == pos) { + wpa_printf(MSG_DEBUG, "TLSv1: Implicit public value encoding " + "not supported"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + if (end - pos < 3) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid client public value " + "length"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + dh_yc_len = WPA_GET_BE16(pos); + dh_yc = pos + 2; + + if (dh_yc + dh_yc_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Client public value overflow " + "(length %d)", dh_yc_len); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)", + dh_yc, dh_yc_len); + + if (conn->cred == NULL || conn->cred->dh_p == NULL || + conn->dh_secret == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + shared_len = conn->cred->dh_p_len; + shared = os_malloc(shared_len); + if (shared == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for " + "DH"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + /* shared = Yc^secret mod p */ + if (wpa2_crypto_funcs.crypto_mod_exp) { + if (wpa2_crypto_funcs.crypto_mod_exp(dh_yc, dh_yc_len, conn->dh_secret, + conn->dh_secret_len, + conn->cred->dh_p, conn->cred->dh_p_len, + shared, &shared_len)) { + os_free(shared); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + } else { + wpa_printf(MSG_ERROR, "Fail to register crypto_mod_exp function!\r\n"); + os_free(shared); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange", + shared, shared_len); + + os_memset(conn->dh_secret, 0, conn->dh_secret_len); + os_free(conn->dh_secret); + conn->dh_secret = NULL; + + res = tlsv1_server_derive_keys(conn, shared, shared_len); + + /* Clear the pre-master secret since it is not needed anymore */ + os_memset(shared, 0, shared_len); + os_free(shared); + + if (res) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + return 0; +} + + +static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len; + u8 type; + tls_key_exchange keyx; + const struct tls_cipher_suite *suite; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short ClientKeyExchange " + "(Left=%lu)", (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ClientKeyExchange " + "length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + end = pos + len; + + if (type != TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected ClientKeyExchange)", type); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received ClientKeyExchange"); + + wpa_hexdump(MSG_DEBUG, "TLSv1: ClientKeyExchange", pos, len); + + suite = tls_get_cipher_suite(conn->rl.cipher_suite); + if (suite == NULL) + keyx = TLS_KEY_X_NULL; + else + keyx = suite->key_exchange; + + if (keyx == TLS_KEY_X_DH_anon && + tls_process_client_key_exchange_dh_anon(conn, pos, end) < 0) + return -1; + + if (keyx != TLS_KEY_X_DH_anon && + tls_process_client_key_exchange_rsa(conn, pos, end) < 0) + return -1; + + *in_len = end - in_data; + + conn->state = CERTIFICATE_VERIFY; + + return 0; +} + + +static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len; + u8 type; + size_t hlen, buflen; + u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos, *buf; + enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA; + u16 slen; + + if (ct == TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) { + if (conn->verify_peer) { + wpa_printf(MSG_DEBUG, "TLSv1: Client did not include " + "CertificateVerify"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + return tls_process_change_cipher_spec(conn, ct, in_data, + in_len); + } + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateVerify " + "message (len=%lu)", (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected CertificateVerify " + "message length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + end = pos + len; + + if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected CertificateVerify)", type); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateVerify"); + + /* + * struct { + * Signature signature; + * } CertificateVerify; + */ + + hpos = hash; + +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version == TLS_VERSION_1_2) { + /* + * RFC 5246, 4.7: + * TLS v1.2 adds explicit indication of the used signature and + * hash algorithms. + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + */ + if (end - pos < 2) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + if (pos[0] != TLS_HASH_ALG_SHA256 || + pos[1] != TLS_SIGN_ALG_RSA) { + wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/" + "signature(%u) algorithm", + pos[0], pos[1]); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos += 2; + + hlen = SHA256_MAC_LEN; + if (wpa2_crypto_funcs.crypto_hash_finish) { + if (conn->verify.sha256_cert == NULL || + wpa2_crypto_funcs.crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) < + 0) { + conn->verify.sha256_cert = NULL; + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + } else { + conn->verify.sha256_cert = NULL; + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__); + return -1; + } + conn->verify.sha256_cert = NULL; + } else { +#endif /* CONFIG_TLSV12 */ + + if (alg == SIGN_ALG_RSA) { + hlen = MD5_MAC_LEN; + if (conn->verify.md5_cert == NULL || + crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) + { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.md5_cert = NULL; + crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL); + conn->verify.sha1_cert = NULL; + return -1; + } + hpos += MD5_MAC_LEN; + } else + crypto_hash_finish(conn->verify.md5_cert, NULL, NULL); + + conn->verify.md5_cert = NULL; + hlen = SHA1_MAC_LEN; + if (conn->verify.sha1_cert == NULL || + crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) { + conn->verify.sha1_cert = NULL; + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha1_cert = NULL; + + if (alg == SIGN_ALG_RSA) + hlen += MD5_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen); + + if (end - pos < 2) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + slen = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < slen) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Signature", pos, end - pos); + if (conn->client_rsa_key == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: No client public key to verify " + "signature"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + buflen = end - pos; + buf = os_malloc(end - pos); + if (crypto_public_key_decrypt_pkcs1(conn->client_rsa_key, + pos, end - pos, buf, &buflen) < 0) + { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt signature"); + os_free(buf); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECRYPT_ERROR); + return -1; + } + + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature", + buf, buflen); + +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + /* + * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5 + * + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithm, + * digest OCTET STRING + * } + * + * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11} + * + * DER encoded DigestInfo for SHA256 per RFC 3447: + * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || + * H + */ + if (buflen >= 19 + 32 && + os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01" + "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0) + { + wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = " + "SHA-256"); + os_memmove(buf, buf + 19, buflen - 19); + buflen -= 19; + } else { + wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized " + "DigestInfo"); + os_free(buf); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECRYPT_ERROR); + return -1; + } + } +#endif /* CONFIG_TLSV12 */ + + if (buflen != hlen || os_memcmp(buf, hash, buflen) != 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in " + "CertificateVerify - did not match with calculated " + "hash"); + os_free(buf); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECRYPT_ERROR); + return -1; + } + + os_free(buf); + + *in_len = end - in_data; + + conn->state = CHANGE_CIPHER_SPEC; + + return 0; +} + + +static int tls_process_change_cipher_spec(struct tlsv1_server *conn, + u8 ct, const u8 *in_data, + size_t *in_len) +{ + const u8 *pos; + size_t left; + + if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " + "received content type 0x%x", ct); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 1) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + if (*pos != TLS_CHANGE_CIPHER_SPEC) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " + "received data 0x%x", *pos); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec"); + if (tlsv1_record_change_read_cipher(&conn->rl) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher " + "for record layer"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + *in_len = pos + 1 - in_data; + + conn->state = CLIENT_FINISHED; + + return 0; +} + + +static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len, hlen; + u8 verify_data[TLS_VERIFY_DATA_LEN]; + u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; " + "received content type 0x%x", ct); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for " + "Finished", + (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received " + "type 0x%x", pos[0]); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + len = WPA_GET_BE24(pos + 1); + + pos += 4; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished " + "(len=%lu > left=%lu)", + (unsigned long) len, (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + end = pos + len; + if (len != TLS_VERIFY_DATA_LEN) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length " + "in Finished: %lu (expected %d)", + (unsigned long) len, TLS_VERIFY_DATA_LEN); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished", + pos, TLS_VERIFY_DATA_LEN); + +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + hlen = SHA256_MAC_LEN; + if (wpa2_crypto_funcs.crypto_hash_finish) { + if (conn->verify.sha256_client == NULL || + crypto_hash_finish(conn->verify.sha256_client, hash, &hlen) + < 0) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.sha256_client = NULL; + return -1; + } + } else { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.sha256_client = NULL; + wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__); + return -1; + } + conn->verify.sha256_client = NULL; + } else { +#endif /* CONFIG_TLSV12 */ + + hlen = MD5_MAC_LEN; + if (conn->verify.md5_client == NULL || + crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.md5_client = NULL; + crypto_hash_finish(conn->verify.sha1_client, NULL, NULL); + conn->verify.sha1_client = NULL; + return -1; + } + conn->verify.md5_client = NULL; + hlen = SHA1_MAC_LEN; + if (conn->verify.sha1_client == NULL || + crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN, + &hlen) < 0) { + conn->verify.sha1_client = NULL; + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha1_client = NULL; + hlen = MD5_MAC_LEN + SHA1_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ + + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + "client finished", hash, hlen, + verify_data, TLS_VERIFY_DATA_LEN)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECRYPT_ERROR); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)", + verify_data, TLS_VERIFY_DATA_LEN); + + if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { + wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data"); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received Finished"); + + *in_len = end - in_data; + + if (conn->use_session_ticket) { + /* Abbreviated handshake using session ticket; RFC 4507 */ + wpa_printf(MSG_DEBUG, "TLSv1: Abbreviated handshake completed " + "successfully"); + conn->state = ESTABLISHED; + } else { + /* Full handshake */ + conn->state = SERVER_CHANGE_CIPHER_SPEC; + } + + return 0; +} + + +int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct, + const u8 *buf, size_t *len) +{ + if (ct == TLS_CONTENT_TYPE_ALERT) { + if (*len < 2) { + wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", + buf[0], buf[1]); + *len = 2; + conn->state = FAILED; + return -1; + } + + switch (conn->state) { + case CLIENT_HELLO: + if (tls_process_client_hello(conn, ct, buf, len)) + return -1; + break; + case CLIENT_CERTIFICATE: + if (tls_process_certificate(conn, ct, buf, len)) + return -1; + break; + case CLIENT_KEY_EXCHANGE: + if (tls_process_client_key_exchange(conn, ct, buf, len)) + return -1; + break; + case CERTIFICATE_VERIFY: + if (tls_process_certificate_verify(conn, ct, buf, len)) + return -1; + break; + case CHANGE_CIPHER_SPEC: + if (tls_process_change_cipher_spec(conn, ct, buf, len)) + return -1; + break; + case CLIENT_FINISHED: + if (tls_process_client_finished(conn, ct, buf, len)) + return -1; + break; + default: + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d " + "while processing received message", + conn->state); + return -1; + } + + if (ct == TLS_CONTENT_TYPE_HANDSHAKE) + tls_verify_hash_add(&conn->verify, buf, *len); + + return 0; +} diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_server_write.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_server_write.c new file mode 100644 index 0000000000..55eff1af6f --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/tlsv1_server_write.c @@ -0,0 +1,819 @@ +/* + * 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" + +#include "wpa2/eap_peer/eap_i.h" + +static size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn) +{ + size_t len = 0; + struct x509_certificate *cert; + + cert = conn->cred->cert; + while (cert) { + len += 3 + cert->cert_len; + if (x509_certificate_self_signed(cert)) + break; + cert = x509_certificate_get_subject(conn->cred->trusted_certs, + &cert->issuer); + } + + return len; +} + + +static int tls_write_server_hello(struct tlsv1_server *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr, *hs_start, *hs_length; + struct os_time now; + size_t rlen; + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHello"); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + os_get_time(&now); + WPA_PUT_BE32(conn->server_random, now.sec); + if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) { + wpa_printf(MSG_ERROR, "TLSv1: Could not generate " + "server_random"); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random", + conn->server_random, TLS_RANDOM_LEN); + + conn->session_id_len = TLS_SESSION_ID_MAX_LEN; + if (random_get_bytes(conn->session_id, conn->session_id_len)) { + wpa_printf(MSG_ERROR, "TLSv1: Could not generate " + "session_id"); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id", + conn->session_id, conn->session_id_len); + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + /* body - ServerHello */ + /* ProtocolVersion server_version */ + WPA_PUT_BE16(pos, conn->rl.tls_version); + pos += 2; + /* Random random: uint32 gmt_unix_time, opaque random_bytes */ + os_memcpy(pos, conn->server_random, TLS_RANDOM_LEN); + pos += TLS_RANDOM_LEN; + /* SessionID session_id */ + *pos++ = conn->session_id_len; + os_memcpy(pos, conn->session_id, conn->session_id_len); + pos += conn->session_id_len; + /* CipherSuite cipher_suite */ + WPA_PUT_BE16(pos, conn->cipher_suite); + pos += 2; + /* CompressionMethod compression_method */ + *pos++ = TLS_COMPRESSION_NULL; + + if (conn->session_ticket && conn->session_ticket_cb) { + int res = conn->session_ticket_cb( + conn->session_ticket_cb_ctx, + conn->session_ticket, conn->session_ticket_len, + conn->client_random, conn->server_random, + conn->master_secret); + if (res < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback " + "indicated failure"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_HANDSHAKE_FAILURE); + return -1; + } + conn->use_session_ticket = res; + + if (conn->use_session_ticket) { + if (tlsv1_server_derive_keys(conn, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to " + "derive keys"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + } + + /* + * RFC 4507 specifies that server would include an empty + * SessionTicket extension in ServerHello and a + * NewSessionTicket message after the ServerHello. However, + * EAP-FAST (RFC 4851), i.e., the only user of SessionTicket + * extension at the moment, does not use such extensions. + * + * TODO: Add support for configuring RFC 4507 behavior and make + * EAP-FAST disable it. + */ + } + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos = rhdr + rlen; + + *msgpos = pos; + + return 0; +} + + +static int tls_write_server_certificate(struct tlsv1_server *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start; + size_t rlen; + struct x509_certificate *cert; + const struct tls_cipher_suite *suite; + + suite = tls_get_cipher_suite(conn->rl.cipher_suite); + if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) { + wpa_printf(MSG_DEBUG, "TLSv1: Do not send Certificate when " + "using anonymous DH"); + return 0; + } + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate"); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + /* body - Certificate */ + /* uint24 length (to be filled) */ + cert_start = pos; + pos += 3; + cert = conn->cred->cert; + while (cert) { + if (pos + 3 + cert->cert_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space " + "for Certificate (cert_len=%lu left=%lu)", + (unsigned long) cert->cert_len, + (unsigned long) (end - pos)); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + WPA_PUT_BE24(pos, cert->cert_len); + pos += 3; + os_memcpy(pos, cert->cert_start, cert->cert_len); + pos += cert->cert_len; + + if (x509_certificate_self_signed(cert)) + break; + cert = x509_certificate_get_subject(conn->cred->trusted_certs, + &cert->issuer); + } + if (cert == conn->cred->cert || cert == NULL) { + /* + * Server was not configured with all the needed certificates + * to form a full certificate chain. The client may fail to + * validate the chain unless it is configured with all the + * missing CA certificates. + */ + wpa_printf(MSG_DEBUG, "TLSv1: Full server certificate chain " + "not configured - validation may fail"); + } + WPA_PUT_BE24(cert_start, pos - cert_start - 3); + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos = rhdr + rlen; + + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + +static int tls_write_server_key_exchange(struct tlsv1_server *conn, + u8 **msgpos, u8 *end) +{ + tls_key_exchange keyx; + const struct tls_cipher_suite *suite; + u8 *pos, *rhdr, *hs_start, *hs_length; + size_t rlen; + u8 *dh_ys; + size_t dh_ys_len; + + suite = tls_get_cipher_suite(conn->rl.cipher_suite); + if (suite == NULL) + keyx = TLS_KEY_X_NULL; + else + keyx = suite->key_exchange; + + if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) { + wpa_printf(MSG_DEBUG, "TLSv1: No ServerKeyExchange needed"); + return 0; + } + + if (keyx != TLS_KEY_X_DH_anon) { + /* TODO? */ + wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not yet " + "supported with key exchange type %d", keyx); + return -1; + } + + if (conn->cred == NULL || conn->cred->dh_p == NULL || + conn->cred->dh_g == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available for " + "ServerKeyExhcange"); + return -1; + } + + os_free(conn->dh_secret); + conn->dh_secret_len = conn->cred->dh_p_len; + conn->dh_secret = os_malloc(conn->dh_secret_len); + if (conn->dh_secret == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " + "memory for secret (Diffie-Hellman)"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + if (random_get_bytes(conn->dh_secret, conn->dh_secret_len)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random " + "data for Diffie-Hellman"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(conn->dh_secret); + conn->dh_secret = NULL; + return -1; + } + + if (os_memcmp(conn->dh_secret, conn->cred->dh_p, conn->dh_secret_len) > + 0) + conn->dh_secret[0] = 0; /* make sure secret < p */ + + pos = conn->dh_secret; + while (pos + 1 < conn->dh_secret + conn->dh_secret_len && *pos == 0) + pos++; + if (pos != conn->dh_secret) { + os_memmove(conn->dh_secret, pos, + conn->dh_secret_len - (pos - conn->dh_secret)); + conn->dh_secret_len -= pos - conn->dh_secret; + } + wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH server's secret value", + conn->dh_secret, conn->dh_secret_len); + + /* Ys = g^secret mod p */ + dh_ys_len = conn->cred->dh_p_len; + dh_ys = os_malloc(dh_ys_len); + if (dh_ys == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate memory for " + "Diffie-Hellman"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + if(wpa2_crypto_funcs.crypto_mod_exp) { + if (wpa2_crypto_funcs.crypto_mod_exp(conn->cred->dh_g, conn->cred->dh_g_len, + conn->dh_secret, conn->dh_secret_len, + conn->cred->dh_p, conn->cred->dh_p_len, + dh_ys, &dh_ys_len)) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(dh_ys); + return -1; + } + } else { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(dh_ys); + wpa_printf(MSG_ERROR, "Fail to register crypto_mod_exp function!\r\n"); + return -1; + } + + + wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)", + dh_ys, dh_ys_len); + + /* + * struct { + * select (KeyExchangeAlgorithm) { + * case diffie_hellman: + * ServerDHParams params; + * Signature signed_params; + * case rsa: + * ServerRSAParams params; + * Signature signed_params; + * }; + * } ServerKeyExchange; + * + * struct { + * opaque dh_p<1..2^16-1>; + * opaque dh_g<1..2^16-1>; + * opaque dh_Ys<1..2^16-1>; + * } ServerDHParams; + */ + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send ServerKeyExchange"); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + + /* body - ServerDHParams */ + /* dh_p */ + if (pos + 2 + conn->cred->dh_p_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " + "dh_p"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(dh_ys); + return -1; + } + WPA_PUT_BE16(pos, conn->cred->dh_p_len); + pos += 2; + os_memcpy(pos, conn->cred->dh_p, conn->cred->dh_p_len); + pos += conn->cred->dh_p_len; + + /* dh_g */ + if (pos + 2 + conn->cred->dh_g_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " + "dh_g"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(dh_ys); + return -1; + } + WPA_PUT_BE16(pos, conn->cred->dh_g_len); + pos += 2; + os_memcpy(pos, conn->cred->dh_g, conn->cred->dh_g_len); + pos += conn->cred->dh_g_len; + + /* dh_Ys */ + if (pos + 2 + dh_ys_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " + "dh_Ys"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(dh_ys); + return -1; + } + WPA_PUT_BE16(pos, dh_ys_len); + pos += 2; + os_memcpy(pos, dh_ys, dh_ys_len); + pos += dh_ys_len; + os_free(dh_ys); + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos = rhdr + rlen; + + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + +static int tls_write_server_certificate_request(struct tlsv1_server *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr, *hs_start, *hs_length; + size_t rlen; + + if (!conn->verify_peer) { + wpa_printf(MSG_DEBUG, "TLSv1: No CertificateRequest needed"); + return 0; + } + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateRequest"); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + /* body - CertificateRequest */ + + /* + * enum { + * rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4), + * (255) + * } ClientCertificateType; + * ClientCertificateType certificate_types<1..2^8-1> + */ + *pos++ = 1; + *pos++ = 1; /* rsa_sign */ + + /* + * opaque DistinguishedName<1..2^16-1> + * DistinguishedName certificate_authorities<3..2^16-1> + */ + /* TODO: add support for listing DNs for trusted CAs */ + WPA_PUT_BE16(pos, 0); + pos += 2; + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos = rhdr + rlen; + + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + +static int tls_write_server_hello_done(struct tlsv1_server *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos; + size_t rlen; + u8 payload[4]; + + wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone"); + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + pos = payload; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE; + /* uint24 length */ + WPA_PUT_BE24(pos, 0); + pos += 3; + /* body - ServerHelloDone (empty) */ + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + *msgpos, end - *msgpos, payload, pos - payload, + &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + tls_verify_hash_add(&conn->verify, payload, pos - payload); + + *msgpos += rlen; + + return 0; +} + + +static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn, + u8 **msgpos, u8 *end) +{ + size_t rlen; + u8 payload[1]; + + wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec"); + + payload[0] = TLS_CHANGE_CIPHER_SPEC; + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC, + *msgpos, end - *msgpos, payload, sizeof(payload), + &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + if (tlsv1_record_change_write_cipher(&conn->rl) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for " + "record layer"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + *msgpos += rlen; + + return 0; +} + + +static int tls_write_server_finished(struct tlsv1_server *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *hs_start; + size_t rlen, hlen; + u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN]; + u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send Finished"); + + /* Encrypted Handshake Message: Finished */ + +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + hlen = SHA256_MAC_LEN; + if (wpa2_crypto_funcs.crypto_hash_finish) { + if (conn->verify.sha256_server == NULL || + wpa2_crypto_funcs.crypto_hash_finish(conn->verify.sha256_server, hash, &hlen) + < 0) { + conn->verify.sha256_server = NULL; + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + } else { + conn->verify.sha256_server = NULL; + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__); + return -1; + } + conn->verify.sha256_server = NULL; + } else { +#endif /* CONFIG_TLSV12 */ + + hlen = MD5_MAC_LEN; + if (conn->verify.md5_server == NULL || + crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.md5_server = NULL; + crypto_hash_finish(conn->verify.sha1_server, NULL, NULL); + conn->verify.sha1_server = NULL; + return -1; + } + conn->verify.md5_server = NULL; + hlen = SHA1_MAC_LEN; + if (conn->verify.sha1_server == NULL || + crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN, + &hlen) < 0) { + conn->verify.sha1_server = NULL; + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha1_server = NULL; + hlen = MD5_MAC_LEN + SHA1_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ + + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + "server finished", hash, hlen, + verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)", + verify_data + 1 + 3, TLS_VERIFY_DATA_LEN); + + /* Handshake */ + pos = hs_start = verify_data; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_FINISHED; + /* uint24 length */ + WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN); + pos += 3; + pos += TLS_VERIFY_DATA_LEN; + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + *msgpos, end - *msgpos, hs_start, pos - hs_start, + &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + *msgpos += rlen; + + return 0; +} + + +static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len) +{ + u8 *msg, *end, *pos; + size_t msglen; + + *out_len = 0; + + msglen = 1000 + tls_server_cert_chain_der_len(conn); + + msg = os_malloc(msglen); + if (msg == NULL) + return NULL; + + pos = msg; + end = msg + msglen; + + if (tls_write_server_hello(conn, &pos, end) < 0) { + os_free(msg); + return NULL; + } + + if (conn->use_session_ticket) { + /* Abbreviated handshake using session ticket; RFC 4507 */ + if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 || + tls_write_server_finished(conn, &pos, end) < 0) { + os_free(msg); + return NULL; + } + + *out_len = pos - msg; + + conn->state = CHANGE_CIPHER_SPEC; + + return msg; + } + + /* Full handshake */ + if (tls_write_server_certificate(conn, &pos, end) < 0 || + tls_write_server_key_exchange(conn, &pos, end) < 0 || + tls_write_server_certificate_request(conn, &pos, end) < 0 || + tls_write_server_hello_done(conn, &pos, end) < 0) { + os_free(msg); + return NULL; + } + + *out_len = pos - msg; + + conn->state = CLIENT_CERTIFICATE; + + return msg; +} + + +static u8 * tls_send_change_cipher_spec(struct tlsv1_server *conn, + size_t *out_len) +{ + u8 *msg, *end, *pos; + + *out_len = 0; + + msg = os_malloc(1000); + if (msg == NULL) + return NULL; + + pos = msg; + end = msg + 1000; + + if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 || + tls_write_server_finished(conn, &pos, end) < 0) { + os_free(msg); + return NULL; + } + + *out_len = pos - msg; + + wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed successfully"); + conn->state = ESTABLISHED; + + return msg; +} + + +u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len) +{ + switch (conn->state) { + case SERVER_HELLO: + return tls_send_server_hello(conn, out_len); + case SERVER_CHANGE_CIPHER_SPEC: + return tls_send_change_cipher_spec(conn, out_len); + default: + if (conn->state == ESTABLISHED && conn->use_session_ticket) { + /* Abbreviated handshake was already completed. */ + return NULL; + } + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while " + "generating reply", conn->state); + return NULL; + } +} + + +u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level, + u8 description, size_t *out_len) +{ + u8 *alert, *pos, *length; + + wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description); + *out_len = 0; + + alert = os_malloc(10); + if (alert == NULL) + return NULL; + + pos = alert; + + /* TLSPlaintext */ + /* ContentType type */ + *pos++ = TLS_CONTENT_TYPE_ALERT; + /* ProtocolVersion version */ + WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version : + TLS_VERSION); + pos += 2; + /* uint16 length (to be filled) */ + length = pos; + pos += 2; + /* opaque fragment[TLSPlaintext.length] */ + + /* Alert */ + /* AlertLevel level */ + *pos++ = level; + /* AlertDescription description */ + *pos++ = description; + + WPA_PUT_BE16(length, pos - length - 2); + *out_len = pos - alert; + + return alert; +} diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/x509v3.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/x509v3.c new file mode 100644 index 0000000000..462ecfafa9 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/tls/x509v3.c @@ -0,0 +1,2013 @@ +/* + * 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/wpa.h" +#include "wpa/common.h" +#include "crypto/crypto.h" +#include "wpa2/tls/asn1.h" +#include "wpa2/tls/x509v3.h" + +#include "wpa2/eap_peer/eap_i.h" + +static void x509_free_name(struct x509_name *name) +{ + size_t i; + + for (i = 0; i < name->num_attr; i++) { + os_free(name->attr[i].value); + name->attr[i].value = NULL; + name->attr[i].type = X509_NAME_ATTR_NOT_USED; + } + name->num_attr = 0; + os_free(name->email); + name->email = NULL; + + os_free(name->alt_email); + os_free(name->dns); + os_free(name->uri); + os_free(name->ip); + name->alt_email = name->dns = name->uri = NULL; + name->ip = NULL; + name->ip_len = 0; + os_memset(&name->rid, 0, sizeof(name->rid)); +} + + +/** + * x509_certificate_free - Free an X.509 certificate + * @cert: Certificate to be freed + */ +void x509_certificate_free(struct x509_certificate *cert) +{ + if (cert == NULL) + return; + if (cert->next) { + wpa_printf(MSG_DEBUG, "X509: x509_certificate_free: cer=%p " + "was still on a list (next=%p)\n", + cert, cert->next); + } + x509_free_name(&cert->issuer); + x509_free_name(&cert->subject); + os_free(cert->public_key); + os_free(cert->sign_value); + os_free(cert); +} + + +/** + * x509_certificate_free - Free an X.509 certificate chain + * @cert: Pointer to the first certificate in the chain + */ +void x509_certificate_chain_free(struct x509_certificate *cert) +{ + struct x509_certificate *next; + + while (cert) { + next = cert->next; + cert->next = NULL; + x509_certificate_free(cert); + cert = next; + } +} + + +static int x509_whitespace(char c) +{ + return c == ' ' || c == '\t'; +} + + +static void x509_str_strip_whitespace(char *a) +{ + char *ipos, *opos; + int remove_whitespace = 1; + + ipos = opos = a; + + while (*ipos) { + if (remove_whitespace && x509_whitespace(*ipos)) + ipos++; + else { + remove_whitespace = x509_whitespace(*ipos); + *opos++ = *ipos++; + } + } + + *opos-- = '\0'; + if (opos > a && x509_whitespace(*opos)) + *opos = '\0'; +} + + +static int x509_str_compare(const char *a, const char *b) +{ + char *aa, *bb; + int ret; + + if (!a && b) + return -1; + if (a && !b) + return 1; + if (!a && !b) + return 0; + + aa = os_strdup(a); + bb = os_strdup(b); + + if (aa == NULL || bb == NULL) { + os_free(aa); + os_free(bb); + return strcasecmp(a, b); + } + + x509_str_strip_whitespace(aa); + x509_str_strip_whitespace(bb); + + ret = strcasecmp(aa, bb); + + os_free(aa); + os_free(bb); + + return ret; +} + + +/** + * x509_name_compare - Compare X.509 certificate names + * @a: Certificate name + * @b: Certificate name + * Returns: <0, 0, or >0 based on whether a is less than, equal to, or + * greater than b + */ +int x509_name_compare(struct x509_name *a, struct x509_name *b) +{ + int res; + size_t i; + + if (!a && b) + return -1; + if (a && !b) + return 1; + if (!a && !b) + return 0; + if (a->num_attr < b->num_attr) + return -1; + if (a->num_attr > b->num_attr) + return 1; + + for (i = 0; i < a->num_attr; i++) { + if (a->attr[i].type < b->attr[i].type) + return -1; + if (a->attr[i].type > b->attr[i].type) + return -1; + res = x509_str_compare(a->attr[i].value, b->attr[i].value); + if (res) + return res; + } + res = x509_str_compare(a->email, b->email); + if (res) + return res; + + return 0; +} + + +static int x509_parse_algorithm_identifier( + const u8 *buf, size_t len, + struct x509_algorithm_identifier *id, const u8 **next) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + + /* + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL + * } + */ + + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(AlgorithmIdentifier) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + end = pos + hdr.length; + + if (end > buf + len) + return -1; + + *next = end; + + if (asn1_get_oid(pos, end - pos, &id->oid, &pos)) + return -1; + + /* TODO: optional parameters */ + + return 0; +} + + +static int x509_parse_public_key(const u8 *buf, size_t len, + struct x509_certificate *cert, + const u8 **next) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + + /* + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING + * } + */ + + pos = buf; + end = buf + len; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(SubjectPublicKeyInfo) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + + if (pos + hdr.length > end) + return -1; + end = pos + hdr.length; + *next = end; + + if (x509_parse_algorithm_identifier(pos, end - pos, + &cert->public_key_alg, &pos)) + return -1; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_BITSTRING) { + wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING " + "(subjectPublicKey) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + if (hdr.length < 1) + return -1; + pos = hdr.payload; + if (*pos) { + wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits", + *pos); + /* + * TODO: should this be rejected? X.509 certificates are + * unlikely to use such a construction. Now we would end up + * including the extra bits in the buffer which may also be + * ok. + */ + } + os_free(cert->public_key); + cert->public_key = os_malloc(hdr.length - 1); + if (cert->public_key == NULL) { + wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for " + "public key"); + return -1; + } + os_memcpy(cert->public_key, pos + 1, hdr.length - 1); + cert->public_key_len = hdr.length - 1; + wpa_hexdump(MSG_MSGDUMP, "X509: subjectPublicKey", + cert->public_key, cert->public_key_len); + + return 0; +} + + +static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, + const u8 **next) +{ + struct asn1_hdr hdr; + const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end; + struct asn1_oid oid; + char *val; + + /* + * Name ::= CHOICE { RDNSequence } + * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue + * AttributeTypeAndValue ::= SEQUENCE { + * type AttributeType, + * value AttributeValue + * } + * AttributeType ::= OBJECT IDENTIFIER + * AttributeValue ::= ANY DEFINED BY AttributeType + */ + + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(Name / RDNSequencer) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + + if (pos + hdr.length > buf + len) + return -1; + + end = *next = pos + hdr.length; + + while (pos < end) { + enum x509_name_attr_type type; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SET) { + wpa_printf(MSG_DEBUG, "X509: Expected SET " + "(RelativeDistinguishedName) - found class " + "%d tag 0x%x", hdr.class, hdr.tag); + x509_free_name(name); + return -1; + } + + set_pos = hdr.payload; + pos = set_end = hdr.payload + hdr.length; + + if (asn1_get_next(set_pos, set_end - set_pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(AttributeTypeAndValue) - found class %d " + "tag 0x%x", hdr.class, hdr.tag); + x509_free_name(name); + return -1; + } + + seq_pos = hdr.payload; + seq_end = hdr.payload + hdr.length; + + if (asn1_get_oid(seq_pos, seq_end - seq_pos, &oid, &seq_pos)) { + x509_free_name(name); + return -1; + } + + if (asn1_get_next(seq_pos, seq_end - seq_pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL) { + wpa_printf(MSG_DEBUG, "X509: Failed to parse " + "AttributeValue"); + x509_free_name(name); + return -1; + } + + /* RFC 3280: + * MUST: country, organization, organizational-unit, + * distinguished name qualifier, state or province name, + * common name, serial number. + * SHOULD: locality, title, surname, given name, initials, + * pseudonym, generation qualifier. + * MUST: domainComponent (RFC 2247). + */ + type = X509_NAME_ATTR_NOT_USED; + if (oid.len == 4 && + oid.oid[0] == 2 && oid.oid[1] == 5 && oid.oid[2] == 4) { + /* id-at ::= 2.5.4 */ + switch (oid.oid[3]) { + case 3: + /* commonName */ + type = X509_NAME_ATTR_CN; + break; + case 6: + /* countryName */ + type = X509_NAME_ATTR_C; + break; + case 7: + /* localityName */ + type = X509_NAME_ATTR_L; + break; + case 8: + /* stateOrProvinceName */ + type = X509_NAME_ATTR_ST; + break; + case 10: + /* organizationName */ + type = X509_NAME_ATTR_O; + break; + case 11: + /* organizationalUnitName */ + type = X509_NAME_ATTR_OU; + break; + } + } else if (oid.len == 7 && + oid.oid[0] == 1 && oid.oid[1] == 2 && + oid.oid[2] == 840 && oid.oid[3] == 113549 && + oid.oid[4] == 1 && oid.oid[5] == 9 && + oid.oid[6] == 1) { + /* 1.2.840.113549.1.9.1 - e-mailAddress */ + os_free(name->email); + name->email = os_malloc(hdr.length + 1); + if (name->email == NULL) { + x509_free_name(name); + return -1; + } + os_memcpy(name->email, hdr.payload, hdr.length); + name->email[hdr.length] = '\0'; + continue; + } else if (oid.len == 7 && + oid.oid[0] == 0 && oid.oid[1] == 9 && + oid.oid[2] == 2342 && oid.oid[3] == 19200300 && + oid.oid[4] == 100 && oid.oid[5] == 1 && + oid.oid[6] == 25) { + /* 0.9.2342.19200300.100.1.25 - domainComponent */ + type = X509_NAME_ATTR_DC; + } + + if (type == X509_NAME_ATTR_NOT_USED) { + wpa_hexdump(MSG_DEBUG, "X509: Unrecognized OID", + (u8 *) oid.oid, + oid.len * sizeof(oid.oid[0])); + wpa_hexdump_ascii(MSG_MSGDUMP, "X509: Attribute Data", + hdr.payload, hdr.length); + continue; + } + + if (name->num_attr == X509_MAX_NAME_ATTRIBUTES) { + wpa_printf(MSG_INFO, "X509: Too many Name attributes"); + x509_free_name(name); + return -1; + } + + val = (char *)dup_binstr(hdr.payload, hdr.length); + if (val == NULL) { + x509_free_name(name); + return -1; + } + if (os_strlen(val) != hdr.length) { + wpa_printf(MSG_INFO, "X509: Reject certificate with " + "embedded NUL byte in a string (%s[NUL])", + val); + os_free(val); + x509_free_name(name); + return -1; + } + + name->attr[name->num_attr].type = type; + name->attr[name->num_attr].value = val; + name->num_attr++; + } + + return 0; +} + + +static char * x509_name_attr_str(enum x509_name_attr_type type) +{ +#ifndef ESPRESSIF_USE + switch (type) { + case X509_NAME_ATTR_NOT_USED: + return "[N/A]"; + case X509_NAME_ATTR_DC: + return "DC"; + case X509_NAME_ATTR_CN: + return "CN"; + case X509_NAME_ATTR_C: + return "C"; + case X509_NAME_ATTR_L: + return "L"; + case X509_NAME_ATTR_ST: + return "ST"; + case X509_NAME_ATTR_O: + return "O"; + case X509_NAME_ATTR_OU: + return "OU"; + } + return "?"; +#else + static char name_attr[6]; + switch (type) { + case X509_NAME_ATTR_NOT_USED: + strcpy(name_attr, "[N/A]"); + case X509_NAME_ATTR_DC: + strcpy(name_attr, "DC"); + case X509_NAME_ATTR_CN: + strcpy(name_attr, "CN"); + case X509_NAME_ATTR_C: + strcpy(name_attr, "C"); + case X509_NAME_ATTR_L: + strcpy(name_attr, "L"); + case X509_NAME_ATTR_ST: + strcpy(name_attr, "ST"); + case X509_NAME_ATTR_O: + strcpy(name_attr, "O"); + case X509_NAME_ATTR_OU: + strcpy(name_attr, "OU"); + default : + strcpy(name_attr, "?"); + } + return name_attr; +#endif +} + + +/** + * x509_name_string - Convert an X.509 certificate name into a string + * @name: Name to convert + * @buf: Buffer for the string + * @len: Maximum buffer length + */ +void x509_name_string(struct x509_name *name, char *buf, size_t len) +{ + char *pos, *end; + int ret; + size_t i; + + if (len == 0) + return; + + pos = buf; + end = buf + len; + + for (i = 0; i < name->num_attr; i++) { + //ret = os_snprintf(pos, end - pos, "%s=%s, ", + ret = sprintf(pos, "%s=%s, ", + x509_name_attr_str(name->attr[i].type), + name->attr[i].value); + if (ret < 0 || ret >= end - pos) + goto done; + pos += ret; + } + + if (pos > buf + 1 && pos[-1] == ' ' && pos[-2] == ',') { + pos--; + *pos = '\0'; + pos--; + *pos = '\0'; + } + + if (name->email) { + //ret = os_snprintf(pos, end - pos, "/emailAddress=%s", + ret = sprintf(pos, "/emailAddress=%s", + name->email); + if (ret < 0 || ret >= end - pos) + goto done; + pos += ret; + } + +done: + end[-1] = '\0'; +} + + +static int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, + os_time_t *val) +{ +#if 0 + const char *pos; + int year, month, day, hour, min, sec; + + /* + * Time ::= CHOICE { + * utcTime UTCTime, + * generalTime GeneralizedTime + * } + * + * UTCTime: YYMMDDHHMMSSZ + * GeneralizedTime: YYYYMMDDHHMMSSZ + */ + + pos = (const char *) buf; + + switch (asn1_tag) { + case ASN1_TAG_UTCTIME: + if (len != 13 || buf[12] != 'Z') { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized " + "UTCTime format", buf, len); + return -1; + } + if (sscanf(pos, "%02d", &year) != 1) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse " + "UTCTime year", buf, len); + return -1; + } + if (year < 50) + year += 2000; + else + year += 1900; + pos += 2; + break; + case ASN1_TAG_GENERALIZEDTIME: + if (len != 15 || buf[14] != 'Z') { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized " + "GeneralizedTime format", buf, len); + return -1; + } + if (sscanf(pos, "%04d", &year) != 1) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse " + "GeneralizedTime year", buf, len); + return -1; + } + pos += 4; + break; + default: + wpa_printf(MSG_DEBUG, "X509: Expected UTCTime or " + "GeneralizedTime - found tag 0x%x", asn1_tag); + return -1; + } + + if (sscanf(pos, "%02d", &month) != 1) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " + "(month)", buf, len); + return -1; + } + pos += 2; + + if (sscanf(pos, "%02d", &day) != 1) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " + "(day)", buf, len); + return -1; + } + pos += 2; + + if (sscanf(pos, "%02d", &hour) != 1) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " + "(hour)", buf, len); + return -1; + } + pos += 2; + + if (sscanf(pos, "%02d", &min) != 1) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " + "(min)", buf, len); + return -1; + } + pos += 2; + + if (sscanf(pos, "%02d", &sec) != 1) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " + "(sec)", buf, len); + return -1; + } + + if (os_mktime(year, month, day, hour, min, sec, val) < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to convert Time", + buf, len); + if (year < 1970) { + /* + * At least some test certificates have been configured + * to use dates prior to 1970. Set the date to + * beginning of 1970 to handle these case. + */ + wpa_printf(MSG_DEBUG, "X509: Year=%d before epoch - " + "assume epoch as the time", year); + *val = 0; + return 0; + } + return -1; + } +#endif + return 0; +} + + +static int x509_parse_validity(const u8 *buf, size_t len, + struct x509_certificate *cert, const u8 **next) +{ + struct asn1_hdr hdr; + const u8 *pos; + size_t plen; + + /* + * Validity ::= SEQUENCE { + * notBefore Time, + * notAfter Time + * } + * + * RFC 3280, 4.1.2.5: + * CAs conforming to this profile MUST always encode certificate + * validity dates through the year 2049 as UTCTime; certificate + * validity dates in 2050 or later MUST be encoded as GeneralizedTime. + */ + + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(Validity) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + plen = hdr.length; + + if (pos + plen > buf + len) + return -1; + + *next = pos + plen; + + if (asn1_get_next(pos, plen, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + x509_parse_time(hdr.payload, hdr.length, hdr.tag, + &cert->not_before) < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notBefore " + "Time", hdr.payload, hdr.length); + return -1; + } + + pos = hdr.payload + hdr.length; + plen = *next - pos; + + if (asn1_get_next(pos, plen, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + x509_parse_time(hdr.payload, hdr.length, hdr.tag, + &cert->not_after) < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notAfter " + "Time", hdr.payload, hdr.length); + return -1; + } + + wpa_printf(MSG_DEBUG, "X509: Validity: notBefore: %lu notAfter: %lu", + (unsigned long) cert->not_before, + (unsigned long) cert->not_after); + + return 0; +} + + +static int x509_id_ce_oid(struct asn1_oid *oid) +{ + /* id-ce arc from X.509 for standard X.509v3 extensions */ + return oid->len >= 4 && + oid->oid[0] == 2 /* joint-iso-ccitt */ && + oid->oid[1] == 5 /* ds */ && + oid->oid[2] == 29 /* id-ce */; +} + + +static int x509_parse_ext_key_usage(struct x509_certificate *cert, + const u8 *pos, size_t len) +{ + struct asn1_hdr hdr; + + /* + * KeyUsage ::= BIT STRING { + * digitalSignature (0), + * nonRepudiation (1), + * keyEncipherment (2), + * dataEncipherment (3), + * keyAgreement (4), + * keyCertSign (5), + * cRLSign (6), + * encipherOnly (7), + * decipherOnly (8) } + */ + + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_BITSTRING || + hdr.length < 1) { + wpa_printf(MSG_DEBUG, "X509: Expected BIT STRING in " + "KeyUsage; found %d tag 0x%x len %d", + hdr.class, hdr.tag, hdr.length); + return -1; + } + + cert->extensions_present |= X509_EXT_KEY_USAGE; + cert->key_usage = asn1_bit_string_to_long(hdr.payload, hdr.length); + + wpa_printf(MSG_DEBUG, "X509: KeyUsage 0x%lx", cert->key_usage); + + return 0; +} + + +static int x509_parse_ext_basic_constraints(struct x509_certificate *cert, + const u8 *pos, size_t len) +{ + struct asn1_hdr hdr; + unsigned long value; + size_t left; + + /* + * BasicConstraints ::= SEQUENCE { + * cA BOOLEAN DEFAULT FALSE, + * pathLenConstraint INTEGER (0..MAX) OPTIONAL } + */ + + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " + "BasicConstraints; found %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + cert->extensions_present |= X509_EXT_BASIC_CONSTRAINTS; + + if (hdr.length == 0) + return 0; + + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL) { + wpa_printf(MSG_DEBUG, "X509: Failed to parse " + "BasicConstraints"); + return -1; + } + + if (hdr.tag == ASN1_TAG_BOOLEAN) { + if (hdr.length != 1) { + wpa_printf(MSG_DEBUG, "X509: Unexpected " + "Boolean length (%u) in BasicConstraints", + hdr.length); + return -1; + } + cert->ca = hdr.payload[0]; + + if (hdr.payload + hdr.length == pos + len) { + wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d", + cert->ca); + return 0; + } + + if (asn1_get_next(hdr.payload + hdr.length, len - hdr.length, + &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL) { + wpa_printf(MSG_DEBUG, "X509: Failed to parse " + "BasicConstraints"); + return -1; + } + } + + if (hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, "X509: Expected INTEGER in " + "BasicConstraints; found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + left = hdr.length; + value = 0; + while (left) { + value <<= 8; + value |= *pos++; + left--; + } + + cert->path_len_constraint = value; + cert->extensions_present |= X509_EXT_PATH_LEN_CONSTRAINT; + + wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d " + "pathLenConstraint=%lu", + cert->ca, cert->path_len_constraint); + + return 0; +} + + +static int x509_parse_alt_name_rfc8222(struct x509_name *name, + const u8 *pos, size_t len) +{ + /* rfc822Name IA5String */ + wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - rfc822Name", pos, len); + os_free(name->alt_email); + name->alt_email = (char *)os_zalloc(len + 1); + if (name->alt_email == NULL) + return -1; + os_memcpy(name->alt_email, pos, len); + if (os_strlen(name->alt_email) != len) { + wpa_printf(MSG_INFO, "X509: Reject certificate with " + "embedded NUL byte in rfc822Name (%s[NUL])", + name->alt_email); + os_free(name->alt_email); + name->alt_email = NULL; + return -1; + } + return 0; +} + + +static int x509_parse_alt_name_dns(struct x509_name *name, + const u8 *pos, size_t len) +{ + /* dNSName IA5String */ + wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - dNSName", pos, len); + os_free(name->dns); + name->dns = (char *)os_zalloc(len + 1); + if (name->dns == NULL) + return -1; + os_memcpy(name->dns, pos, len); + if (os_strlen(name->dns) != len) { + wpa_printf(MSG_INFO, "X509: Reject certificate with " + "embedded NUL byte in dNSName (%s[NUL])", + name->dns); + os_free(name->dns); + name->dns = NULL; + return -1; + } + return 0; +} + + +static int x509_parse_alt_name_uri(struct x509_name *name, + const u8 *pos, size_t len) +{ + /* uniformResourceIdentifier IA5String */ + wpa_hexdump_ascii(MSG_MSGDUMP, + "X509: altName - uniformResourceIdentifier", + pos, len); + os_free(name->uri); + name->uri = (char *)os_zalloc(len + 1); + if (name->uri == NULL) + return -1; + os_memcpy(name->uri, pos, len); + if (os_strlen(name->uri) != len) { + wpa_printf(MSG_INFO, "X509: Reject certificate with " + "embedded NUL byte in uniformResourceIdentifier " + "(%s[NUL])", name->uri); + os_free(name->uri); + name->uri = NULL; + return -1; + } + return 0; +} + + +static int x509_parse_alt_name_ip(struct x509_name *name, + const u8 *pos, size_t len) +{ + /* iPAddress OCTET STRING */ + wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len); + os_free(name->ip); + name->ip = os_malloc(len); + if (name->ip == NULL) + return -1; + os_memcpy(name->ip, pos, len); + name->ip_len = len; + return 0; +} + + +static int x509_parse_alt_name_rid(struct x509_name *name, + const u8 *pos, size_t len) +{ + char buf[80]; + + /* registeredID OBJECT IDENTIFIER */ + if (asn1_parse_oid(pos, len, &name->rid) < 0) + return -1; + + asn1_oid_to_str(&name->rid, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "X509: altName - registeredID: %s", buf); + + return 0; +} + + +static int x509_parse_ext_alt_name(struct x509_name *name, + const u8 *pos, size_t len) +{ + struct asn1_hdr hdr; + const u8 *p, *end; + + /* + * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + * + * GeneralName ::= CHOICE { + * otherName [0] OtherName, + * rfc822Name [1] IA5String, + * dNSName [2] IA5String, + * x400Address [3] ORAddress, + * directoryName [4] Name, + * ediPartyName [5] EDIPartyName, + * uniformResourceIdentifier [6] IA5String, + * iPAddress [7] OCTET STRING, + * registeredID [8] OBJECT IDENTIFIER } + * + * OtherName ::= SEQUENCE { + * type-id OBJECT IDENTIFIER, + * value [0] EXPLICIT ANY DEFINED BY type-id } + * + * EDIPartyName ::= SEQUENCE { + * nameAssigner [0] DirectoryString OPTIONAL, + * partyName [1] DirectoryString } + */ + + for (p = pos, end = pos + len; p < end; p = hdr.payload + hdr.length) { + int res; + + if (asn1_get_next(p, end - p, &hdr) < 0) { + wpa_printf(MSG_DEBUG, "X509: Failed to parse " + "SubjectAltName item"); + return -1; + } + + if (hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) + continue; + + switch (hdr.tag) { + case 1: + res = x509_parse_alt_name_rfc8222(name, hdr.payload, + hdr.length); + break; + case 2: + res = x509_parse_alt_name_dns(name, hdr.payload, + hdr.length); + break; + case 6: + res = x509_parse_alt_name_uri(name, hdr.payload, + hdr.length); + break; + case 7: + res = x509_parse_alt_name_ip(name, hdr.payload, + hdr.length); + break; + case 8: + res = x509_parse_alt_name_rid(name, hdr.payload, + hdr.length); + break; + case 0: /* TODO: otherName */ + case 3: /* TODO: x500Address */ + case 4: /* TODO: directoryName */ + case 5: /* TODO: ediPartyName */ + default: + res = 0; + break; + } + if (res < 0) + return res; + } + + return 0; +} + + +static int x509_parse_ext_subject_alt_name(struct x509_certificate *cert, + const u8 *pos, size_t len) +{ + struct asn1_hdr hdr; + + /* SubjectAltName ::= GeneralNames */ + + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " + "SubjectAltName; found %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + wpa_printf(MSG_DEBUG, "X509: SubjectAltName"); + cert->extensions_present |= X509_EXT_SUBJECT_ALT_NAME; + + if (hdr.length == 0) + return 0; + + return x509_parse_ext_alt_name(&cert->subject, hdr.payload, + hdr.length); +} + + +static int x509_parse_ext_issuer_alt_name(struct x509_certificate *cert, + const u8 *pos, size_t len) +{ + struct asn1_hdr hdr; + + /* IssuerAltName ::= GeneralNames */ + + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " + "IssuerAltName; found %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + wpa_printf(MSG_DEBUG, "X509: IssuerAltName"); + cert->extensions_present |= X509_EXT_ISSUER_ALT_NAME; + + if (hdr.length == 0) + return 0; + + return x509_parse_ext_alt_name(&cert->issuer, hdr.payload, + hdr.length); +} + + +static int x509_parse_extension_data(struct x509_certificate *cert, + struct asn1_oid *oid, + const u8 *pos, size_t len) +{ + if (!x509_id_ce_oid(oid)) + return 1; + + /* TODO: add other extensions required by RFC 3280, Ch 4.2: + * certificate policies (section 4.2.1.5) + * name constraints (section 4.2.1.11) + * policy constraints (section 4.2.1.12) + * extended key usage (section 4.2.1.13) + * inhibit any-policy (section 4.2.1.15) + */ + switch (oid->oid[3]) { + case 15: /* id-ce-keyUsage */ + return x509_parse_ext_key_usage(cert, pos, len); + case 17: /* id-ce-subjectAltName */ + return x509_parse_ext_subject_alt_name(cert, pos, len); + case 18: /* id-ce-issuerAltName */ + return x509_parse_ext_issuer_alt_name(cert, pos, len); + case 19: /* id-ce-basicConstraints */ + return x509_parse_ext_basic_constraints(cert, pos, len); + default: + return 1; + } +} + + +static int x509_parse_extension(struct x509_certificate *cert, + const u8 *pos, size_t len, const u8 **next) +{ + const u8 *end; + struct asn1_hdr hdr; + struct asn1_oid oid; + int critical_ext = 0, res; + char buf[80]; + + /* + * Extension ::= SEQUENCE { + * extnID OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING + * } + */ + + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in " + "Extensions: class %d tag 0x%x; expected SEQUENCE", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + *next = end = pos + hdr.length; + + if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) { + wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data for " + "Extension (expected OID)"); + return -1; + } + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + (hdr.tag != ASN1_TAG_BOOLEAN && + hdr.tag != ASN1_TAG_OCTETSTRING)) { + wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in " + "Extensions: class %d tag 0x%x; expected BOOLEAN " + "or OCTET STRING", hdr.class, hdr.tag); + return -1; + } + + if (hdr.tag == ASN1_TAG_BOOLEAN) { + if (hdr.length != 1) { + wpa_printf(MSG_DEBUG, "X509: Unexpected " + "Boolean length (%u)", hdr.length); + return -1; + } + critical_ext = hdr.payload[0]; + pos = hdr.payload; + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + (hdr.class != ASN1_CLASS_UNIVERSAL && + hdr.class != ASN1_CLASS_PRIVATE) || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header " + "in Extensions: class %d tag 0x%x; " + "expected OCTET STRING", + hdr.class, hdr.tag); + return -1; + } + } + + asn1_oid_to_str(&oid, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "X509: Extension: extnID=%s critical=%d", + buf, critical_ext); + wpa_hexdump(MSG_MSGDUMP, "X509: extnValue", hdr.payload, hdr.length); + + res = x509_parse_extension_data(cert, &oid, hdr.payload, hdr.length); + if (res < 0) + return res; + if (res == 1 && critical_ext) { + wpa_printf(MSG_INFO, "X509: Unknown critical extension %s", + buf); + //return -1; //for wpa2 certification , commenout , ignore the error + } + + return 0; +} + + +static int x509_parse_extensions(struct x509_certificate *cert, + const u8 *pos, size_t len) +{ + const u8 *end; + struct asn1_hdr hdr; + + /* Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension */ + + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data " + "for Extensions: class %d tag 0x%x; " + "expected SEQUENCE", hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + while (pos < end) { + if (x509_parse_extension(cert, pos, end - pos, &pos) + < 0) + return -1; + } + + return 0; +} + + +static int x509_parse_tbs_certificate(const u8 *buf, size_t len, + struct x509_certificate *cert, + const u8 **next) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + size_t left; + char sbuf[128]; + unsigned long value; + + /* tbsCertificate TBSCertificate ::= SEQUENCE */ + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: tbsCertificate did not start " + "with a valid SEQUENCE - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + end = *next = pos + hdr.length; + + /* + * version [0] EXPLICIT Version DEFAULT v1 + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0) + return -1; + pos = hdr.payload; + + if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC) { + if (asn1_get_next(pos, end - pos, &hdr) < 0) + return -1; + + if (hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for " + "version field - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + if (hdr.length != 1) { + wpa_printf(MSG_DEBUG, "X509: Unexpected version field " + "length %u (expected 1)", hdr.length); + return -1; + } + pos = hdr.payload; + left = hdr.length; + value = 0; + while (left) { + value <<= 8; + value |= *pos++; + left--; + } + + cert->version = value; + if (cert->version != X509_CERT_V1 && + cert->version != X509_CERT_V2 && + cert->version != X509_CERT_V3) { + wpa_printf(MSG_DEBUG, "X509: Unsupported version %d", + cert->version + 1); + return -1; + } + + if (asn1_get_next(pos, end - pos, &hdr) < 0) + return -1; + } else + cert->version = X509_CERT_V1; + wpa_printf(MSG_DEBUG, "X509: Version X.509v%d", cert->version + 1); + + /* serialNumber CertificateSerialNumber ::= INTEGER */ + if (hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for " + "serialNumber; class=%d tag=0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + left = hdr.length; + while (left) { + cert->serial_number <<= 8; + cert->serial_number |= *pos++; + left--; + } + wpa_printf(MSG_DEBUG, "X509: serialNumber %lu", cert->serial_number); + + /* signature AlgorithmIdentifier */ + if (x509_parse_algorithm_identifier(pos, end - pos, &cert->signature, + &pos)) + return -1; + + /* issuer Name */ + if (x509_parse_name(pos, end - pos, &cert->issuer, &pos)) + return -1; + x509_name_string(&cert->issuer, sbuf, sizeof(sbuf)); + wpa_printf(MSG_DEBUG, "X509: issuer %s", sbuf); + + /* validity Validity */ + if (x509_parse_validity(pos, end - pos, cert, &pos)) + return -1; + + /* subject Name */ + if (x509_parse_name(pos, end - pos, &cert->subject, &pos)) + return -1; + x509_name_string(&cert->subject, sbuf, sizeof(sbuf)); + wpa_printf(MSG_DEBUG, "X509: subject %s", sbuf); + + /* subjectPublicKeyInfo SubjectPublicKeyInfo */ + if (x509_parse_public_key(pos, end - pos, cert, &pos)) + return -1; + + if (pos == end) + return 0; + + if (cert->version == X509_CERT_V1) + return 0; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { + wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" + " tag to parse optional tbsCertificate " + "field(s); parsed class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + if (hdr.tag == 1) { + /* issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL */ + wpa_printf(MSG_DEBUG, "X509: issuerUniqueID"); + /* TODO: parse UniqueIdentifier ::= BIT STRING */ + + if (hdr.payload + hdr.length == end) + return 0; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { + wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" + " tag to parse optional tbsCertificate " + "field(s); parsed class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + } + + if (hdr.tag == 2) { + /* subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL */ + wpa_printf(MSG_DEBUG, "X509: subjectUniqueID"); + /* TODO: parse UniqueIdentifier ::= BIT STRING */ + + if (hdr.payload + hdr.length == end) + return 0; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { + wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" + " tag to parse optional tbsCertificate " + "field(s); parsed class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + } + + if (hdr.tag != 3) { + wpa_printf(MSG_DEBUG, "X509: Ignored unexpected " + "Context-Specific tag %d in optional " + "tbsCertificate fields", hdr.tag); + return 0; + } + + /* extensions [3] EXPLICIT Extensions OPTIONAL */ + + if (cert->version != X509_CERT_V3) { + wpa_printf(MSG_DEBUG, "X509: X.509%d certificate and " + "Extensions data which are only allowed for " + "version 3", cert->version + 1); + return -1; + } + + if (x509_parse_extensions(cert, hdr.payload, hdr.length) < 0) + return -1; + + pos = hdr.payload + hdr.length; + if (pos < end) { + wpa_hexdump(MSG_DEBUG, + "X509: Ignored extra tbsCertificate data", + pos, end - pos); + } + + return 0; +} + + +static int x509_rsadsi_oid(struct asn1_oid *oid) +{ + return oid->len >= 4 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */; +} + + +static int x509_pkcs_oid(struct asn1_oid *oid) +{ + return oid->len >= 5 && + x509_rsadsi_oid(oid) && + oid->oid[4] == 1 /* pkcs */; +} + + +static int x509_digest_oid(struct asn1_oid *oid) +{ + return oid->len >= 5 && + x509_rsadsi_oid(oid) && + oid->oid[4] == 2 /* digestAlgorithm */; +} + + +static int x509_sha1_oid(struct asn1_oid *oid) +{ + return oid->len == 6 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 3 /* identified-organization */ && + oid->oid[2] == 14 /* oiw */ && + oid->oid[3] == 3 /* secsig */ && + oid->oid[4] == 2 /* algorithms */ && + oid->oid[5] == 26 /* id-sha1 */; +} + + +static int x509_sha256_oid(struct asn1_oid *oid) +{ + return oid->len == 9 && + oid->oid[0] == 2 /* joint-iso-itu-t */ && + oid->oid[1] == 16 /* country */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 1 /* organization */ && + oid->oid[4] == 101 /* gov */ && + oid->oid[5] == 3 /* csor */ && + oid->oid[6] == 4 /* nistAlgorithm */ && + oid->oid[7] == 2 /* hashAlgs */ && + oid->oid[8] == 1 /* sha256 */; +} + + +/** + * x509_certificate_parse - Parse a X.509 certificate in DER format + * @buf: Pointer to the X.509 certificate in DER format + * @len: Buffer length + * Returns: Pointer to the parsed certificate or %NULL on failure + * + * Caller is responsible for freeing the returned certificate by calling + * x509_certificate_free(). + */ +struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) +{ + struct asn1_hdr hdr; + const u8 *pos, *end, *hash_start; + struct x509_certificate *cert; + + cert = (struct x509_certificate *)os_zalloc(sizeof(*cert) + len); + if (cert == NULL) + return NULL; + os_memcpy(cert + 1, buf, len); + cert->cert_start = (u8 *) (cert + 1); + cert->cert_len = len; + + pos = buf; + end = buf + len; + + /* RFC 3280 - X.509 v3 certificate / ASN.1 DER */ + + /* Certificate ::= SEQUENCE */ + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Certificate did not start with " + "a valid SEQUENCE - found class %d tag 0x%x", + hdr.class, hdr.tag); + x509_certificate_free(cert); + return NULL; + } + pos = hdr.payload; + + if (pos + hdr.length > end) { + x509_certificate_free(cert); + return NULL; + } + + if (pos + hdr.length < end) { + wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER " + "encoded certificate", + pos + hdr.length, end - pos + hdr.length); + end = pos + hdr.length; + } + + hash_start = pos; + cert->tbs_cert_start = cert->cert_start + (hash_start - buf); + if (x509_parse_tbs_certificate(pos, end - pos, cert, &pos)) { + x509_certificate_free(cert); + return NULL; + } + cert->tbs_cert_len = pos - hash_start; + + /* signatureAlgorithm AlgorithmIdentifier */ + if (x509_parse_algorithm_identifier(pos, end - pos, + &cert->signature_alg, &pos)) { + x509_certificate_free(cert); + return NULL; + } + + /* signatureValue BIT STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_BITSTRING) { + wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING " + "(signatureValue) - found class %d tag 0x%x", + hdr.class, hdr.tag); + x509_certificate_free(cert); + return NULL; + } + if (hdr.length < 1) { + x509_certificate_free(cert); + return NULL; + } + pos = hdr.payload; + if (*pos) { + wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits", + *pos); + /* PKCS #1 v1.5 10.2.1: + * It is an error if the length in bits of the signature S is + * not a multiple of eight. + */ + x509_certificate_free(cert); + return NULL; + } + os_free(cert->sign_value); + cert->sign_value = os_malloc(hdr.length - 1); + if (cert->sign_value == NULL) { + wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for " + "signatureValue"); + x509_certificate_free(cert); + return NULL; + } + os_memcpy(cert->sign_value, pos + 1, hdr.length - 1); + cert->sign_value_len = hdr.length - 1; + wpa_hexdump(MSG_MSGDUMP, "X509: signature", + cert->sign_value, cert->sign_value_len); + + return cert; +} + + +/** + * x509_certificate_check_signature - Verify certificate signature + * @issuer: Issuer certificate + * @cert: Certificate to be verified + * Returns: 0 if cert has a valid signature that was signed by the issuer, + * -1 if not + */ +int x509_certificate_check_signature(struct x509_certificate *issuer, + struct x509_certificate *cert) +{ + struct crypto_public_key *pk; + u8 *data; + const u8 *pos, *end, *next, *da_end; + size_t data_len; + struct asn1_hdr hdr; + struct asn1_oid oid; + u8 hash[32]; + size_t hash_len; + + if (!x509_pkcs_oid(&cert->signature.oid) || + cert->signature.oid.len != 7 || + cert->signature.oid.oid[5] != 1 /* pkcs-1 */) { + wpa_printf(MSG_DEBUG, "X509: Unrecognized signature " + "algorithm"); + return -1; + } + + pk = crypto_public_key_import(issuer->public_key, + issuer->public_key_len); + if (pk == NULL) + return -1; + + data_len = cert->sign_value_len; + data = os_malloc(data_len); + if (data == NULL) { + crypto_public_key_free(pk); + return -1; + } + + if (crypto_public_key_decrypt_pkcs1(pk, cert->sign_value, + cert->sign_value_len, data, + &data_len) < 0) { + wpa_printf(MSG_DEBUG, "X509: Failed to decrypt signature"); + crypto_public_key_free(pk); + os_free(data); + return -1; + } + crypto_public_key_free(pk); + + wpa_hexdump(MSG_MSGDUMP, "X509: Signature data D", data, data_len); + + /* + * PKCS #1 v1.5, 10.1.2: + * + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithmIdentifier, + * digest Digest + * } + * + * DigestAlgorithmIdentifier ::= AlgorithmIdentifier + * + * Digest ::= OCTET STRING + * + */ + if (asn1_get_next(data, data_len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(DigestInfo) - found class %d tag 0x%x", + hdr.class, hdr.tag); + os_free(data); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + /* + * X.509: + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL + * } + */ + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(AlgorithmIdentifier) - found class %d tag 0x%x", + hdr.class, hdr.tag); + os_free(data); + return -1; + } + da_end = hdr.payload + hdr.length; + + if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) { + wpa_printf(MSG_DEBUG, "X509: Failed to parse digestAlgorithm"); + os_free(data); + return -1; + } + + if (x509_sha1_oid(&oid)) { + if (cert->signature.oid.oid[6] != + 5 /* sha-1WithRSAEncryption */) { + wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA1 " + "does not match with certificate " + "signatureAlgorithm (%lu)", + cert->signature.oid.oid[6]); + os_free(data); + return -1; + } + goto skip_digest_oid; + } + + if (x509_sha256_oid(&oid)) { + if (cert->signature.oid.oid[6] != + 11 /* sha2561WithRSAEncryption */) { + wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA256 " + "does not match with certificate " + "signatureAlgorithm (%lu)", + cert->signature.oid.oid[6]); + os_free(data); + return -1; + } + goto skip_digest_oid; + } + + if (!x509_digest_oid(&oid)) { + wpa_printf(MSG_DEBUG, "X509: Unrecognized digestAlgorithm"); + os_free(data); + return -1; + } + switch (oid.oid[5]) { + case 5: /* md5 */ + if (cert->signature.oid.oid[6] != 4 /* md5WithRSAEncryption */) + { + wpa_printf(MSG_DEBUG, "X509: digestAlgorithm MD5 does " + "not match with certificate " + "signatureAlgorithm (%lu)", + cert->signature.oid.oid[6]); + os_free(data); + return -1; + } + break; + case 2: /* md2 */ + case 4: /* md4 */ + default: + wpa_printf(MSG_DEBUG, "X509: Unsupported digestAlgorithm " + "(%lu)", oid.oid[5]); + os_free(data); + return -1; + } + +skip_digest_oid: + /* Digest ::= OCTET STRING */ + pos = da_end; + end = data + data_len; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, "X509: Expected OCTETSTRING " + "(Digest) - found class %d tag 0x%x", + hdr.class, hdr.tag); + os_free(data); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "X509: Decrypted Digest", + hdr.payload, hdr.length); + + switch (cert->signature.oid.oid[6]) { + case 4: /* md5WithRSAEncryption */ + wpa_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 */ + wpa_sha1_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, + hash); + hash_len = 20; + wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA1)", + hash, hash_len); + break; + case 11: /* sha256WithRSAEncryption */ + if (wpa2_crypto_funcs.sha256_vector) { + wpa2_crypto_funcs.sha256_vector(1, &cert->tbs_cert_start, (int *)&cert->tbs_cert_len, + hash); + } else { + wpa_printf(MSG_ERROR, "Fail to register sha256 vector function!\r\n"); + return -1; + } + hash_len = 32; + wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)", + hash, hash_len); + break; + case 2: /* md2WithRSAEncryption */ + case 12: /* sha384WithRSAEncryption */ + case 13: /* sha512WithRSAEncryption */ + default: + wpa_printf(MSG_INFO, "X509: Unsupported certificate signature " + "algorithm (%lu)", cert->signature.oid.oid[6]); + os_free(data); + return -1; + } + + if (hdr.length != hash_len || + os_memcmp(hdr.payload, hash, hdr.length) != 0) { + wpa_printf(MSG_INFO, "X509: Certificate Digest does not match " + "with calculated tbsCertificate hash"); + os_free(data); + return -1; + } + + os_free(data); + + wpa_printf(MSG_DEBUG, "X509: Certificate Digest matches with " + "calculated tbsCertificate hash"); + + return 0; +} + + +static int x509_valid_issuer(const struct x509_certificate *cert) +{ + if ((cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS) && + !cert->ca) { + wpa_printf(MSG_DEBUG, "X509: Non-CA certificate used as an " + "issuer"); + return -1; + } + + if (cert->version == X509_CERT_V3 && + !(cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS)) { + wpa_printf(MSG_DEBUG, "X509: v3 CA certificate did not " + "include BasicConstraints extension"); + return -1; + } + + if ((cert->extensions_present & X509_EXT_KEY_USAGE) && + !(cert->key_usage & X509_KEY_USAGE_KEY_CERT_SIGN)) { + wpa_printf(MSG_DEBUG, "X509: Issuer certificate did not have " + "keyCertSign bit in Key Usage"); + return -1; + } + + return 0; +} + + +/** + * x509_certificate_chain_validate - Validate X.509 certificate chain + * @trusted: List of trusted certificates + * @chain: Certificate chain to be validated (first chain must be issued by + * signed by the second certificate in the chain and so on) + * @reason: Buffer for returning failure reason (X509_VALIDATE_*) + * Returns: 0 if chain is valid, -1 if not + */ +int x509_certificate_chain_validate(struct x509_certificate *trusted, + struct x509_certificate *chain, + int *reason, int disable_time_checks) +{ + long unsigned idx; + int chain_trusted = 0; + struct x509_certificate *cert, *trust; + char buf[128]; + struct os_time now; + + *reason = X509_VALIDATE_OK; + + wpa_printf(MSG_DEBUG, "X509: Validate certificate chain"); + os_get_time(&now); + + for (cert = chain, idx = 0; cert; cert = cert->next, idx++) { + x509_name_string(&cert->subject, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf); + + if (chain_trusted) + continue; + + if (!disable_time_checks && + ((unsigned long) now.sec < + (unsigned long) cert->not_before || + (unsigned long) now.sec > + (unsigned long) cert->not_after)) { + wpa_printf(MSG_INFO, "X509: Certificate not valid " + "(now=%lu not_before=%lu not_after=%lu)", + now.sec, cert->not_before, cert->not_after); + *reason = X509_VALIDATE_CERTIFICATE_EXPIRED; + return -1; + } + + if (cert->next) { + if (x509_name_compare(&cert->issuer, + &cert->next->subject) != 0) { + wpa_printf(MSG_DEBUG, "X509: Certificate " + "chain issuer name mismatch"); + x509_name_string(&cert->issuer, buf, + sizeof(buf)); + wpa_printf(MSG_DEBUG, "X509: cert issuer: %s", + buf); + x509_name_string(&cert->next->subject, buf, + sizeof(buf)); + wpa_printf(MSG_DEBUG, "X509: next cert " + "subject: %s", buf); + *reason = X509_VALIDATE_CERTIFICATE_UNKNOWN; + return -1; + } + + if (x509_valid_issuer(cert->next) < 0) { + *reason = X509_VALIDATE_BAD_CERTIFICATE; + return -1; + } + + if ((cert->next->extensions_present & + X509_EXT_PATH_LEN_CONSTRAINT) && + idx > cert->next->path_len_constraint) { + wpa_printf(MSG_DEBUG, "X509: pathLenConstraint" + " not met (idx=%lu issuer " + "pathLenConstraint=%lu)", idx, + cert->next->path_len_constraint); + *reason = X509_VALIDATE_BAD_CERTIFICATE; + return -1; + } + + if (x509_certificate_check_signature(cert->next, cert) + < 0) { + wpa_printf(MSG_DEBUG, "X509: Invalid " + "certificate signature within " + "chain"); + *reason = X509_VALIDATE_BAD_CERTIFICATE; + return -1; + } + } + + for (trust = trusted; trust; trust = trust->next) { + if (x509_name_compare(&cert->issuer, &trust->subject) + == 0) + break; + } + + if (trust) { + wpa_printf(MSG_DEBUG, "X509: Found issuer from the " + "list of trusted certificates"); + if (x509_valid_issuer(trust) < 0) { + *reason = X509_VALIDATE_BAD_CERTIFICATE; + return -1; + } + + if (x509_certificate_check_signature(trust, cert) < 0) + { + wpa_printf(MSG_DEBUG, "X509: Invalid " + "certificate signature"); + *reason = X509_VALIDATE_BAD_CERTIFICATE; + return -1; + } + + wpa_printf(MSG_DEBUG, "X509: Trusted certificate " + "found to complete the chain"); + chain_trusted = 1; + } + } + + if (!chain_trusted) { + wpa_printf(MSG_DEBUG, "X509: Did not find any of the issuers " + "from the list of trusted certificates"); + if (trusted) { + *reason = X509_VALIDATE_UNKNOWN_CA; + return -1; + } + wpa_printf(MSG_DEBUG, "X509: Certificate chain validation " + "disabled - ignore unknown CA issue"); + } + + wpa_printf(MSG_DEBUG, "X509: Certificate chain valid"); + + return 0; +} + + +/** + * x509_certificate_get_subject - Get a certificate based on Subject name + * @chain: Certificate chain to search through + * @name: Subject name to search for + * Returns: Pointer to the certificate with the given Subject name or + * %NULL on failure + */ +struct x509_certificate * +x509_certificate_get_subject(struct x509_certificate *chain, + struct x509_name *name) +{ + struct x509_certificate *cert; + + for (cert = chain; cert; cert = cert->next) { + if (x509_name_compare(&cert->subject, name) == 0) + return cert; + } + return NULL; +} + + +/** + * x509_certificate_self_signed - Is the certificate self-signed? + * @cert: Certificate + * Returns: 1 if certificate is self-signed, 0 if not + */ +int x509_certificate_self_signed(struct x509_certificate *cert) +{ + return x509_name_compare(&cert->issuer, &cert->subject) == 0; +} diff --git a/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/utils/base64.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/utils/base64.c new file mode 100644 index 0000000000..0340c390e7 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/utils/base64.c @@ -0,0 +1,155 @@ +/* + * 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" + +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/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/utils/ext_password.c b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/utils/ext_password.c new file mode 100644 index 0000000000..3989f949b1 --- /dev/null +++ b/cpu/esp32/vendor/esp-idf/wpa_supplicant/src/wpa2/utils/ext_password.c @@ -0,0 +1,114 @@ +/* + * 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 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 */