1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-16 18:13:49 +01:00

Merge pull request #12024 from gschorcht/cpu/esp32/esp_wifi/wpa2_enterprise

cpu/esp32/esp wifi: add WPA2 enterprise mode with IEEE 802.1x/EAP authentication
This commit is contained in:
benpicco 2020-03-22 17:10:31 +01:00 committed by GitHub
commit 921a6a663e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 18906 additions and 30 deletions

View File

@ -27,6 +27,13 @@ ifneq (,$(filter esp_wifi_any,$(USEMODULE)))
USEMODULE += esp_idf_wpa_supplicant_port USEMODULE += esp_idf_wpa_supplicant_port
endif endif
ifneq (,$(filter esp_wifi_enterprise,$(USEMODULE)))
# add additional modules used for WPA2 Enterprise mode
USEMODULE += esp_idf_wpa_supplicant_wpa2_eap_peer
USEMODULE += esp_idf_wpa_supplicant_wpa2_tls
USEMODULE += esp_idf_wpa_supplicant_wpa2_utils
endif
ifneq (,$(filter esp_idf_nvs_flash,$(USEMODULE))) ifneq (,$(filter esp_idf_nvs_flash,$(USEMODULE)))
# add additional modules required by esp_idf_nvs_flash # add additional modules required by esp_idf_nvs_flash
USEMODULE += mtd USEMODULE += mtd

View File

@ -3,4 +3,5 @@
include $(RIOTCPU)/esp_common/Makefile.features include $(RIOTCPU)/esp_common/Makefile.features
FEATURES_PROVIDED += arch_esp32 FEATURES_PROVIDED += arch_esp32
FEATURES_PROVIDED += esp_wifi_enterprise
FEATURES_PROVIDED += periph_rtc FEATURES_PROVIDED += periph_rtc

View File

@ -44,6 +44,7 @@ PSEUDOMODULES += esp_idf_newlib
PSEUDOMODULES += esp_rtc_timer PSEUDOMODULES += esp_rtc_timer
PSEUDOMODULES += esp_rtc_timer_32k PSEUDOMODULES += esp_rtc_timer_32k
PSEUDOMODULES += esp_spi_ram PSEUDOMODULES += esp_spi_ram
PSEUDOMODULES += esp_wifi_enterprise
USEMODULE += esp_idf_driver USEMODULE += esp_idf_driver
USEMODULE += esp_idf_esp32 USEMODULE += esp_idf_esp32

View File

@ -123,7 +123,8 @@ Module | Default | Short description
[esp_rtc_timer_32k](#esp32_rtc_timer) not used | enable RTC hardware timer with external 32.768 kHz crystal. [esp_rtc_timer_32k](#esp32_rtc_timer) not used | enable RTC hardware timer with external 32.768 kHz crystal.
[esp_spi_ram](#esp32_spi_ram) | not used | enable SPI RAM [esp_spi_ram](#esp32_spi_ram) | not used | enable SPI RAM
[esp_spiffs](#esp32_spiffs_device) | not used | enable SPIFFS for on-board flash memory [esp_spiffs](#esp32_spiffs_device) | not used | enable SPIFFS for on-board flash memory
[esp_wifi](#esp32_wifi_network_interface) | not used | enable the Wifi network device [esp_wifi](#esp32_wifi_network_interface) | not used | enable the Wifi network device in WPA2 personal mode
[esp_wifi_enterprise](#esp32_wifi_network_interface) | not used | enable the Wifi network device in WPA2 enterprise mode
</center> </center>
@ -423,7 +424,8 @@ esp_rtc_timer | Enable RTC hardware timer with internal 150 kHz RC oscillator.
esp_rtc_timer_32k | Enable RTC hardware timer with external 32.768 kHz crystal. esp_rtc_timer_32k | Enable RTC hardware timer with external 32.768 kHz crystal.
esp_spiffs | Enable the optional SPIFFS drive in on-board flash memory, see section [SPIFFS Device](#esp32_spiffs_device). esp_spiffs | Enable the optional SPIFFS drive in on-board flash memory, see section [SPIFFS Device](#esp32_spiffs_device).
esp_spi_ram | Enable the optional SPI RAM, see section [SPI RAM Modules](#esp32_spi_ram). esp_spi_ram | Enable the optional SPI RAM, see section [SPI RAM Modules](#esp32_spi_ram).
esp_wifi | Enable the built-in WiFi module as `netdev` network device, see section [WiFi Network Interface](#esp32_wifi_network_interface). esp_wifi | Enable the built-in WiFi module as `netdev` network device in WPA2 personal mode, see section [WiFi Network Interface](#esp32_wifi_network_interface).
esp_wifi_enterprise | Enable the built-in WiFi module as `netdev` network device in WPA2 enterprise mode, see section [WiFi Network Interface](#esp32_wifi_network_interface).
</center> </center>
@ -1025,12 +1027,14 @@ The board has to have one of the supported PHY modules to be able to use the Eth
\anchor esp32_wifi_network_interface \anchor esp32_wifi_network_interface
## <a name="esp32_wifi_network_interface"> WiFi Network Interface </a> &nbsp;[[TOC](#esp32_toc)] ## <a name="esp32_wifi_network_interface"> WiFi Network Interface </a> &nbsp;[[TOC](#esp32_toc)]
The RIOT port for ESP32 implements in module `esp_wifi` a `netdev` driver for The RIOT port for ESP32 implements a `netdev` driver for the built-in WiFi
the built-in WiFi interface. interface. This `netdev` driver supports WPA2 personal mode as well as WPA2
enterprise mode.
@note Module `esp_wifi` is not enabled automatically when the `netdev_default` ### WPA2 personal mode
module is used. Instead, if necessary, the application has to add the
`esp_wifi` module in the Makefile. To use the WiFi `netdev` driver in WPA2 personal mode with a
preshared key (PSK), module `esp_wifi` has to be enabled.
``` ```
USEMODULE += esp_wifi USEMODULE += esp_wifi
@ -1043,14 +1047,14 @@ Furthermore, the following configuration parameters have to be defined:
Parameter | Default | Description Parameter | Default | Description
:------------------|:--------------------------|:------------ :------------------|:--------------------------|:------------
ESP_WIFI_SSID | "RIOT_AP" | SSID of the AP to be used. ESP_WIFI_SSID | "RIOT_AP" | SSID of the AP to be used.
ESP_WIFI_PASS | "ThisistheRIOTporttoESP" | Passphrase used for the AP as clear text (max. 64 chars). ESP_WIFI_PASS | - | Passphrase used for the AP as clear text (max. 64 chars).
ESP_WIFI_STACKSIZE | #THREAD_STACKSIZE_DEFAULT |Stack size used for the WiFi netdev driver thread. ESP_WIFI_STACKSIZE | #THREAD_STACKSIZE_DEFAULT | Stack size used for the WiFi netdev driver thread.
</center> </center>
These configuration parameter definitions, as well as enabling the `esp_wifi` These configuration parameter definitions, as well as enabling the `esp_wifi`
module, can be done either in the makefile of the project or at make command module, can be done either in the makefile of the project or at make command
line, e.g.: line, for example:
``` ```
USEMODULE=esp_wifi \ USEMODULE=esp_wifi \
@ -1058,7 +1062,63 @@ CFLAGS='-DESP_WIFI_SSID=\"MySSID\" -DESP_WIFI_PASS=\"MyPassphrase\"' \
make -C examples/gnrc_networking BOARD=... make -C examples/gnrc_networking BOARD=...
``` ```
@note The Wifi network interface (module `esp_wifi`) and the @note
- Module `esp_wifi` is not enabled automatically when module
`netdev_default` is used.
- Leave 'ESP_WIFI_PASS' undefined to connect to an open WiFi access point.
- The Wifi network interface (module `esp_wifi`) and the
[ESP-NOW network interface](#esp32_esp_now_network_interface) (module `esp_now`)
can be used simultaneously, for example, to realize a border router for
a mesh network which uses ESP-NOW.
### WPA2 Enterprise Mode
To use the WiFi `netdev` driver in WPA2 enterprise mode with IEEE 802.1X/EAP
authentication, module `esp_wifi_enterprise` has to be enabled.
```
USEMODULE += esp_wifi_enterprise
```
It supports the following EAP authentication methods:
- PEAPv0
- PEAPv1
- TTLS
As inner (phase 2) EAP authentication method, only MSCHAPv2 is supported.
To use module `esp_wifi_enterprise` with these authentication methods, the
following configuration parameters have to be defined:
<center>
Parameter | Default | Description
:------------------|:----------|:------------
ESP_WIFI_SSID | "RIOT_AP" | SSID of the AP to be used.
ESP_WIFI_EAP_ID | none | Optional anonymous identity used in phase 1 (outer) EAP authentication. If it is not defined, the user name defined for phase 2 (inner) EAP authentication is used as identity in phase 1.
ESP_WIFI_EAP_USER | none | User name used in phase 2 (inner) EAP authentication.
ESP_WIFI_EAP_PASS | none | Password used in phase 2 (inner) EAP authentication.
ESP_WIFI_STACKSIZE | #THREAD_STACKSIZE_DEFAULT | Stack size used for the WiFi netdev driver thread.
</center>
These configuration parameter definitions, as well as enabling the `esp_wifi`
module, can be done either in the makefile of the project or at make command
line, for example:
```
USEMODULE=esp_wifi_enterprise \
CFLAGS='-DESP_WIFI_SSID=\"MySSID\" -DESP_WIFI_EAP_ID=\"anonymous\" -DESP_WIFI_EAP_USER=\"MyUserName\" -DESP_WIFI_EAP_PASS=\"MyPassphrase\"' \
make -C examples/gnrc_networking BOARD=...
```
@note
- Since there is no possibility to add the CA certificate to the RIOT image,
the verification of the AP certificate is not yet supported.
- Module `esp_wifi_enterprise` is not enabled automatically when module
`netdev_default` is used.
- The Wifi network interface (module `esp_wifi_enterprise`) and the
[ESP-NOW network interface](#esp32_esp_now_network_interface) (module `esp_now`) [ESP-NOW network interface](#esp32_esp_now_network_interface) (module `esp_now`)
can be used simultaneously, for example, to realize a border router for can be used simultaneously, for example, to realize a border router for
a mesh network which uses ESP-NOW. a mesh network which uses ESP-NOW.
@ -1093,7 +1153,7 @@ ESP_NOW_KEY | NULL | Defines a key that is used for encrypted communication betw
</center> </center>
@note The ESP-NOW network interface (module `esp_now`) and the @note The ESP-NOW network interface (module `esp_now`) and the
[Wifi network interface](#esp32_wifi_network_interface) (module `esp_wifi`) [Wifi network interface](#esp32_wifi_network_interface) (module `esp_wifi` or `esp_wifi_enterprise`)
can be used simultaneously, for example, to realize a border router for can be used simultaneously, for example, to realize a border router for
a mesh network which uses ESP-NOW. a mesh network which uses ESP-NOW.

View File

@ -157,7 +157,9 @@ extern "C" {
#define CONFIG_ESP32_WIFI_CSI_ENABLED 0 #define CONFIG_ESP32_WIFI_CSI_ENABLED 0
#define CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0 1 #define CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0 1
#define CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1 0 #define CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1 0
#define CONFIG_ESP32_WIFI_NVS_ENABLED 0 #if MODULE_ESP_IDF_NVS_ENABLED
#define CONFIG_ESP32_WIFI_NVS_ENABLED 1
#endif
/** /**
* PHY configuration * PHY configuration

View File

@ -187,7 +187,6 @@ const wps_crypto_funcs_t g_wifi_default_wps_crypto_funcs = {
* fast_crypto_hash_finish for finish hash calculate, rather than call crypto_hash_update and * fast_crypto_hash_finish for finish hash calculate, rather than call crypto_hash_update and
* crypto_hash_finish, so do crypto_cipher. * crypto_hash_finish, so do crypto_cipher.
*/ */
#if 0 /* WPA2 enterprise not supported at the moment */
const wpa2_crypto_funcs_t g_wifi_default_wpa2_crypto_funcs = { const wpa2_crypto_funcs_t g_wifi_default_wpa2_crypto_funcs = {
.size = sizeof(wpa2_crypto_funcs_t), .size = sizeof(wpa2_crypto_funcs_t),
.version = ESP_WIFI_CRYPTO_VERSION, .version = ESP_WIFI_CRYPTO_VERSION,
@ -199,7 +198,7 @@ const wpa2_crypto_funcs_t g_wifi_default_wpa2_crypto_funcs = {
.crypto_cipher_decrypt = (esp_crypto_cipher_decrypt_t)crypto_cipher_decrypt, .crypto_cipher_decrypt = (esp_crypto_cipher_decrypt_t)crypto_cipher_decrypt,
.crypto_cipher_deinit = (esp_crypto_cipher_deinit_t)crypto_cipher_deinit, .crypto_cipher_deinit = (esp_crypto_cipher_deinit_t)crypto_cipher_deinit,
.crypto_mod_exp = (esp_crypto_mod_exp_t)crypto_mod_exp, .crypto_mod_exp = (esp_crypto_mod_exp_t)crypto_mod_exp,
.sha256_vector = (esp_sha256_vector_t)sha256_vector, .sha256_vector = (esp_sha256_vector_t)wpa_sha256_vector,
.tls_init = (esp_tls_init_t)tls_init, .tls_init = (esp_tls_init_t)tls_init,
.tls_deinit = (esp_tls_deinit_t)tls_deinit, .tls_deinit = (esp_tls_deinit_t)tls_deinit,
.eap_peer_blob_init = (esp_eap_peer_blob_init_t)eap_peer_blob_init, .eap_peer_blob_init = (esp_eap_peer_blob_init_t)eap_peer_blob_init,
@ -215,7 +214,6 @@ const wpa2_crypto_funcs_t g_wifi_default_wpa2_crypto_funcs = {
.eap_sm_build_identity_resp = (esp_eap_sm_build_identity_resp_t)eap_sm_build_identity_resp, .eap_sm_build_identity_resp = (esp_eap_sm_build_identity_resp_t)eap_sm_build_identity_resp,
.eap_msg_alloc = (esp_eap_msg_alloc_t)eap_msg_alloc .eap_msg_alloc = (esp_eap_msg_alloc_t)eap_msg_alloc
}; };
#endif
const mesh_crypto_funcs_t g_wifi_default_mesh_crypto_funcs = { const mesh_crypto_funcs_t g_wifi_default_mesh_crypto_funcs = {
.aes_128_encrypt = (esp_aes_128_encrypt_t)wpa_aes_128_cbc_encrypt, .aes_128_encrypt = (esp_aes_128_encrypt_t)wpa_aes_128_cbc_encrypt,

View File

@ -476,6 +476,7 @@ wifi_osi_funcs_t g_wifi_osi_funcs = {
._periph_module_enable = periph_module_enable, ._periph_module_enable = periph_module_enable,
._periph_module_disable = periph_module_disable, ._periph_module_disable = periph_module_disable,
._esp_timer_get_time = esp_timer_get_time, ._esp_timer_get_time = esp_timer_get_time,
#if MODULE_ESP_IDF_NVS_FLASH
._nvs_set_i8 = nvs_set_i8, ._nvs_set_i8 = nvs_set_i8,
._nvs_get_i8 = nvs_get_i8, ._nvs_get_i8 = nvs_get_i8,
._nvs_set_u8 = nvs_set_u8, ._nvs_set_u8 = nvs_set_u8,
@ -488,6 +489,20 @@ wifi_osi_funcs_t g_wifi_osi_funcs = {
._nvs_set_blob = nvs_set_blob, ._nvs_set_blob = nvs_set_blob,
._nvs_get_blob = nvs_get_blob, ._nvs_get_blob = nvs_get_blob,
._nvs_erase_key = nvs_erase_key, ._nvs_erase_key = nvs_erase_key,
#else
._nvs_set_i8 = NULL,
._nvs_get_i8 = NULL,
._nvs_set_u8 = NULL,
._nvs_get_u8 = NULL,
._nvs_set_u16 = NULL,
._nvs_get_u16 = NULL,
._nvs_open = NULL,
._nvs_close = NULL,
._nvs_commit = NULL,
._nvs_set_blob = NULL,
._nvs_get_blob = NULL,
._nvs_erase_key = NULL,
#endif
._get_random = os_get_random, ._get_random = os_get_random,
._get_time = get_time_wrapper, ._get_time = get_time_wrapper,
._random = os_random, ._random = os_random,

View File

@ -1,9 +1,21 @@
ifneq (,$(filter esp_idf_wpa_supplicant_port ,$(USEMODULE))) ifneq (,$(filter esp_idf_wpa_supplicant_port,$(USEMODULE)))
DIRS += port DIRS += port
endif endif
ifneq (,$(filter esp_idf_wpa_supplicant_crypto ,$(USEMODULE))) ifneq (,$(filter esp_idf_wpa_supplicant_crypto,$(USEMODULE)))
DIRS += src/crypto DIRS += src/crypto
endif endif
ifneq (,$(filter esp_idf_wpa_supplicant_wpa2_eap_peer,$(USEMODULE)))
DIRS += src/wpa2/eap_peer
endif
ifneq (,$(filter esp_idf_wpa_supplicant_wpa2_tls,$(USEMODULE)))
DIRS += src/wpa2/tls
endif
ifneq (,$(filter esp_idf_wpa_supplicant_wpa2_utils,$(USEMODULE)))
DIRS += src/wpa2/utils
endif
include $(RIOTBASE)/Makefile.base include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,337 @@
/*
* wpa_supplicant/hostapd / common helper functions, etc.
* Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#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 <stdint.h>
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 <machine/endian.h>
#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 <endian.h>
#include <byteswap.h>
#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 &ethbroadcast
#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 */

View File

@ -0,0 +1,186 @@
/*
* wpa_supplicant - WPA definitions
* Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#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 */

View File

@ -0,0 +1,219 @@
/*
* wpa_supplicant/hostapd / Debug prints
* Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#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 */

View File

@ -0,0 +1,176 @@
/*
* Dynamic data buffer
* Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#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 */

View File

@ -0,0 +1,62 @@
/*
* EAP peer state machine functions (RFC 4137)
* Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#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 */

View File

@ -0,0 +1,151 @@
/*
* EAP peer state machines internal structures (RFC 4137)
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef EAP_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 */

View File

@ -5,6 +5,7 @@ include $(RIOTBASE)/Makefile.base
# we have to do it in that way to avoid that $(RIOTBASE)/sys/include/crypto # we have to do it in that way to avoid that $(RIOTBASE)/sys/include/crypto
# is found first # is found first
INCLUDES = -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/wpa_supplicant/include INCLUDES = -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/wpa_supplicant/include
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/wpa_supplicant/include/wpa
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/wpa_supplicant/port/include INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/wpa_supplicant/port/include
INCLUDES += -I$(ESP32_SDK_DIR)/components/wpa_supplicant/include INCLUDES += -I$(ESP32_SDK_DIR)/components/wpa_supplicant/include
INCLUDES += -I$(ESP32_SDK_DIR)/components/wpa_supplicant/port/include INCLUDES += -I$(ESP32_SDK_DIR)/components/wpa_supplicant/port/include

View File

@ -0,0 +1,20 @@
MODULE=esp_idf_wpa_supplicant_wpa2_eap_peer
include $(RIOTBASE)/Makefile.base
# we have to do it in that way to avoid that $(RIOTBASE)/sys/include/crypto
# is found first
INCLUDES = -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/wpa_supplicant/include
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/wpa_supplicant/include/wpa
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/wpa_supplicant/port/include
INCLUDES += -I$(ESP32_SDK_DIR)/components/wpa_supplicant/include
INCLUDES += -I$(ESP32_SDK_DIR)/components/wpa_supplicant/port/include
CFLAGS += -D__ets__ -DESPRESSIF_USE -DESP32_IDF_CODE=1
CFLAGS += -DEAP_PEER_METHOD -DEAP_TLS -DEAP_PEAP -DEAP_TTLS -DEAP_MSCHAPv2 -DUSE_WPA2_TASK
include $(RIOTCPU)/$(CPU)/Makefile.include
INCLUDES += -I$(RIOTBASE)/core/include
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/include/log
INCLUDES += -I$(RIOTCPU)/$(CPU)/include
INCLUDES += -I$(RIOTBOARD)/$(BOARD)/include

View File

@ -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 */

View File

@ -0,0 +1,731 @@
/*
* EAP peer state machines (RFC 4137)
* Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*
* This file implements the Peer State Machine as defined in RFC 4137. The used
* states and state transitions match mostly with the RFC. However, there are
* couple of additional transitions for working around small issues noticed
* during testing. These exceptions are explained in comments within the
* functions in this file. The method functions, m.func(), are similar to the
* ones used in RFC 4137, but some small changes have used here to optimize
* operations and to add functionality needed for fast re-authentication
* (session resumption).
*/
#include <string.h>
#include "esp_err.h"
#include "wpa/includes.h"
#include "wpa/common.h"
#include "wpa/wpa_debug.h"
#include "wpa/eapol_common.h"
#include "wpa/ieee802_11_defs.h"
#include "wpa/state_machine.h"
#include "wpa/wpa.h"
#include "crypto/crypto.h"
#include "wpa2/utils/ext_password.h"
#include "wpa2/tls/tls.h"
#include "wpa2/eap_peer/eap_i.h"
#include "wpa2/eap_peer/eap_config.h"
#include "wpa2/eap_peer/eap.h"
#include "wpa2/eap_peer/eap_tls.h"
#ifdef EAP_PEER_METHOD
#include "wpa2/eap_peer/eap_methods.h"
#endif
static bool gl_disable_time_check = true;
void eap_peer_config_deinit(struct eap_sm *sm);
void eap_peer_blob_deinit(struct eap_sm *sm);
void eap_deinit_prev_method(struct eap_sm *sm, const char *txt);
extern bool ieee80211_unregister_wpa2_cb(void);
#ifdef EAP_PEER_METHOD
static struct eap_method *eap_methods = NULL;
const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method)
{
struct eap_method *m;
for (m = eap_methods; m; m = m->next) {
if (m->vendor == vendor && m->method == method)
return m;
}
return NULL;
}
const struct eap_method * eap_peer_get_methods(size_t *count)
{
int c = 0;
struct eap_method *m;
for (m = eap_methods; m; m = m->next)
c++;
*count = c;
return eap_methods;
}
EapType eap_peer_get_type(const char *name, int *vendor)
{
struct eap_method *m;
for (m = eap_methods; m; m = m->next) {
if (os_strcmp(m->name, name) == 0) {
*vendor = m->vendor;
return m->method;
}
}
*vendor = EAP_VENDOR_IETF;
return EAP_TYPE_NONE;
}
static int
eap_allowed_phase2_type(int vendor, int type)
{
if (vendor != EAP_VENDOR_IETF)
return 0;
return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS &&
type != EAP_TYPE_FAST;
}
u32 eap_get_phase2_type(const char *name, int *vendor)
{
int v;
u8 type = eap_peer_get_type(name, &v);
if (eap_allowed_phase2_type(v, type)) {
*vendor = v;
return type;
}
*vendor = EAP_VENDOR_IETF;
return EAP_TYPE_NONE;
}
struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config,
size_t *count)
{
struct eap_method_type *buf;
u32 method;
int vendor;
size_t mcount;
const struct eap_method *methods, *m;
methods = eap_peer_get_methods(&mcount);
if (methods == NULL)
return NULL;
*count = 0;
buf = os_malloc(mcount * sizeof(struct eap_method_type));
if (buf == NULL)
return NULL;
for (m = methods; m; m = m->next) {
vendor = m->vendor;
method = m->method;
if (eap_allowed_phase2_type(vendor, method)) {
if (vendor == EAP_VENDOR_IETF &&
method == EAP_TYPE_TLS && config &&
config->private_key2 == NULL)
continue;
buf[*count].vendor = vendor;
buf[*count].method = method;
(*count)++;
}
}
return buf;
}
struct eap_method * eap_peer_method_alloc(int vendor, EapType method,
const char *name)
{
struct eap_method *eap;
eap = (struct eap_method *)os_zalloc(sizeof(*eap));
if (eap == NULL)
return NULL;
eap->vendor = vendor;
eap->method = method;
eap->name = name;
return eap;
}
void eap_peer_method_free(struct eap_method *method)
{
os_free(method);
}
int eap_peer_method_register(struct eap_method *method)
{
struct eap_method *m, *last = NULL;
if (method == NULL || method->name == NULL)
return -1;
for (m = eap_methods; m; m = m->next) {
if (m->vendor == method->vendor &&
m->method == method->method &&
os_strcmp(m->name, method->name))
return -2;
last = m;
}
if (last)
last->next = method;
else
eap_methods = method;
return 0;
}
void eap_peer_unregister_methods(void)
{
struct eap_method *m;
while (eap_methods) {
m = eap_methods;
eap_methods = eap_methods->next;
if (m->free)
m->free(m);
else
eap_peer_method_free(m);
}
}
int eap_peer_register_methods(void)
{
int ret = 0;
#ifdef EAP_MD5
if (ret == 0)
ret = eap_peer_md5_register();
#endif
#ifdef EAP_TLS
if (ret == 0)
ret = eap_peer_tls_register();
#endif
#ifdef EAP_MSCHAPv2
if (ret == 0)
ret = eap_peer_mschapv2_register();
#endif
#ifdef EAP_PEAP
if (ret == 0)
ret = eap_peer_peap_register();
#endif
#ifdef EAP_TTLS
if (ret == 0)
ret = eap_peer_ttls_register();
#endif
return ret;
}
void eap_deinit_prev_method(struct eap_sm *sm, const char *txt)
{
if (sm->m == NULL || sm->eap_method_priv == NULL)
return;
sm->m->deinit(sm, sm->eap_method_priv);
sm->eap_method_priv = NULL;
sm->m = NULL;
}
struct wpabuf * eap_sm_build_identity_resp(struct eap_sm *sm, u8 id, int encrypted)
{
const u8 *identity;
size_t identity_len;
struct wpabuf *eap_buf = NULL;
struct eap_peer_config *config = eap_get_config(sm);
if (config == NULL) {
wpa_printf(MSG_ERROR, "EAP: Build Identity Resp-> configuration was not available\n");
return NULL;
}
if (sm->m && sm->m->get_identity) {
identity = sm->m->get_identity(sm,
sm->eap_method_priv,
&identity_len);
} else if (!encrypted && config->anonymous_identity) {
identity = config->anonymous_identity;
identity_len = config->anonymous_identity_len;
} else {
identity = config->identity;
identity_len = config->identity_len;
}
if (identity == NULL) {
wpa_printf(MSG_ERROR, "EAP: Build Identity Resp-> identity was not available\n");
return NULL;
}
eap_buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY,
identity_len, EAP_CODE_RESPONSE, id);
if (!eap_buf) {
return NULL;
}
wpabuf_put_data(eap_buf, identity, identity_len);
return eap_buf;
}
struct wpabuf * eap_sm_build_nak(struct eap_sm *sm, EapType type, u8 id)
{
size_t count = 0;
int found = 0;
struct wpabuf *resp;
const struct eap_method *methods, *m;
methods = eap_peer_get_methods(&count);
if (methods == NULL)
return NULL;
if (type == EAP_TYPE_EXPANDED) {
/*Build Expanded NAK*/
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EXPANDED,
8 + 8 * (count + 1), EAP_CODE_RESPONSE, id);
if (resp == NULL)
return NULL;
wpabuf_put_be24(resp, EAP_VENDOR_IETF);
wpabuf_put_be32(resp, EAP_TYPE_NAK);
} else {
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK,
sizeof(struct eap_hdr) + 1 + count + 1,
EAP_CODE_RESPONSE, id);
if (resp == NULL)
return NULL;
wpabuf_put(resp, 0);
}
for (m = methods; m; m = m->next) {
if (type == EAP_TYPE_EXPANDED) {
wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
wpabuf_put_be24(resp, m->vendor);
wpabuf_put_be32(resp, m->method);
} else
wpabuf_put_u8(resp, 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;
}

View File

@ -0,0 +1,205 @@
/*
* EAP common peer/server definitions
* Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "wpa/includes.h"
#include "wpa/common.h"
#include "wpa2/eap_peer/eap_defs.h"
#include "wpa2/eap_peer/eap_common.h"
/**
* eap_hdr_len_valid - Validate EAP header length field
* @msg: EAP frame (starting with EAP header)
* @min_payload: Minimum payload length needed
* Returns: 1 for valid header, 0 for invalid
*
* This is a helper function that does minimal validation of EAP messages. The
* length field is verified to be large enough to include the header and not
* too large to go beyond the end of the buffer.
*/
int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload)
{
const struct eap_hdr *hdr;
size_t len;
if (msg == NULL)
return 0;
hdr = wpabuf_head(msg);
if (wpabuf_len(msg) < sizeof(*hdr)) {
wpa_printf(MSG_INFO, "EAP: Too short EAP frame");
return 0;
}
len = be_to_host16(hdr->length);
if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) {
wpa_printf(MSG_INFO, "EAP: Invalid EAP length");
return 0;
}
return 1;
}
/**
* eap_hdr_validate - Validate EAP header
* @vendor: Expected EAP Vendor-Id (0 = IETF)
* @eap_type: Expected EAP type number
* @msg: EAP frame (starting with EAP header)
* @plen: Pointer to variable to contain the returned payload length
* Returns: Pointer to EAP payload (after type field), or %NULL on failure
*
* This is a helper function for EAP method implementations. This is usually
* called in the beginning of struct eap_method::process() function to verify
* that the received EAP request packet has a valid header. This function is
* able to process both legacy and expanded EAP headers and in most cases, the
* caller can just use the returned payload pointer (into *plen) for processing
* the payload regardless of whether the packet used the expanded EAP header or
* not.
*/
const u8 * eap_hdr_validate(int vendor, EapType eap_type,
const struct wpabuf *msg, size_t *plen)
{
const struct eap_hdr *hdr;
const u8 *pos;
size_t len;
if (!eap_hdr_len_valid(msg, 1))
return NULL;
hdr = wpabuf_head(msg);
len = be_to_host16(hdr->length);
pos = (const u8 *) (hdr + 1);
if (*pos == EAP_TYPE_EXPANDED) {
int exp_vendor;
u32 exp_type;
if (len < sizeof(*hdr) + 8) {
wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP "
"length");
return NULL;
}
pos++;
exp_vendor = WPA_GET_BE24(pos);
pos += 3;
exp_type = WPA_GET_BE32(pos);
pos += 4;
if (exp_vendor != vendor || exp_type != (u32) eap_type) {
wpa_printf(MSG_INFO, "EAP: Invalid expanded frame "
"type");
return NULL;
}
*plen = len - sizeof(*hdr) - 8;
return pos;
} else {
if (vendor != EAP_VENDOR_IETF || *pos != eap_type) {
wpa_printf(MSG_INFO, "EAP: Invalid frame type");
return NULL;
}
*plen = len - sizeof(*hdr) - 1;
return pos + 1;
}
}
/**
* eap_msg_alloc - Allocate a buffer for an EAP message
* @vendor: Vendor-Id (0 = IETF)
* @type: EAP type
* @payload_len: Payload length in bytes (data after Type)
* @code: Message Code (EAP_CODE_*)
* @identifier: Identifier
* Returns: Pointer to the allocated message buffer or %NULL on error
*
* This function can be used to allocate a buffer for an EAP message and fill
* in the EAP header. This function is automatically using expanded EAP header
* if the selected Vendor-Id is not IETF. In other words, most EAP methods do
* not need to separately select which header type to use when using this
* function to allocate the message buffers. The returned buffer has room for
* payload_len bytes and has the EAP header and Type field already filled in.
*/
struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
u8 code, u8 identifier)
{
struct wpabuf *buf;
struct eap_hdr *hdr;
size_t len;
len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) +
payload_len;
buf = wpabuf_alloc(len);
if (buf == NULL)
return NULL;
hdr = wpabuf_put(buf, sizeof(*hdr));
hdr->code = code;
hdr->identifier = identifier;
hdr->length = host_to_be16(len);
if (vendor == EAP_VENDOR_IETF) {
wpabuf_put_u8(buf, type);
} else {
wpabuf_put_u8(buf, EAP_TYPE_EXPANDED);
wpabuf_put_be24(buf, vendor);
wpabuf_put_be32(buf, type);
}
return buf;
}
/**
* eap_update_len - Update EAP header length
* @msg: EAP message from eap_msg_alloc
*
* This function updates the length field in the EAP header to match with the
* current length for the buffer. This allows eap_msg_alloc() to be used to
* allocate a larger buffer than the exact message length (e.g., if exact
* message length is not yet known).
*/
void eap_update_len(struct wpabuf *msg)
{
struct eap_hdr *hdr;
hdr = wpabuf_mhead(msg);
if (wpabuf_len(msg) < sizeof(*hdr))
return;
hdr->length = host_to_be16(wpabuf_len(msg));
}
/**
* eap_get_id - Get EAP Identifier from wpabuf
* @msg: Buffer starting with an EAP header
* Returns: The Identifier field from the EAP header
*/
u8 eap_get_id(const struct wpabuf *msg)
{
const struct eap_hdr *eap;
if (wpabuf_len(msg) < sizeof(*eap))
return 0;
eap = wpabuf_head(msg);
return eap->identifier;
}
/**
* eap_get_id - Get EAP Type from wpabuf
* @msg: Buffer starting with an EAP header
* Returns: The EAP Type after the EAP header
*/
EapType eap_get_type(const struct wpabuf *msg)
{
if (wpabuf_len(msg) < sizeof(struct eap_hdr) + 1)
return EAP_TYPE_NONE;
return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)];
}

View File

@ -0,0 +1,671 @@
/*
* EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt)
* Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifdef EAP_MSCHAPv2
#include "wpa/wpa.h"
#include "wpa/includes.h"
#include "wpa/common.h"
#include "crypto/random.h"
#include "crypto/ms_funcs.h"
#include "wpa2/tls/tls.h"
#include "wpa2/eap_peer/eap_i.h"
#include "wpa2/eap_peer/eap_defs.h"
#include "wpa2/eap_peer/eap_tls_common.h"
#include "wpa2/eap_peer/eap_config.h"
#include "wpa2/eap_peer/mschapv2.h"
#include "wpa2/eap_peer/eap_methods.h"
#define MSCHAPV2_OP_CHALLENGE 1
#define MSCHAPV2_OP_RESPONSE 2
#define MSCHAPV2_OP_SUCCESS 3
#define MSCHAPV2_OP_FAILURE 4
#define MSCHAPV2_OP_CHANGE_PASSWORD 7
#define PASSWD_CHANGE_CHAL_LEN 16
#define MSCHAPV2_KEY_LEN 16
#define ERROR_RESTRICTED_LOGON_HOURS 646
#define ERROR_ACCT_DISABLED 647
#define ERROR_PASSWD_EXPIRED 648
#define ERROR_NO_DIALIN_PERMISSION 649
#define ERROR_AUTHENTICATION_FAILURE 691
#define ERROR_CHANGING_PASSWORD 709
struct eap_mschapv2_hdr {
u8 op_code;
u8 mschapv2_id;
u8 ms_length[2];
} __packed;
struct ms_response {
u8 peer_challenge[MSCHAPV2_CHAL_LEN];
u8 reserved[8];
u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
u8 flags;
} __packed;
struct ms_change_password {
u8 encr_password[516];
u8 encr_hash[16];
u8 peer_challenge[MSCHAPV2_CHAL_LEN];
u8 reserved[8];
u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
u8 flags[2];
} __packed;
struct eap_mschapv2_data {
u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN];
int auth_response_valid;
int prev_error;
u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN];
int passwd_change_challenge_valid;
int passwd_change_version;
u8 *peer_challenge;
u8 *auth_challenge;
int phase2;
u8 master_key[MSCHAPV2_MASTER_KEY_LEN];
int master_key_valid;
int success;
struct wpabuf *prev_challenge;
};
static void
eap_mschapv2_deinit(struct eap_sm *sm, void *priv)
{
struct eap_mschapv2_data *data = priv;
os_free(data->peer_challenge);
os_free(data->auth_challenge);
wpabuf_free(data->prev_challenge);
os_free(data);
}
static void *
eap_mschapv2_init(struct eap_sm *sm)
{
struct eap_mschapv2_data *data;
data = (struct eap_mschapv2_data *)os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->phase2 = sm->init_phase2;
return data;
}
static struct wpabuf *
eap_mschapv2_challenge_reply(
struct eap_sm *sm, struct eap_mschapv2_data *data,
u8 id, u8 mschapv2_id, const u8 *auth_challenge)
{
struct wpabuf *resp;
struct eap_mschapv2_hdr *ms;
u8 *peer_challenge;
int ms_len;
struct ms_response *r;
size_t identity_len, password_len;
const u8 *identity, *password;
int pwhash;
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generate Challenge Response\n");
identity = eap_get_config_identity(sm, &identity_len);
password = eap_get_config_password2(sm, &password_len, &pwhash);
if (identity == NULL || password == NULL)
return NULL;
ms_len = sizeof(*ms) + 1 + sizeof(*r) + identity_len;
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
ms_len, EAP_CODE_RESPONSE, id);
if (resp == NULL)
return NULL;
ms = wpabuf_put(resp, sizeof(*ms));
ms->op_code = MSCHAPV2_OP_RESPONSE;
ms->mschapv2_id = mschapv2_id;
if (data->prev_error)
ms->mschapv2_id++;
WPA_PUT_BE16(ms->ms_length, ms_len);
wpabuf_put_u8(resp, sizeof(*r));
/* Response */
r = wpabuf_put(resp, sizeof(*r));
peer_challenge = r->peer_challenge;
if (data->peer_challenge) {
peer_challenge = data->peer_challenge;
os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN);
} else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) {
wpabuf_free(resp);
return NULL;
}
os_memset(r->reserved, 0, 8);
if (data->auth_challenge)
auth_challenge = data->auth_challenge;
if (mschapv2_derive_response(identity, identity_len, password,
password_len, pwhash, auth_challenge,
peer_challenge, r->nt_response,
data->auth_response, data->master_key)) {
wpabuf_free(resp);
return NULL;
}
data->auth_response_valid = 1;
data->master_key_valid = 1;
r->flags = 0;
wpabuf_put_data(resp, identity, identity_len);
return resp;
}
static struct wpabuf *
eap_mschapv2_challenge(
struct eap_sm *sm, struct eap_mschapv2_data *data,
struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req,
size_t req_len, u8 id)
{
size_t len, challenge_len;
const u8 *pos, *challenge;
if (eap_get_config_identity(sm, &len) == NULL ||
eap_get_config_password(sm, &len) == NULL)
return NULL;
if (req_len < sizeof(*req) + 1) {
ret->ignore = true;
return NULL;
}
pos = (const u8 *)(req + 1);
challenge_len = *pos++;
len = req_len - sizeof(*req) - 1;
if (challenge_len != MSCHAPV2_CHAL_LEN) {
ret->ignore = true;
return NULL;
}
if (len < challenge_len) {
ret->ignore = true;
return NULL;
}
if (data->passwd_change_challenge_valid)
challenge = data->passwd_change_challenge;
else
challenge = pos;
pos += challenge_len;
len -= challenge_len;
ret->ignore = false;
ret->methodState = METHOD_MAY_CONT;
ret->decision = DECISION_FAIL;
ret->allowNotifications = true;
return eap_mschapv2_challenge_reply(sm, data, id, req->mschapv2_id,
challenge);
}
static void
eap_mschapv2_password_changed(struct eap_sm *sm,
struct eap_mschapv2_data *data)
{
struct eap_peer_config *config = eap_get_config(sm);
if (config && config->new_password) {
data->prev_error = 0;
os_free(config->password);
if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
} else if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) {
config->password = os_malloc(16);
config->password_len = 16;
if (config->password) {
nt_password_hash(config->new_password,
config->new_password_len,
config->password);
}
os_free(config->new_password);
} else {
config->password = config->new_password;
config->password_len = config->new_password_len;
}
config->new_password = NULL;
config->new_password_len = 0;
}
}
static struct wpabuf *
eap_mschapv2_success(struct eap_sm *sm,
struct eap_mschapv2_data *data,
struct eap_method_ret *ret,
const struct eap_mschapv2_hdr *req,
size_t req_len, u8 id)
{
struct wpabuf *resp;
const u8 *pos;
size_t len;
len = req_len - sizeof(*req);
pos = (const u8 *)(req + 1);
if (!data->auth_response_valid ||
mschapv2_verify_auth_response(data->auth_response, pos, len)) {
ret->methodState = METHOD_NONE;
ret->decision = DECISION_FAIL;
return NULL;
}
pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
while (len > 0 && *pos == ' ') {
pos++;
len--;
}
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
EAP_CODE_RESPONSE, id);
if (resp == NULL) {
ret->ignore = true;
return NULL;
}
wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS);
ret->methodState = METHOD_DONE;
ret->decision = DECISION_UNCOND_SUCC;
ret->allowNotifications = false;
data->success = 1;
if (data->prev_error == ERROR_PASSWD_EXPIRED)
eap_mschapv2_password_changed(sm, data);
return resp;
}
static int
eap_mschapv2_failure_txt(struct eap_sm *sm,
struct eap_mschapv2_data *data, char *txt)
{
char *pos;
//char *msg = "";
int retry = 1;
struct eap_peer_config *config = eap_get_config(sm);
pos = txt;
if (pos && os_strncmp(pos, "E=", 2) == 0) {
pos += 2;
data->prev_error = atoi(pos);
pos = (char *)os_strchr(pos, ' ');
if (pos)
pos++;
}
if (pos && os_strncmp(pos, "R=", 2) == 0) {
pos += 2;
retry = atoi(pos);
pos = (char *)os_strchr(pos, ' ');
if (pos)
pos++;
}
if (pos && os_strncmp(pos, "C=", 2) == 0) {
int hex_len;
pos += 2;
hex_len = (char *)os_strchr(pos, ' ') - (char *)pos;
if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) {
if (hexstr2bin(pos, data->passwd_change_challenge,
PASSWD_CHANGE_CHAL_LEN)) {
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: invalid failure challenge\n");
} else {
data->passwd_change_challenge_valid = 1;
}
} else {
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: required challenge field "
"was not present in failure message\n");
}
}
if (pos && os_strncmp(pos, "V=", 2) == 0) {
pos += 2;
data->passwd_change_version = atoi(pos);
pos = (char *)os_strchr(pos, ' ');
if (pos)
pos++;
}
if (pos && os_strncmp(pos, "M=", 2) == 0) {
pos += 2;
//msg = pos;
}
#if 0
wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error %d)",
msg, retry == 1? "" : "not ", data->prev_error);
#endif
if (data->prev_error == ERROR_PASSWD_EXPIRED &&
data->passwd_change_version == 3 && config) {
if (config->new_password == NULL) {
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Password expired - "
"password change reqired\n");
//eap_sm_request_new_password(sm);
}
} else if (retry == 1 && config) {
if (!config->mschapv2_retry)
//eap_sm_request_identity(sm);
//eap_sm_request_password(sm);
config->mschapv2_retry = 1;
} else if (config) {
config->mschapv2_retry = 0;
}
return retry == 1;
}
static struct wpabuf *
eap_mschapv2_change_password(
struct eap_sm *sm, struct eap_mschapv2_data *data,
struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id)
{
struct wpabuf *resp;
int ms_len;
const u8 *username, *password, *new_password;
size_t username_len, password_len, new_password_len;
struct eap_mschapv2_hdr *ms;
struct ms_change_password *cp;
u8 password_hash[16], password_hash_hash[16];
int pwhash;
username = eap_get_config_identity(sm, &username_len);
password = eap_get_config_password2(sm, &password_len, &pwhash);
new_password = eap_get_config_new_password(sm, &new_password_len);
if (username == NULL || password == NULL || new_password == NULL)
return NULL;
username = mschapv2_remove_domain(username, &username_len);
ret->ignore = false;
ret->methodState = METHOD_MAY_CONT;
ret->decision = DECISION_COND_SUCC;
ret->allowNotifications = TRUE;
ms_len = sizeof(*ms) + sizeof(*cp);
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
EAP_CODE_RESPONSE, id);
if (resp == NULL)
return NULL;
ms = wpabuf_put(resp, sizeof(*ms));
ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD;
ms->mschapv2_id = req->mschapv2_id + 1;
WPA_PUT_BE16(ms->ms_length, ms_len);
cp = wpabuf_put(resp, sizeof(*cp));
if (pwhash) {
if (encrypt_pw_block_with_password_hash(
new_password, new_password_len,
password, cp->encr_password))
goto fail;
} else {
if (new_password_encrypted_with_old_nt_password_hash(
new_password, new_password_len,
password, password_len, cp->encr_password))
goto fail;
}
if (pwhash) {
u8 new_password_hash[16];
nt_password_hash(new_password, new_password_len,
new_password_hash);
nt_password_hash_encrypted_with_block(password,
new_password_hash,
cp->encr_hash);
} else {
old_nt_password_hash_encrypted_with_new_nt_password_hash(
new_password, new_password_len,
password, password_len, cp->encr_hash);
}
if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
goto fail;
os_memset(cp->reserved, 0, 8);
generate_nt_response(data->passwd_change_challenge, cp->peer_challenge,
username, username_len, new_password,
new_password_len, cp->nt_response);
generate_authenticator_response(new_password, new_password_len,
cp->peer_challenge,
data->passwd_change_challenge,
username, username_len,
cp->nt_response, data->auth_response);
data->auth_response_valid = 1;
nt_password_hash(new_password, new_password_len, password_hash);
hash_nt_password_hash(password_hash, password_hash_hash);
get_master_key(password_hash_hash, cp->nt_response, data->master_key);
data->master_key_valid = 1;
os_memset(cp->flags, 0, 2);
return resp;
fail:
wpabuf_free(resp);
return NULL;
}
static struct wpabuf *
eap_mschapv2_failure(struct eap_sm *sm,
struct eap_mschapv2_data *data,
struct eap_method_ret *ret,
const struct eap_mschapv2_hdr *req,
size_t req_len, u8 id)
{
struct wpabuf *resp;
const u8 *msdata = (const u8 *)(req + 1);
char *buf;
size_t len = req_len - sizeof(*req);
int retry = 0;
buf = (char *)dup_binstr(msdata, len);
if (buf) {
retry = eap_mschapv2_failure_txt(sm, data, buf);
os_free(buf);
}
ret->ignore = false;
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
ret->allowNotifications = false;
if (data->prev_error == ERROR_PASSWD_EXPIRED &&
data->passwd_change_version == 3) {
struct eap_peer_config *config = eap_get_config(sm);
if (config && config->new_password)
return eap_mschapv2_change_password(sm, data, ret,
req, id);
//if (config && config->pending_req_new_password)
// return NULL;
} else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
return NULL;
}
resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
EAP_CODE_RESPONSE, id);
if (resp == NULL)
return NULL;
wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE);
return resp;
}
static int
eap_mschapv2_check_config(struct eap_sm *sm)
{
struct eap_peer_config *config = eap_get_config(sm);
if (config == NULL)
return -1;
if (config->identity == NULL ||
config->identity_len == 0) {
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: idetity not configured\n");
return -1;
}
if (config->password == NULL ||
config->password_len == 0) {
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Password not configured\n");
return -1;
}
return 0;
}
static int
eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len,
const struct eap_mschapv2_hdr *ms)
{
size_t ms_len = WPA_GET_BE16(ms->ms_length);
if (ms_len == len)
return 0;
if (sm->workaround) {
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Workaround, ignore Invalid"
" header len=%lu ms_len=%lu\n",
(unsigned long)len, (unsigned long)ms_len);
return 0;
}
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Invalid header len=%lu ms_len=%lu\n",
(unsigned long)len, (unsigned long)ms_len);
return -1;
}
static void
eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data,
const struct wpabuf *reqData)
{
wpabuf_free(data->prev_challenge);
data->prev_challenge = wpabuf_dup(reqData);
}
static struct wpabuf *
eap_mschapv2_process(struct eap_sm *sm, void *priv,
struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
u8 id;
size_t len;
const u8 *pos;
int using_prev_challenge = 0;
const struct eap_mschapv2_hdr *ms;
struct eap_mschapv2_data *data = priv;
struct eap_peer_config *config = eap_get_config(sm);
if (eap_mschapv2_check_config(sm)) {
ret->ignore = true;
return NULL;
}
if (config->mschapv2_retry && data->prev_challenge &&
data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
reqData = data->prev_challenge;
using_prev_challenge = 1;
config->mschapv2_retry = 0;
}
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
reqData, &len);
if (pos == NULL || len < sizeof(*ms) + 1) {
ret->ignore = true;
return NULL;
}
ms = (const struct eap_mschapv2_hdr *)pos;
if (eap_mschapv2_check_mslen(sm, len, ms)) {
ret->ignore = true;
return NULL;
}
id = eap_get_id(reqData);
wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d\n",
id, ms->mschapv2_id);
switch (ms->op_code) {
case MSCHAPV2_OP_CHALLENGE:
if (!using_prev_challenge)
eap_mschapv2_copy_challenge(data, reqData);
return eap_mschapv2_challenge(sm, data, ret, ms, len, id);
case MSCHAPV2_OP_SUCCESS:
return eap_mschapv2_success(sm, data, ret, ms, len, id);
case MSCHAPV2_OP_FAILURE:
return eap_mschapv2_failure(sm, data, ret, ms, len, id);
default:
wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Unknow op code %d -ignored\n",
ms->op_code);
return NULL;
}
}
static bool
eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv)
{
struct eap_mschapv2_data *data = priv;
return data->success && data->master_key_valid;
}
static u8 *
eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_mschapv2_data *data = priv;
u8 *key;
int key_len;
if (!data->master_key_valid || !data->success)
return NULL;
key_len = 2 * MSCHAPV2_KEY_LEN;
key = os_malloc(key_len);
/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key,
* peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */
get_asymetric_start_key(data->master_key, key,
MSCHAPV2_KEY_LEN, 1, 0);
get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
MSCHAPV2_KEY_LEN, 0, 0);
*len = key_len;
return key;
}
int
eap_peer_mschapv2_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_peer_method_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
"MSCHAPV2");
if (eap == NULL)
return -1;
eap->init = eap_mschapv2_init;
eap->deinit = eap_mschapv2_deinit;
eap->process = eap_mschapv2_process;
eap->isKeyAvailable = eap_mschapv2_isKeyAvailable;
eap->getKey = eap_mschapv2_getKey;
ret = eap_peer_method_register(eap);
if (ret)
eap_peer_method_free(eap);
return ret;
}
#endif /* EAP_MSCHAPv2 */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,90 @@
/*
* EAP-PEAP common routines
* Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
*
* 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 */

View File

@ -0,0 +1,232 @@
/*
* EAP peer method: EAP-TLS (RFC 2716)
* Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifdef EAP_TLS
#include "wpa/includes.h"
#include "wpa/common.h"
#include "wpa2/tls/tls.h"
#include "wpa2/eap_peer/eap_i.h"
#include "wpa2/eap_peer/eap_defs.h"
#include "wpa2/eap_peer/eap_tls_common.h"
#include "wpa2/eap_peer/eap_config.h"
#include "wpa2/eap_peer/eap_methods.h"
struct eap_tls_data {
struct eap_ssl_data ssl;
u8 *key_data;
u8 *session_id;
size_t id_len;
void *ssl_ctx;
u8 eap_type;
};
static void eap_tls_deinit(struct eap_sm *sm, void *priv)
{
struct eap_tls_data *data = priv;
if (data == NULL)
return;
eap_peer_tls_ssl_deinit(sm, &data->ssl);
os_free(data->key_data);
os_free(data->session_id);
os_free(data);
}
static void * eap_tls_init(struct eap_sm *sm)
{
struct eap_tls_data *data;
struct eap_peer_config *config = eap_get_config(sm);
if (config == NULL ||
config->private_key == 0) {
wpa_printf(MSG_INFO, "EAP-TLS: Private key not configured");
return NULL;
}
data = (struct eap_tls_data *)os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->ssl_ctx = sm->ssl_ctx;
if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TLS)) {
wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
eap_tls_deinit(sm, data);
return NULL;
}
data->eap_type = EAP_TYPE_TLS;
return data;
}
static struct wpabuf * eap_tls_failure(struct eap_sm *sm,
struct eap_tls_data *data,
struct eap_method_ret *ret, int res,
struct wpabuf *resp, u8 id)
{
wpa_printf(MSG_DEBUG, "EAP-TLS: TLS processing failed");
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
if (res == -1) {
struct eap_peer_config *config = eap_get_config(sm);
if (config) {
/*
* The TLS handshake failed. So better forget the old
* PIN. It may be wrong, we cannot be sure but trying
* the wrong one again might block it on the card--so
* better ask the user again.
*/
os_free(config->pin);
config->pin = NULL;
}
}
if (resp) {
/*
* This is likely an alert message, so send it instead of just
* ACKing the error.
*/
return resp;
}
return eap_peer_tls_build_ack(id, data->eap_type, 0);
}
static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data,
struct eap_method_ret *ret)
{
wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
ret->methodState = METHOD_DONE;
ret->decision = DECISION_UNCOND_SUCC;
os_free(data->key_data);
data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
"client EAP encryption",
EAP_TLS_KEY_LEN +
EAP_EMSK_LEN);
if (data->key_data) {
wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived key",
data->key_data, EAP_TLS_KEY_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived EMSK",
data->key_data + EAP_TLS_KEY_LEN,
EAP_EMSK_LEN);
} else {
wpa_printf(MSG_INFO, "EAP-TLS: Failed to derive key");
}
os_free(data->session_id);
data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl,
EAP_TYPE_TLS,
&data->id_len);
if (data->session_id) {
wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived Session-Id",
data->session_id, data->id_len);
} else {
wpa_printf(MSG_ERROR, "EAP-TLS: Failed to derive Session-Id");
}
}
static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv,
struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
size_t left;
int res;
struct wpabuf *resp;
u8 flags, id;
const u8 *pos;
struct eap_tls_data *data = priv;
pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret,
reqData, &left, &flags);
if (pos == NULL)
return NULL;
id = eap_get_id(reqData);
if (flags & EAP_TLS_FLAGS_START) {
wpa_printf(MSG_DEBUG, "EAP-TLS: Start");
left = 0; /* make sure that this frame is empty, even though it
* should always be, anyway */
}
resp = NULL;
res = eap_peer_tls_process_helper(sm, &data->ssl, data->eap_type, 0,
id, pos, left, &resp);
if (res < 0) {
return eap_tls_failure(sm, data, ret, res, resp, id);
}
if (tls_connection_established(data->ssl_ctx, data->ssl.conn))
eap_tls_success(sm, data, ret);
if (res == 1) {
wpabuf_free(resp);
return eap_peer_tls_build_ack(id, data->eap_type, 0);
}
return resp;
}
static bool eap_tls_isKeyAvailable(struct eap_sm *sm, void *priv)
{
struct eap_tls_data *data = priv;
return data->key_data != NULL;
}
static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_tls_data *data = priv;
u8 *key;
if (data->key_data == NULL)
return NULL;
key = os_malloc(EAP_TLS_KEY_LEN);
if (key == NULL)
return NULL;
*len = EAP_TLS_KEY_LEN;
os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
return key;
}
int eap_peer_tls_register(void)
{
struct eap_method *eap;
int ret;
eap = eap_peer_method_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLS,
"TLS");
if (eap == NULL)
return -1;
eap->init = eap_tls_init;
eap->deinit = eap_tls_deinit;
eap->process = eap_tls_process;
eap->isKeyAvailable = eap_tls_isKeyAvailable;
eap->getKey = eap_tls_getKey;
ret = eap_peer_method_register(eap);
if (ret)
eap_peer_method_free(eap);
return ret;
}
#endif /* EAP_TLS */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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 */

View File

@ -0,0 +1,19 @@
MODULE=esp_idf_wpa_supplicant_wpa2_tls
include $(RIOTBASE)/Makefile.base
# we have to do it in that way to avoid that $(RIOTBASE)/sys/include/crypto
# is found first
INCLUDES = -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/wpa_supplicant/include
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/wpa_supplicant/include/wpa
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/wpa_supplicant/port/include
INCLUDES += -I$(ESP32_SDK_DIR)/components/wpa_supplicant/include
INCLUDES += -I$(ESP32_SDK_DIR)/components/wpa_supplicant/port/include
CFLAGS += -D__ets__ -DESPRESSIF_USE -DESP32_IDF_CODE=1
include $(RIOTCPU)/$(CPU)/Makefile.include
INCLUDES += -I$(RIOTBASE)/core/include
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/include/log
INCLUDES += -I$(RIOTCPU)/$(CPU)/include
INCLUDES += -I$(RIOTBOARD)/$(BOARD)/include

View File

@ -0,0 +1,207 @@
/*
* ASN.1 DER parsing
* Copyright (c) 2006, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "wpa/includes.h"
#include "wpa/common.h"
#include "wpa2/tls/asn1.h"
int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr)
{
const u8 *pos, *end;
u8 tmp;
os_memset(hdr, 0, sizeof(*hdr));
pos = buf;
end = buf + len;
hdr->identifier = *pos++;
hdr->class = hdr->identifier >> 6;
hdr->constructed = !!(hdr->identifier & (1 << 5));
if ((hdr->identifier & 0x1f) == 0x1f) {
hdr->tag = 0;
do {
if (pos >= end) {
wpa_printf(MSG_DEBUG, "ASN.1: Identifier "
"underflow");
return -1;
}
tmp = *pos++;
wpa_printf(MSG_DEBUG, "ASN.1: Extended tag data: "
"0x%02x", tmp);
hdr->tag = (hdr->tag << 7) | (tmp & 0x7f);
} while (tmp & 0x80);
} else
hdr->tag = hdr->identifier & 0x1f;
tmp = *pos++;
if (tmp & 0x80) {
if (tmp == 0xff) {
wpa_printf(MSG_DEBUG, "ASN.1: Reserved length "
"value 0xff used");
return -1;
}
tmp &= 0x7f; /* number of subsequent octets */
hdr->length = 0;
if (tmp > 4) {
wpa_printf(MSG_DEBUG, "ASN.1: Too long length field");
return -1;
}
while (tmp--) {
if (pos >= end) {
wpa_printf(MSG_DEBUG, "ASN.1: Length "
"underflow");
return -1;
}
hdr->length = (hdr->length << 8) | *pos++;
}
} else {
/* Short form - length 0..127 in one octet */
hdr->length = tmp;
}
if (end < pos || hdr->length > (unsigned int) (end - pos)) {
wpa_printf(MSG_DEBUG, "ASN.1: Contents underflow");
return -1;
}
hdr->payload = pos;
return 0;
}
int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid)
{
const u8 *pos, *end;
unsigned long val;
u8 tmp;
os_memset(oid, 0, sizeof(*oid));
pos = buf;
end = buf + len;
while (pos < end) {
val = 0;
do {
if (pos >= end)
return -1;
tmp = *pos++;
val = (val << 7) | (tmp & 0x7f);
} while (tmp & 0x80);
if (oid->len >= ASN1_MAX_OID_LEN) {
wpa_printf(MSG_DEBUG, "ASN.1: Too long OID value");
return -1;
}
if (oid->len == 0) {
/*
* The first octet encodes the first two object
* identifier components in (X*40) + Y formula.
* X = 0..2.
*/
oid->oid[0] = val / 40;
if (oid->oid[0] > 2)
oid->oid[0] = 2;
oid->oid[1] = val - oid->oid[0] * 40;
oid->len = 2;
} else
oid->oid[oid->len++] = val;
}
return 0;
}
int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid,
const u8 **next)
{
struct asn1_hdr hdr;
if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0)
return -1;
if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OID) {
wpa_printf(MSG_DEBUG, "ASN.1: Expected OID - found class %d "
"tag 0x%x", hdr.class, hdr.tag);
return -1;
}
*next = hdr.payload + hdr.length;
return asn1_parse_oid(hdr.payload, hdr.length, oid);
}
void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len)
{
char *pos = buf;
size_t i;
int ret;
if (len == 0)
return;
buf[0] = '\0';
for (i = 0; i < oid->len; i++) {
//ret = os_snprintf(pos, buf + len - pos,
ret = sprintf(pos,
"%s%lu",
i == 0 ? "" : ".", oid->oid[i]);
if (ret < 0 || ret >= buf + len - pos)
break;
pos += ret;
}
buf[len - 1] = '\0';
}
static u8 rotate_bits(u8 octet)
{
int i;
u8 res;
res = 0;
for (i = 0; i < 8; i++) {
res <<= 1;
if (octet & 1)
res |= 1;
octet >>= 1;
}
return res;
}
unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len)
{
unsigned long val = 0;
const u8 *pos = buf;
/* BER requires that unused bits are zero, so we can ignore the number
* of unused bits */
pos++;
if (len >= 2)
val |= rotate_bits(*pos++);
if (len >= 3)
val |= ((unsigned long) rotate_bits(*pos++)) << 8;
if (len >= 4)
val |= ((unsigned long) rotate_bits(*pos++)) << 16;
if (len >= 5)
val |= ((unsigned long) rotate_bits(*pos++)) << 24;
if (len >= 6) {
wpa_printf(MSG_DEBUG, "X509: %s - some bits ignored "
"(BIT STRING length %lu)",
__func__, (unsigned long) len);
}
return val;
}

View File

@ -0,0 +1,244 @@
/*
* Big number math
* Copyright (c) 2006, Jouni Malinen <j@w1.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
* See README and COPYING for more details.
*/
#include "crypto/includes.h"
#include "crypto/common.h"
#include "wpa/wpabuf.h"
#include "wpa/wpa_debug.h"
#include "wpa2/tls/bignum.h"
#define CONFIG_INTERNAL_LIBTOMMATH
#ifdef CONFIG_INTERNAL_LIBTOMMATH
#include "wpa2/tls/libtommath.h"
#else /* CONFIG_INTERNAL_LIBTOMMATH */
#include <tommath.h>
#endif /* CONFIG_INTERNAL_LIBTOMMATH */
/*
* The current version is just a wrapper for LibTomMath library, so
* struct bignum is just typecast to mp_int.
*/
/**
* bignum_init - Allocate memory for bignum
* Returns: Pointer to allocated bignum or %NULL on failure
*/
struct bignum *
bignum_init(void)
{
struct bignum *n = (struct bignum *)os_zalloc(sizeof(mp_int));
if (n == NULL)
return NULL;
if (mp_init((mp_int *) n) != MP_OKAY) {
os_free(n);
n = NULL;
}
return n;
}
/**
* bignum_deinit - Free bignum
* @n: Bignum from bignum_init()
*/
void
bignum_deinit(struct bignum *n)
{
if (n) {
mp_clear((mp_int *) n);
os_free(n);
}
}
/**
* bignum_get_unsigned_bin - Get length of bignum as an unsigned binary buffer
* @n: Bignum from bignum_init()
* Returns: Length of n if written to a binary buffer
*/
size_t
bignum_get_unsigned_bin_len(struct bignum *n)
{
return mp_unsigned_bin_size((mp_int *) n);
}
/**
* bignum_get_unsigned_bin - Set binary buffer to unsigned bignum
* @n: Bignum from bignum_init()
* @buf: Buffer for the binary number
* @len: Length of the buffer, can be %NULL if buffer is known to be long
* enough. Set to used buffer length on success if not %NULL.
* Returns: 0 on success, -1 on failure
*/
int
bignum_get_unsigned_bin(const struct bignum *n, u8 *buf, size_t *len)
{
size_t need = mp_unsigned_bin_size((mp_int *) n);
if (len && need > *len) {
*len = need;
return -1;
}
if (mp_to_unsigned_bin((mp_int *) n, buf) != MP_OKAY) {
wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
return -1;
}
if (len)
*len = need;
return 0;
}
/**
* bignum_set_unsigned_bin - Set bignum based on unsigned binary buffer
* @n: Bignum from bignum_init(); to be set to the given value
* @buf: Buffer with unsigned binary value
* @len: Length of buf in octets
* Returns: 0 on success, -1 on failure
*/
int
bignum_set_unsigned_bin(struct bignum *n, const u8 *buf, size_t len)
{
if (mp_read_unsigned_bin((mp_int *) n, (u8 *) buf, len) != MP_OKAY) {
wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
return -1;
}
return 0;
}
/**
* bignum_cmp - Signed comparison
* @a: Bignum from bignum_init()
* @b: Bignum from bignum_init()
* Returns: 0 on success, -1 on failure
*/
int
bignum_cmp(const struct bignum *a, const struct bignum *b)
{
return mp_cmp((mp_int *) a, (mp_int *) b);
}
/**
* bignum_cmd_d - Compare bignum to standard integer
* @a: Bignum from bignum_init()
* @b: Small integer
* Returns: 0 on success, -1 on failure
*/
int
bignum_cmp_d(const struct bignum *a, unsigned long b)
{
return mp_cmp_d((mp_int *) a, b);
}
/**
* bignum_add - c = a + b
* @a: Bignum from bignum_init()
* @b: Bignum from bignum_init()
* @c: Bignum from bignum_init(); used to store the result of a + b
* Returns: 0 on success, -1 on failure
*/
int
bignum_add(const struct bignum *a, const struct bignum *b,
struct bignum *c)
{
if (mp_add((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
return -1;
}
return 0;
}
/**
* bignum_sub - c = a - b
* @a: Bignum from bignum_init()
* @b: Bignum from bignum_init()
* @c: Bignum from bignum_init(); used to store the result of a - b
* Returns: 0 on success, -1 on failure
*/
int
bignum_sub(const struct bignum *a, const struct bignum *b,
struct bignum *c)
{
if (mp_sub((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
return -1;
}
return 0;
}
/**
* bignum_mul - c = a * b
* @a: Bignum from bignum_init()
* @b: Bignum from bignum_init()
* @c: Bignum from bignum_init(); used to store the result of a * b
* Returns: 0 on success, -1 on failure
*/
int
bignum_mul(const struct bignum *a, const struct bignum *b,
struct bignum *c)
{
if (mp_mul((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
return -1;
}
return 0;
}
/**
* bignum_mulmod - d = a * b (mod c)
* @a: Bignum from bignum_init()
* @b: Bignum from bignum_init()
* @c: Bignum from bignum_init(); modulus
* @d: Bignum from bignum_init(); used to store the result of a * b (mod c)
* Returns: 0 on success, -1 on failure
*/
int
bignum_mulmod(const struct bignum *a, const struct bignum *b,
const struct bignum *c, struct bignum *d)
{
if (mp_mulmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d)
!= MP_OKAY) {
wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
return -1;
}
return 0;
}
/**
* bignum_exptmod - Modular exponentiation: d = a^b (mod c)
* @a: Bignum from bignum_init(); base
* @b: Bignum from bignum_init(); exponent
* @c: Bignum from bignum_init(); modulus
* @d: Bignum from bignum_init(); used to store the result of a^b (mod c)
* Returns: 0 on success, -1 on failure
*/
int
bignum_exptmod(const struct bignum *a, const struct bignum *b,
const struct bignum *c, struct bignum *d)
{
if (mp_exptmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d)
!= MP_OKAY) {
wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
return -1;
}
return 0;
}

View File

@ -0,0 +1,195 @@
/*
* PKCS #1 (RSA Encryption)
* Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "wpa/includes.h"
#include "wpa/common.h"
#include "wpa2/tls/rsa.h"
#include "wpa2/tls/pkcs1.h"
static int pkcs1_generate_encryption_block(u8 block_type, size_t modlen,
const u8 *in, size_t inlen,
u8 *out, size_t *outlen)
{
size_t ps_len;
u8 *pos;
/*
* PKCS #1 v1.5, 8.1:
*
* EB = 00 || BT || PS || 00 || D
* BT = 00 or 01 for private-key operation; 02 for public-key operation
* PS = k-3-||D||; at least eight octets
* (BT=0: PS=0x00, BT=1: PS=0xff, BT=2: PS=pseudorandom non-zero)
* k = length of modulus in octets (modlen)
*/
if (modlen < 12 || modlen > *outlen || inlen > modlen - 11) {
wpa_printf(MSG_DEBUG, "PKCS #1: %s - Invalid buffer "
"lengths (modlen=%lu outlen=%lu inlen=%lu)",
__func__, (unsigned long) modlen,
(unsigned long) *outlen,
(unsigned long) inlen);
return -1;
}
pos = out;
*pos++ = 0x00;
*pos++ = block_type; /* BT */
ps_len = modlen - inlen - 3;
switch (block_type) {
case 0:
os_memset(pos, 0x00, ps_len);
pos += ps_len;
break;
case 1:
os_memset(pos, 0xff, ps_len);
pos += ps_len;
break;
case 2:
if (os_get_random(pos, ps_len) < 0) {
wpa_printf(MSG_DEBUG, "PKCS #1: %s - Failed to get "
"random data for PS", __func__);
return -1;
}
while (ps_len--) {
if (*pos == 0x00)
*pos = 0x01;
pos++;
}
break;
default:
wpa_printf(MSG_DEBUG, "PKCS #1: %s - Unsupported block type "
"%d", __func__, block_type);
return -1;
}
*pos++ = 0x00;
os_memcpy(pos, in, inlen); /* D */
return 0;
}
int pkcs1_encrypt(int block_type, struct crypto_rsa_key *key,
int use_private, const u8 *in, size_t inlen,
u8 *out, size_t *outlen)
{
size_t modlen;
modlen = crypto_rsa_get_modulus_len(key);
if (pkcs1_generate_encryption_block(block_type, modlen, in, inlen,
out, outlen) < 0)
return -1;
return crypto_rsa_exptmod(out, modlen, out, outlen, key, use_private);
}
int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key,
const u8 *in, size_t inlen,
u8 *out, size_t *outlen)
{
int res;
u8 *pos, *end;
res = crypto_rsa_exptmod(in, inlen, out, outlen, key, 1);
if (res)
return res;
if (*outlen < 2 || out[0] != 0 || out[1] != 2)
return -1;
/* Skip PS (pseudorandom non-zero octets) */
pos = out + 2;
end = out + *outlen;
while (*pos && pos < end)
pos++;
if (pos == end)
return -1;
pos++;
*outlen -= pos - out;
/* Strip PKCS #1 header */
os_memmove(out, pos, *outlen);
return 0;
}
int pkcs1_decrypt_public_key(struct crypto_rsa_key *key,
const u8 *crypt, size_t crypt_len,
u8 *plain, size_t *plain_len)
{
size_t len;
u8 *pos;
len = *plain_len;
if (crypto_rsa_exptmod(crypt, crypt_len, plain, &len, key, 0) < 0)
return -1;
/*
* PKCS #1 v1.5, 8.1:
*
* EB = 00 || BT || PS || 00 || D
* BT = 00 or 01
* PS = k-3-||D|| times (00 if BT=00) or (FF if BT=01)
* k = length of modulus in octets
*/
if (len < 3 + 8 + 16 /* min hash len */ ||
plain[0] != 0x00 || (plain[1] != 0x00 && plain[1] != 0x01)) {
wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
"structure");
return -1;
}
pos = plain + 3;
if (plain[1] == 0x00) {
/* BT = 00 */
if (plain[2] != 0x00) {
wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature "
"PS (BT=00)");
return -1;
}
while (pos + 1 < plain + len && *pos == 0x00 && pos[1] == 0x00)
pos++;
} else {
/* BT = 01 */
if (plain[2] != 0xff) {
wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature "
"PS (BT=01)");
return -1;
}
while (pos < plain + len && *pos == 0xff)
pos++;
}
if (pos - plain - 2 < 8) {
/* PKCS #1 v1.5, 8.1: At least eight octets long PS */
wpa_printf(MSG_INFO, "LibTomCrypt: Too short signature "
"padding");
return -1;
}
if (pos + 16 /* min hash len */ >= plain + len || *pos != 0x00) {
wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
"structure (2)");
return -1;
}
pos++;
len -= pos - plain;
/* Strip PKCS #1 header */
os_memmove(plain, pos, len);
*plain_len = len;
return 0;
}

View File

@ -0,0 +1,262 @@
/*
* PKCS #5 (Password-based Encryption)
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "wpa/includes.h"
#include "wpa/common.h"
#include "crypto/crypto.h"
#include "crypto/md5.h"
#include "wpa2/tls/asn1.h"
#include "wpa2/tls/pkcs5.h"
#include "wpa2/eap_peer/eap_i.h"
struct pkcs5_params {
enum pkcs5_alg {
PKCS5_ALG_UNKNOWN,
PKCS5_ALG_MD5_DES_CBC
} alg;
u8 salt[8];
size_t salt_len;
unsigned int iter_count;
};
static enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid)
{
if (oid->len == 7 &&
oid->oid[0] == 1 /* iso */ &&
oid->oid[1] == 2 /* member-body */ &&
oid->oid[2] == 840 /* us */ &&
oid->oid[3] == 113549 /* rsadsi */ &&
oid->oid[4] == 1 /* pkcs */ &&
oid->oid[5] == 5 /* pkcs-5 */ &&
oid->oid[6] == 3 /* pbeWithMD5AndDES-CBC */)
return PKCS5_ALG_MD5_DES_CBC;
return PKCS5_ALG_UNKNOWN;
}
static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len,
struct pkcs5_params *params)
{
struct asn1_hdr hdr;
const u8 *enc_alg_end, *pos, *end;
struct asn1_oid oid;
char obuf[80];
/* AlgorithmIdentifier */
enc_alg_end = enc_alg + enc_alg_len;
os_memset(params, 0, sizeof(*params));
if (asn1_get_oid(enc_alg, enc_alg_end - enc_alg, &oid, &pos)) {
wpa_printf(MSG_DEBUG, "PKCS #5: Failed to parse OID "
"(algorithm)");
return -1;
}
asn1_oid_to_str(&oid, obuf, sizeof(obuf));
wpa_printf(MSG_DEBUG, "PKCS #5: encryption algorithm %s", obuf);
params->alg = pkcs5_get_alg(&oid);
if (params->alg == PKCS5_ALG_UNKNOWN) {
wpa_printf(MSG_INFO, "PKCS #5: unsupported encryption "
"algorithm %s", obuf);
return -1;
}
/*
* PKCS#5, Section 8
* PBEParameter ::= SEQUENCE {
* salt OCTET STRING SIZE(8),
* iterationCount INTEGER }
*/
if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 ||
hdr.class != ASN1_CLASS_UNIVERSAL ||
hdr.tag != ASN1_TAG_SEQUENCE) {
wpa_printf(MSG_DEBUG, "PKCS #5: Expected SEQUENCE "
"(PBEParameter) - found class %d tag 0x%x",
hdr.class, hdr.tag);
return -1;
}
pos = hdr.payload;
end = hdr.payload + hdr.length;
/* salt OCTET STRING SIZE(8) */
if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
hdr.class != ASN1_CLASS_UNIVERSAL ||
hdr.tag != ASN1_TAG_OCTETSTRING ||
hdr.length != 8) {
wpa_printf(MSG_DEBUG, "PKCS #5: Expected OCTETSTRING SIZE(8) "
"(salt) - found class %d tag 0x%x size %d",
hdr.class, hdr.tag, hdr.length);
return -1;
}
pos = hdr.payload + hdr.length;
os_memcpy(params->salt, hdr.payload, hdr.length);
params->salt_len = hdr.length;
wpa_hexdump(MSG_DEBUG, "PKCS #5: salt",
params->salt, params->salt_len);
/* iterationCount INTEGER */
if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
wpa_printf(MSG_DEBUG, "PKCS #5: Expected INTEGER - found "
"class %d tag 0x%x", hdr.class, hdr.tag);
return -1;
}
if (hdr.length == 1)
params->iter_count = *hdr.payload;
else if (hdr.length == 2)
params->iter_count = WPA_GET_BE16(hdr.payload);
else if (hdr.length == 4)
params->iter_count = WPA_GET_BE32(hdr.payload);
else {
wpa_hexdump(MSG_DEBUG, "PKCS #5: Unsupported INTEGER value "
" (iterationCount)",
hdr.payload, hdr.length);
return -1;
}
wpa_printf(MSG_DEBUG, "PKCS #5: iterationCount=0x%x",
params->iter_count);
if (params->iter_count == 0 || params->iter_count > 0xffff) {
wpa_printf(MSG_INFO, "PKCS #5: Unsupported "
"iterationCount=0x%x", params->iter_count);
return -1;
}
return 0;
}
static struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params,
const char *passwd)
{
unsigned int i;
u8 hash[MD5_MAC_LEN];
const u8 *addr[2];
size_t len[2];
if (params->alg != PKCS5_ALG_MD5_DES_CBC) {
return NULL;
}
addr[0] = (const u8 *) passwd;
len[0] = os_strlen(passwd);
addr[1] = params->salt;
len[1] = params->salt_len;
if (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, &params) < 0) {
wpa_printf(MSG_DEBUG, "PKCS #5: Unsupported parameters");
return NULL;
}
ctx = pkcs5_crypto_init(&params, passwd);
if (ctx == NULL) {
wpa_printf(MSG_DEBUG, "PKCS #5: Failed to initialize crypto");
return NULL;
}
/* PKCS #5, Section 7 - Decryption process */
if (enc_data_len < 16 || enc_data_len % 8) {
wpa_printf(MSG_INFO, "PKCS #5: invalid length of ciphertext "
"%d", (int) enc_data_len);
if (wpa2_crypto_funcs.crypto_cipher_deinit) {
wpa2_crypto_funcs.crypto_cipher_deinit(ctx);
} else {
wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n");
return NULL;
}
return NULL;
}
eb = os_malloc(enc_data_len);
if (eb == NULL) {
if (wpa2_crypto_funcs.crypto_cipher_deinit) {
wpa2_crypto_funcs.crypto_cipher_deinit(ctx);
} else {
wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n");
return NULL;
}
return NULL;
}
if (wpa2_crypto_funcs.crypto_cipher_decrypt) {
if ((int)wpa2_crypto_funcs.crypto_cipher_decrypt(ctx, enc_data, eb, enc_data_len) < 0) {
wpa_printf(MSG_DEBUG, "PKCS #5: Failed to decrypt EB");
wpa2_crypto_funcs.crypto_cipher_deinit(ctx);
os_free(eb);
return NULL;
}
} else {
wpa_printf(MSG_ERROR, "Fail to register crypto cipher decrypt function.\r\n");
wpa2_crypto_funcs.crypto_cipher_deinit(ctx);
os_free(eb);
return NULL;
}
if (wpa2_crypto_funcs.crypto_cipher_deinit) {
wpa2_crypto_funcs.crypto_cipher_deinit(ctx);
} else {
wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n");
return NULL;
}
pad = eb[enc_data_len - 1];
if (pad > 8) {
wpa_printf(MSG_INFO, "PKCS #5: Invalid PS octet 0x%x", pad);
os_free(eb);
return NULL;
}
for (i = enc_data_len - pad; i < enc_data_len; i++) {
if (eb[i] != pad) {
wpa_hexdump(MSG_INFO, "PKCS #5: Invalid PS",
eb + enc_data_len - pad, pad);
os_free(eb);
return NULL;
}
}
wpa_hexdump_key(MSG_MSGDUMP, "PKCS #5: message M (encrypted key)",
eb, enc_data_len - pad);
*data_len = enc_data_len - pad;
return eb;
}

View File

@ -0,0 +1,186 @@
/*
* PKCS #8 (Private-key information syntax)
* Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "wpa/includes.h"
#include "wpa/common.h"
#include "wpa2/tls/asn1.h"
#include "wpa2/tls/bignum.h"
#include "wpa2/tls/rsa.h"
#include "wpa2/tls/pkcs5.h"
#include "wpa2/tls/pkcs8.h"
struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len)
{
struct asn1_hdr hdr;
const u8 *pos, *end;
struct bignum *zero;
struct asn1_oid oid;
char obuf[80];
/* PKCS #8, Chapter 6 */
/* PrivateKeyInfo ::= SEQUENCE */
if (asn1_get_next(buf, len, &hdr) < 0 ||
hdr.class != ASN1_CLASS_UNIVERSAL ||
hdr.tag != ASN1_TAG_SEQUENCE) {
wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 "
"header (SEQUENCE); assume PKCS #8 not used");
return NULL;
}
pos = hdr.payload;
end = pos + hdr.length;
/* version Version (Version ::= INTEGER) */
if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
wpa_printf(MSG_DEBUG, "PKCS #8: Expected INTEGER - found "
"class %d tag 0x%x; assume PKCS #8 not used",
hdr.class, hdr.tag);
return NULL;
}
zero = bignum_init();
if (zero == NULL)
return NULL;
if (bignum_set_unsigned_bin(zero, hdr.payload, hdr.length) < 0) {
wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse INTEGER");
bignum_deinit(zero);
return NULL;
}
pos = hdr.payload + hdr.length;
if (bignum_cmp_d(zero, 0) != 0) {
wpa_printf(MSG_DEBUG, "PKCS #8: Expected zero INTEGER in the "
"beginning of private key; not found; assume "
"PKCS #8 not used");
bignum_deinit(zero);
return NULL;
}
bignum_deinit(zero);
/* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier
* (PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier) */
if (asn1_get_next(pos, len, &hdr) < 0 ||
hdr.class != ASN1_CLASS_UNIVERSAL ||
hdr.tag != ASN1_TAG_SEQUENCE) {
wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE "
"(AlgorithmIdentifier) - found class %d tag 0x%x; "
"assume PKCS #8 not used",
hdr.class, hdr.tag);
return NULL;
}
if (asn1_get_oid(hdr.payload, hdr.length, &oid, &pos)) {
wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse OID "
"(algorithm); assume PKCS #8 not used");
return NULL;
}
asn1_oid_to_str(&oid, obuf, sizeof(obuf));
wpa_printf(MSG_DEBUG, "PKCS #8: algorithm=%s", obuf);
if (oid.len != 7 ||
oid.oid[0] != 1 /* iso */ ||
oid.oid[1] != 2 /* member-body */ ||
oid.oid[2] != 840 /* us */ ||
oid.oid[3] != 113549 /* rsadsi */ ||
oid.oid[4] != 1 /* pkcs */ ||
oid.oid[5] != 1 /* pkcs-1 */ ||
oid.oid[6] != 1 /* rsaEncryption */) {
wpa_printf(MSG_DEBUG, "PKCS #8: Unsupported private key "
"algorithm %s", obuf);
return NULL;
}
pos = hdr.payload + hdr.length;
/* privateKey PrivateKey (PrivateKey ::= OCTET STRING) */
if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
hdr.class != ASN1_CLASS_UNIVERSAL ||
hdr.tag != ASN1_TAG_OCTETSTRING) {
wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING "
"(privateKey) - found class %d tag 0x%x",
hdr.class, hdr.tag);
return NULL;
}
wpa_printf(MSG_DEBUG, "PKCS #8: Try to parse RSAPrivateKey");
return (struct crypto_private_key *)
crypto_rsa_import_private_key(hdr.payload, hdr.length);
}
struct crypto_private_key *
pkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd)
{
struct asn1_hdr hdr;
const u8 *pos, *end, *enc_alg;
size_t enc_alg_len;
u8 *data;
size_t data_len;
if (passwd == NULL)
return NULL;
/*
* PKCS #8, Chapter 7
* EncryptedPrivateKeyInfo ::= SEQUENCE {
* encryptionAlgorithm EncryptionAlgorithmIdentifier,
* encryptedData EncryptedData }
* EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
* EncryptedData ::= OCTET STRING
*/
if (asn1_get_next(buf, len, &hdr) < 0 ||
hdr.class != ASN1_CLASS_UNIVERSAL ||
hdr.tag != ASN1_TAG_SEQUENCE) {
wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 "
"header (SEQUENCE); assume encrypted PKCS #8 not "
"used");
return NULL;
}
pos = hdr.payload;
end = pos + hdr.length;
/* encryptionAlgorithm EncryptionAlgorithmIdentifier */
if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
hdr.class != ASN1_CLASS_UNIVERSAL ||
hdr.tag != ASN1_TAG_SEQUENCE) {
wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE "
"(AlgorithmIdentifier) - found class %d tag 0x%x; "
"assume encrypted PKCS #8 not used",
hdr.class, hdr.tag);
return NULL;
}
enc_alg = hdr.payload;
enc_alg_len = hdr.length;
pos = hdr.payload + hdr.length;
/* encryptedData EncryptedData */
if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
hdr.class != ASN1_CLASS_UNIVERSAL ||
hdr.tag != ASN1_TAG_OCTETSTRING) {
wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING "
"(encryptedData) - found class %d tag 0x%x",
hdr.class, hdr.tag);
return NULL;
}
data = pkcs5_decrypt(enc_alg, enc_alg_len, hdr.payload, hdr.length,
passwd, &data_len);
if (data) {
struct crypto_private_key *key;
key = pkcs8_key_import(data, data_len);
os_free(data);
return key;
}
return NULL;
}

View File

@ -0,0 +1,353 @@
/*
* RSA
* Copyright (c) 2006, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "wpa/includes.h"
#include "wpa/common.h"
#include "wpa2/tls/asn1.h"
#include "wpa2/tls/bignum.h"
#include "wpa2/tls/rsa.h"
#include "soc/dport_reg.h"
struct crypto_rsa_key {
int private_key; /* whether private key is set */
struct bignum *n; /* modulus (p * q) */
struct bignum *e; /* public exponent */
/* The following parameters are available only if private_key is set */
struct bignum *d; /* private exponent */
struct bignum *p; /* prime p (factor of n) */
struct bignum *q; /* prime q (factor of n) */
struct bignum *dmp1; /* d mod (p - 1); CRT exponent */
struct bignum *dmq1; /* d mod (q - 1); CRT exponent */
struct bignum *iqmp; /* 1 / q mod p; CRT coefficient */
};
static const u8 * crypto_rsa_parse_integer(const u8 *pos, const u8 *end,
struct bignum *num)
{
struct asn1_hdr hdr;
if (pos == NULL)
return NULL;
if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
wpa_printf(MSG_DEBUG, "RSA: Expected INTEGER - found class %d "
"tag 0x%x", hdr.class, hdr.tag);
return NULL;
}
if (bignum_set_unsigned_bin(num, hdr.payload, hdr.length) < 0) {
wpa_printf(MSG_DEBUG, "RSA: Failed to parse INTEGER");
return NULL;
}
return hdr.payload + hdr.length;
}
/**
* crypto_rsa_import_public_key - Import an RSA public key
* @buf: Key buffer (DER encoded RSA public key)
* @len: Key buffer length in bytes
* Returns: Pointer to the public key or %NULL on failure
*/
struct crypto_rsa_key *
crypto_rsa_import_public_key(const u8 *buf, size_t len)
{
struct crypto_rsa_key *key;
struct asn1_hdr hdr;
const u8 *pos, *end;
key = (struct crypto_rsa_key *)os_zalloc(sizeof(*key));
if (key == NULL)
return NULL;
key->n = bignum_init();
key->e = bignum_init();
if (key->n == NULL || key->e == NULL) {
crypto_rsa_free(key);
return NULL;
}
/*
* PKCS #1, 7.1:
* RSAPublicKey ::= SEQUENCE {
* modulus INTEGER, -- n
* publicExponent INTEGER -- e
* }
*/
if (asn1_get_next(buf, len, &hdr) < 0 ||
hdr.class != ASN1_CLASS_UNIVERSAL ||
hdr.tag != ASN1_TAG_SEQUENCE) {
wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE "
"(public key) - found class %d tag 0x%x",
hdr.class, hdr.tag);
goto error;
}
pos = hdr.payload;
end = pos + hdr.length;
pos = crypto_rsa_parse_integer(pos, end, key->n);
pos = crypto_rsa_parse_integer(pos, end, key->e);
if (pos == NULL)
goto error;
if (pos != end) {
wpa_hexdump(MSG_DEBUG,
"RSA: Extra data in public key SEQUENCE",
pos, end - pos);
goto error;
}
return key;
error:
crypto_rsa_free(key);
return NULL;
}
/**
* crypto_rsa_import_private_key - Import an RSA private key
* @buf: Key buffer (DER encoded RSA private key)
* @len: Key buffer length in bytes
* Returns: Pointer to the private key or %NULL on failure
*/
struct crypto_rsa_key *
crypto_rsa_import_private_key(const u8 *buf, size_t len)
{
struct crypto_rsa_key *key;
struct bignum *zero;
struct asn1_hdr hdr;
const u8 *pos, *end;
key = (struct crypto_rsa_key *)os_zalloc(sizeof(*key));
if (key == NULL)
return NULL;
key->private_key = 1;
key->n = bignum_init();
key->e = bignum_init();
key->d = bignum_init();
key->p = bignum_init();
key->q = bignum_init();
key->dmp1 = bignum_init();
key->dmq1 = bignum_init();
key->iqmp = bignum_init();
if (key->n == NULL || key->e == NULL || key->d == NULL ||
key->p == NULL || key->q == NULL || key->dmp1 == NULL ||
key->dmq1 == NULL || key->iqmp == NULL) {
crypto_rsa_free(key);
return NULL;
}
/*
* PKCS #1, 7.2:
* RSAPrivateKey ::= SEQUENCE {
* version Version,
* modulus INTEGER, -- n
* publicExponent INTEGER, -- e
* privateExponent INTEGER, -- d
* prime1 INTEGER, -- p
* prime2 INTEGER, -- q
* exponent1 INTEGER, -- d mod (p-1)
* exponent2 INTEGER, -- d mod (q-1)
* coefficient INTEGER -- (inverse of q) mod p
* }
*
* Version ::= INTEGER -- shall be 0 for this version of the standard
*/
if (asn1_get_next(buf, len, &hdr) < 0 ||
hdr.class != ASN1_CLASS_UNIVERSAL ||
hdr.tag != ASN1_TAG_SEQUENCE) {
wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE "
"(public key) - found class %d tag 0x%x",
hdr.class, hdr.tag);
goto error;
}
pos = hdr.payload;
end = pos + hdr.length;
zero = bignum_init();
if (zero == NULL)
goto error;
pos = crypto_rsa_parse_integer(pos, end, zero);
if (pos == NULL || bignum_cmp_d(zero, 0) != 0) {
wpa_printf(MSG_DEBUG, "RSA: Expected zero INTEGER in the "
"beginning of private key; not found");
bignum_deinit(zero);
goto error;
}
bignum_deinit(zero);
pos = crypto_rsa_parse_integer(pos, end, key->n);
pos = crypto_rsa_parse_integer(pos, end, key->e);
pos = crypto_rsa_parse_integer(pos, end, key->d);
pos = crypto_rsa_parse_integer(pos, end, key->p);
pos = crypto_rsa_parse_integer(pos, end, key->q);
pos = crypto_rsa_parse_integer(pos, end, key->dmp1);
pos = crypto_rsa_parse_integer(pos, end, key->dmq1);
pos = crypto_rsa_parse_integer(pos, end, key->iqmp);
if (pos == NULL)
goto error;
if (pos != end) {
wpa_hexdump(MSG_DEBUG,
"RSA: Extra data in public key SEQUENCE",
pos, end - pos);
goto error;
}
return key;
error:
crypto_rsa_free(key);
return NULL;
}
/**
* crypto_rsa_get_modulus_len - Get the modulus length of the RSA key
* @key: RSA key
* Returns: Modulus length of the key
*/
size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key)
{
return bignum_get_unsigned_bin_len(key->n);
}
/**
* crypto_rsa_exptmod - RSA modular exponentiation
* @in: Input data
* @inlen: Input data length
* @out: Buffer for output data
* @outlen: Maximum size of the output buffer and used size on success
* @key: RSA key
* @use_private: 1 = Use RSA private key, 0 = Use RSA public key
* Returns: 0 on success, -1 on failure
*/
int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen,
struct crypto_rsa_key *key, int use_private)
{
struct bignum *tmp, *a = NULL, *b = NULL;
int ret = -1;
size_t modlen;
if (use_private && !key->private_key)
return -1;
tmp = bignum_init();
if (tmp == NULL)
return -1;
if (bignum_set_unsigned_bin(tmp, in, inlen) < 0)
goto error;
if (bignum_cmp(key->n, tmp) < 0) {
/* Too large input value for the RSA key modulus */
goto error;
}
if (use_private) {
/*
* Decrypt (or sign) using Chinese remainer theorem to speed
* up calculation. This is equivalent to tmp = tmp^d mod n
* (which would require more CPU to calculate directly).
*
* dmp1 = (1/e) mod (p-1)
* dmq1 = (1/e) mod (q-1)
* iqmp = (1/q) mod p, where p > q
* m1 = c^dmp1 mod p
* m2 = c^dmq1 mod q
* h = q^-1 (m1 - m2) mod p
* m = m2 + hq
*/
a = bignum_init();
b = bignum_init();
if (a == NULL || b == NULL)
goto error;
/* a = tmp^dmp1 mod p */
if (bignum_exptmod(tmp, key->dmp1, key->p, a) < 0)
goto error;
/* b = tmp^dmq1 mod q */
if (bignum_exptmod(tmp, key->dmq1, key->q, b) < 0)
goto error;
/* tmp = (a - b) * (1/q mod p) (mod p) */
if (bignum_sub(a, b, tmp) < 0 ||
bignum_mulmod(tmp, key->iqmp, key->p, tmp) < 0)
goto error;
/* tmp = b + q * tmp */
if (bignum_mul(tmp, key->q, tmp) < 0 ||
bignum_add(tmp, b, tmp) < 0)
goto error;
} else {
/* Encrypt (or verify signature) */
/* tmp = tmp^e mod N */
if (bignum_exptmod(tmp, key->e, key->n, tmp) < 0)
goto error;
}
modlen = crypto_rsa_get_modulus_len(key);
if (modlen > *outlen) {
*outlen = modlen;
goto error;
}
if (bignum_get_unsigned_bin_len(tmp) > modlen)
goto error; /* should never happen */
*outlen = modlen;
os_memset(out, 0, modlen);
if (bignum_get_unsigned_bin(
tmp, out +
(modlen - bignum_get_unsigned_bin_len(tmp)), NULL) < 0)
goto error;
ret = 0;
error:
bignum_deinit(tmp);
bignum_deinit(a);
bignum_deinit(b);
return ret;
}
/**
* crypto_rsa_free - Free RSA key
* @key: RSA key to be freed
*
* This function frees an RSA key imported with either
* crypto_rsa_import_public_key() or crypto_rsa_import_private_key().
*/
void crypto_rsa_free(struct crypto_rsa_key *key)
{
if (key) {
bignum_deinit(key->n);
bignum_deinit(key->e);
bignum_deinit(key->d);
bignum_deinit(key->p);
bignum_deinit(key->q);
bignum_deinit(key->dmp1);
bignum_deinit(key->dmq1);
bignum_deinit(key->iqmp);
os_free(key);
}
}

View File

@ -0,0 +1,716 @@
/*
* TLS interface functions and an internal TLS implementation
* Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*
* This file interface functions for hostapd/wpa_supplicant to use the
* integrated TLSv1 implementation.
*/
#include "wpa/includes.h"
#include "wpa/common.h"
#include "crypto/sha1.h"
#include "crypto/md5.h"
#include "wpa2/tls/tls.h"
#include "wpa2/tls/tlsv1_client.h"
#include "wpa2/tls/tlsv1_server.h"
#ifndef CONFIG_TLS_INTERNAL_CLIENT
#define CONFIG_TLS_INTERNAL_CLIENT
#endif
static int tls_ref_count = 0;
struct tls_global {
int server;
struct tlsv1_credentials *server_cred;
int check_crl;
};
struct tls_connection {
struct tlsv1_client *client;
struct tlsv1_server *server;
};
void * tls_init(void)
{
struct tls_global *global;
if (tls_ref_count == 0) {
#ifdef CONFIG_TLS_INTERNAL_CLIENT
if (tlsv1_client_global_init())
return NULL;
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
#ifdef CONFIG_TLS_INTERNAL_SERVER
if (tlsv1_server_global_init())
return NULL;
#endif /* CONFIG_TLS_INTERNAL_SERVER */
}
tls_ref_count++;
global = (struct tls_global *)os_zalloc(sizeof(*global));
if (global == NULL)
return NULL;
return global;
}
void tls_deinit(void *ssl_ctx)
{
struct tls_global *global = ssl_ctx;
tls_ref_count--;
if (tls_ref_count == 0) {
#ifdef CONFIG_TLS_INTERNAL_CLIENT
tlsv1_client_global_deinit();
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
#ifdef CONFIG_TLS_INTERNAL_SERVER
tlsv1_cred_free(global->server_cred);
tlsv1_server_global_deinit();
#endif /* CONFIG_TLS_INTERNAL_SERVER */
}
os_free(global);
}
int tls_get_errors(void *tls_ctx)
{
return 0;
}
struct tls_connection * tls_connection_init(void *tls_ctx)
{
struct tls_connection *conn;
struct tls_global *global = tls_ctx;
conn = (struct tls_connection *)os_zalloc(sizeof(*conn));
if (conn == NULL)
return NULL;
#ifdef CONFIG_TLS_INTERNAL_CLIENT
if (!global->server) {
conn->client = tlsv1_client_init();
if (conn->client == NULL) {
os_free(conn);
return NULL;
}
}
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
#ifdef CONFIG_TLS_INTERNAL_SERVER
if (global->server) {
conn->server = tlsv1_server_init(global->server_cred);
if (conn->server == NULL) {
os_free(conn);
return NULL;
}
}
#endif /* CONFIG_TLS_INTERNAL_SERVER */
return conn;
}
void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
{
if (conn == NULL)
return;
#ifdef CONFIG_TLS_INTERNAL_CLIENT
if (conn->client)
tlsv1_client_deinit(conn->client);
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
#ifdef CONFIG_TLS_INTERNAL_SERVER
if (conn->server)
tlsv1_server_deinit(conn->server);
#endif /* CONFIG_TLS_INTERNAL_SERVER */
os_free(conn);
}
int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
{
#ifdef CONFIG_TLS_INTERNAL_CLIENT
if (conn->client)
return tlsv1_client_established(conn->client);
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
#ifdef CONFIG_TLS_INTERNAL_SERVER
if (conn->server)
return tlsv1_server_established(conn->server);
#endif /* CONFIG_TLS_INTERNAL_SERVER */
return 0;
}
int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
{
#ifdef CONFIG_TLS_INTERNAL_CLIENT
if (conn->client)
return tlsv1_client_shutdown(conn->client);
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
#ifdef CONFIG_TLS_INTERNAL_SERVER
if (conn->server)
return tlsv1_server_shutdown(conn->server);
#endif /* CONFIG_TLS_INTERNAL_SERVER */
return -1;
}
int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
const struct tls_connection_params *params)
{
#ifdef CONFIG_TLS_INTERNAL_CLIENT
struct tlsv1_credentials *cred;
if (conn->client == NULL)
return -1;
cred = tlsv1_cred_alloc();
if (cred == NULL)
return -1;
if (tlsv1_set_ca_cert(cred, params->ca_cert,
params->ca_cert_blob, params->ca_cert_blob_len,
params->ca_path)) {
wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA "
"certificates");
tlsv1_cred_free(cred);
return -1;
}
if (tlsv1_set_cert(cred, params->client_cert,
params->client_cert_blob,
params->client_cert_blob_len)) {
wpa_printf(MSG_INFO, "TLS: Failed to configure client "
"certificate");
tlsv1_cred_free(cred);
return -1;
}
if (tlsv1_set_private_key(cred, params->private_key,
params->private_key_passwd,
params->private_key_blob,
params->private_key_blob_len)) {
wpa_printf(MSG_INFO, "TLS: Failed to load private key");
tlsv1_cred_free(cred);
return -1;
}
if (tlsv1_client_set_cred(conn->client, cred) < 0) {
tlsv1_cred_free(cred);
return -1;
}
tlsv1_client_set_time_checks(
conn->client, !(params->flags & TLS_CONN_DISABLE_TIME_CHECKS));
//conn->client, !(TLS_CONN_DISABLE_TIME_CHECKS)); //snake
return 0;
#else /* CONFIG_TLS_INTERNAL_CLIENT */
return -1;
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
}
int tls_global_set_params(void *tls_ctx,
const struct tls_connection_params *params)
{
#ifdef CONFIG_TLS_INTERNAL_SERVER
struct tls_global *global = tls_ctx;
struct tlsv1_credentials *cred;
/* Currently, global parameters are only set when running in server
* mode. */
global->server = 1;
tlsv1_cred_free(global->server_cred);
global->server_cred = cred = tlsv1_cred_alloc();
if (cred == NULL)
return -1;
if (tlsv1_set_ca_cert(cred, params->ca_cert, params->ca_cert_blob,
params->ca_cert_blob_len, params->ca_path)) {
wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA "
"certificates");
return -1;
}
if (tlsv1_set_cert(cred, params->client_cert, params->client_cert_blob,
params->client_cert_blob_len)) {
wpa_printf(MSG_INFO, "TLS: Failed to configure server "
"certificate");
return -1;
}
if (tlsv1_set_private_key(cred, params->private_key,
params->private_key_passwd,
params->private_key_blob,
params->private_key_blob_len)) {
wpa_printf(MSG_INFO, "TLS: Failed to load private key");
return -1;
}
if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob,
params->dh_blob_len)) {
wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters");
return -1;
}
return 0;
#else /* CONFIG_TLS_INTERNAL_SERVER */
return -1;
#endif /* CONFIG_TLS_INTERNAL_SERVER */
}
int tls_global_set_verify(void *tls_ctx, int check_crl)
{
struct tls_global *global = tls_ctx;
global->check_crl = check_crl;
return 0;
}
int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
int verify_peer)
{
#ifdef CONFIG_TLS_INTERNAL_SERVER
if (conn->server)
return tlsv1_server_set_verify(conn->server, verify_peer);
#endif /* CONFIG_TLS_INTERNAL_SERVER */
return -1;
}
int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
struct tls_keys *keys)
{
#ifdef CONFIG_TLS_INTERNAL_CLIENT
if (conn->client)
return tlsv1_client_get_keys(conn->client, keys);
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
#ifdef CONFIG_TLS_INTERNAL_SERVER
if (conn->server)
return tlsv1_server_get_keys(conn->server, keys);
#endif /* CONFIG_TLS_INTERNAL_SERVER */
return -1;
}
int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
const char *label, int server_random_first,
u8 *out, size_t out_len)
{
#ifdef CONFIG_TLS_INTERNAL_CLIENT
if (conn->client) {
return tlsv1_client_prf(conn->client, label,
server_random_first,
out, out_len);
}
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
#ifdef CONFIG_TLS_INTERNAL_SERVER
if (conn->server) {
return tlsv1_server_prf(conn->server, label,
server_random_first,
out, out_len);
}
#endif /* CONFIG_TLS_INTERNAL_SERVER */
return -1;
}
struct wpabuf * tls_connection_handshake(void *tls_ctx,
struct tls_connection *conn,
const struct wpabuf *in_data,
struct wpabuf **appl_data)
{
return tls_connection_handshake2(tls_ctx, conn, in_data, appl_data,
NULL);
}
struct wpabuf * tls_connection_handshake2(void *tls_ctx,
struct tls_connection *conn,
const struct wpabuf *in_data,
struct wpabuf **appl_data,
int *need_more_data)
{
#ifdef CONFIG_TLS_INTERNAL_CLIENT
u8 *res, *ad;
size_t res_len, ad_len;
struct wpabuf *out;
if (conn->client == NULL)
return NULL;
ad = NULL;
res = tlsv1_client_handshake(conn->client,
in_data ? wpabuf_head(in_data) : NULL,
in_data ? wpabuf_len(in_data) : 0,
&res_len, &ad, &ad_len, need_more_data);
if (res == NULL) {
return NULL;
}
out = wpabuf_alloc_ext_data(res, res_len);
if (out == NULL) {
os_free(res);
os_free(ad);
return NULL;
}
if (appl_data) {
if (ad) {
*appl_data = wpabuf_alloc_ext_data(ad, ad_len);
if (*appl_data == NULL)
os_free(ad);
} else
*appl_data = NULL;
} else
os_free(ad);
return out;
#else /* CONFIG_TLS_INTERNAL_CLIENT */
return NULL;
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
}
struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
struct tls_connection *conn,
const struct wpabuf *in_data,
struct wpabuf **appl_data)
{
#ifdef CONFIG_TLS_INTERNAL_SERVER
u8 *res;
size_t res_len;
struct wpabuf *out;
if (conn->server == NULL)
return NULL;
if (appl_data)
*appl_data = NULL;
res = tlsv1_server_handshake(conn->server, wpabuf_head(in_data),
wpabuf_len(in_data), &res_len);
if (res == NULL && tlsv1_server_established(conn->server))
return wpabuf_alloc(0);
if (res == NULL)
return NULL;
out = wpabuf_alloc_ext_data(res, res_len);
if (out == NULL) {
os_free(res);
return NULL;
}
return out;
#else /* CONFIG_TLS_INTERNAL_SERVER */
return NULL;
#endif /* CONFIG_TLS_INTERNAL_SERVER */
}
struct wpabuf * tls_connection_encrypt(void *tls_ctx,
struct tls_connection *conn,
const struct wpabuf *in_data)
{
#ifdef CONFIG_TLS_INTERNAL_CLIENT
if (conn->client) {
struct wpabuf *buf;
int res;
buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
if (buf == NULL)
return NULL;
res = tlsv1_client_encrypt(conn->client, wpabuf_head(in_data),
wpabuf_len(in_data),
wpabuf_mhead(buf),
wpabuf_size(buf));
if (res < 0) {
wpabuf_free(buf);
return NULL;
}
wpabuf_put(buf, res);
return buf;
}
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
#ifdef CONFIG_TLS_INTERNAL_SERVER
if (conn->server) {
struct wpabuf *buf;
int res;
buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
if (buf == NULL)
return NULL;
res = tlsv1_server_encrypt(conn->server, wpabuf_head(in_data),
wpabuf_len(in_data),
wpabuf_mhead(buf),
wpabuf_size(buf));
if (res < 0) {
wpabuf_free(buf);
return NULL;
}
wpabuf_put(buf, res);
return buf;
}
#endif /* CONFIG_TLS_INTERNAL_SERVER */
return NULL;
}
struct wpabuf * tls_connection_decrypt(void *tls_ctx,
struct tls_connection *conn,
const struct wpabuf *in_data)
{
return tls_connection_decrypt2(tls_ctx, conn, in_data, NULL);
}
struct wpabuf * tls_connection_decrypt2(void *tls_ctx,
struct tls_connection *conn,
const struct wpabuf *in_data,
int *need_more_data)
{
if (need_more_data)
*need_more_data = 0;
#ifdef CONFIG_TLS_INTERNAL_CLIENT
if (conn->client) {
return tlsv1_client_decrypt(conn->client, wpabuf_head(in_data),
wpabuf_len(in_data),
need_more_data);
}
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
#ifdef CONFIG_TLS_INTERNAL_SERVER
if (conn->server) {
struct wpabuf *buf;
int res;
buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
if (buf == NULL)
return NULL;
res = tlsv1_server_decrypt(conn->server, wpabuf_head(in_data),
wpabuf_len(in_data),
wpabuf_mhead(buf),
wpabuf_size(buf));
if (res < 0) {
wpabuf_free(buf);
return NULL;
}
wpabuf_put(buf, res);
return buf;
}
#endif /* CONFIG_TLS_INTERNAL_SERVER */
return NULL;
}
int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
{
#ifdef CONFIG_TLS_INTERNAL_CLIENT
if (conn->client)
return tlsv1_client_resumed(conn->client);
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
#ifdef CONFIG_TLS_INTERNAL_SERVER
if (conn->server)
return tlsv1_server_resumed(conn->server);
#endif /* CONFIG_TLS_INTERNAL_SERVER */
return -1;
}
int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
u8 *ciphers)
{
#ifdef CONFIG_TLS_INTERNAL_CLIENT
if (conn->client)
return tlsv1_client_set_cipher_list(conn->client, ciphers);
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
#ifdef CONFIG_TLS_INTERNAL_SERVER
if (conn->server)
return tlsv1_server_set_cipher_list(conn->server, ciphers);
#endif /* CONFIG_TLS_INTERNAL_SERVER */
return -1;
}
int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
char *buf, size_t buflen)
{
if (conn == NULL)
return -1;
#ifdef CONFIG_TLS_INTERNAL_CLIENT
if (conn->client)
return tlsv1_client_get_cipher(conn->client, buf, buflen);
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
#ifdef CONFIG_TLS_INTERNAL_SERVER
if (conn->server)
return tlsv1_server_get_cipher(conn->server, buf, buflen);
#endif /* CONFIG_TLS_INTERNAL_SERVER */
return -1;
}
int tls_connection_enable_workaround(void *tls_ctx,
struct tls_connection *conn)
{
return -1;
}
int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
int ext_type, const u8 *data,
size_t data_len)
{
#ifdef CONFIG_TLS_INTERNAL_CLIENT
if (conn->client) {
return tlsv1_client_hello_ext(conn->client, ext_type,
data, data_len);
}
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
return -1;
}
int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
{
return 0;
}
int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
{
return 0;
}
int tls_connection_get_write_alerts(void *tls_ctx,
struct tls_connection *conn)
{
return 0;
}
int tls_connection_get_keyblock_size(void *tls_ctx,
struct tls_connection *conn)
{
#ifdef CONFIG_TLS_INTERNAL_CLIENT
if (conn->client)
return tlsv1_client_get_keyblock_size(conn->client);
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
#ifdef CONFIG_TLS_INTERNAL_SERVER
if (conn->server)
return tlsv1_server_get_keyblock_size(conn->server);
#endif /* CONFIG_TLS_INTERNAL_SERVER */
return -1;
}
unsigned int tls_capabilities(void *tls_ctx)
{
return 0;
}
int tls_connection_set_session_ticket_cb(void *tls_ctx,
struct tls_connection *conn,
tls_session_ticket_cb cb,
void *ctx)
{
#ifdef CONFIG_TLS_INTERNAL_CLIENT
if (conn->client) {
tlsv1_client_set_session_ticket_cb(conn->client, cb, ctx);
return 0;
}
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
#ifdef CONFIG_TLS_INTERNAL_SERVER
if (conn->server) {
tlsv1_server_set_session_ticket_cb(conn->server, cb, ctx);
return 0;
}
#endif /* CONFIG_TLS_INTERNAL_SERVER */
return -1;
}
/**
* tls_prf_sha1_md5 - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246)
* @secret: Key for PRF
* @secret_len: Length of the key in bytes
* @label: A unique label for each purpose of the PRF
* @seed: Seed value to bind into the key
* @seed_len: Length of the seed
* @out: Buffer for the generated pseudo-random key
* @outlen: Number of bytes of key to generate
* Returns: 0 on success, -1 on failure.
*
* This function is used to derive new, cryptographically separate keys from a
* given key in TLS. This PRF is defined in RFC 2246, Chapter 5.
*/
int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label,
const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
{
size_t L_S1, L_S2, i;
const u8 *S1, *S2;
u8 A_MD5[MD5_MAC_LEN], A_SHA1[SHA1_MAC_LEN];
u8 P_MD5[MD5_MAC_LEN], P_SHA1[SHA1_MAC_LEN];
int MD5_pos, SHA1_pos;
const u8 *MD5_addr[3];
size_t MD5_len[3];
const unsigned char *SHA1_addr[3];
size_t SHA1_len[3];
if (secret_len & 1)
return -1;
MD5_addr[0] = A_MD5;
MD5_len[0] = MD5_MAC_LEN;
MD5_addr[1] = (unsigned char *) label;
MD5_len[1] = os_strlen(label);
MD5_addr[2] = seed;
MD5_len[2] = seed_len;
SHA1_addr[0] = A_SHA1;
SHA1_len[0] = SHA1_MAC_LEN;
SHA1_addr[1] = (unsigned char *) label;
SHA1_len[1] = os_strlen(label);
SHA1_addr[2] = seed;
SHA1_len[2] = seed_len;
/* RFC 2246, Chapter 5
* A(0) = seed, A(i) = HMAC(secret, A(i-1))
* P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + ..
* PRF = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed)
*/
L_S1 = L_S2 = (secret_len + 1) / 2;
S1 = secret;
S2 = secret + L_S1;
if (secret_len & 1) {
/* The last byte of S1 will be shared with S2 */
S2--;
}
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;
}

View File

@ -0,0 +1,837 @@
/*
* TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
* Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "wpa/includes.h"
#include "wpa/common.h"
#include "crypto/sha1.h"
#include "wpa2/tls/tls.h"
#include "wpa2/tls/tlsv1_common.h"
#include "wpa2/tls/tlsv1_record.h"
#include "wpa2/tls/tlsv1_client.h"
#include "wpa2/tls/tlsv1_client_i.h"
/* TODO:
* Support for a message fragmented across several records (RFC 2246, 6.2.1)
*/
void tls_alert(struct tlsv1_client *conn, u8 level, u8 description)
{
conn->alert_level = level;
conn->alert_description = description;
}
void tlsv1_client_free_dh(struct tlsv1_client *conn)
{
os_free(conn->dh_p);
os_free(conn->dh_g);
os_free(conn->dh_ys);
conn->dh_p = conn->dh_g = conn->dh_ys = NULL;
}
int tls_derive_pre_master_secret(u8 *pre_master_secret)
{
WPA_PUT_BE16(pre_master_secret, TLS_VERSION);
if (os_get_random(pre_master_secret + 2,
TLS_PRE_MASTER_SECRET_LEN - 2))
return -1;
return 0;
}
int tls_derive_keys(struct tlsv1_client *conn,
const u8 *pre_master_secret, size_t pre_master_secret_len)
{
u8 seed[2 * TLS_RANDOM_LEN];
u8 key_block[TLS_MAX_KEY_BLOCK_LEN];
u8 *pos;
size_t key_block_len;
if (pre_master_secret) {
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret",
pre_master_secret, pre_master_secret_len);
os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
TLS_RANDOM_LEN);
if (tls_prf(conn->rl.tls_version,
pre_master_secret, pre_master_secret_len,
"master secret", seed, 2 * TLS_RANDOM_LEN,
conn->master_secret, TLS_MASTER_SECRET_LEN)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive "
"master_secret");
return -1;
}
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret",
conn->master_secret, TLS_MASTER_SECRET_LEN);
}
os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN);
key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len);
if (conn->rl.tls_version == TLS_VERSION_1)
key_block_len += 2 * conn->rl.iv_size;
if (tls_prf(conn->rl.tls_version,
conn->master_secret, TLS_MASTER_SECRET_LEN,
"key expansion", seed, 2 * TLS_RANDOM_LEN,
key_block, key_block_len)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block");
return -1;
}
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block",
key_block, key_block_len);
pos = key_block;
/* client_write_MAC_secret */
os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size);
pos += conn->rl.hash_size;
/* server_write_MAC_secret */
os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size);
pos += conn->rl.hash_size;
/* client_write_key */
os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len);
pos += conn->rl.key_material_len;
/* server_write_key */
os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len);
pos += conn->rl.key_material_len;
if (conn->rl.tls_version == TLS_VERSION_1) {
/* client_write_IV */
os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size);
pos += conn->rl.iv_size;
/* server_write_IV */
os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
pos += conn->rl.iv_size;
} else {
/*
* Use IV field to set the mask value for TLS v1.1. A fixed
* mask of zero is used per the RFC 4346, 6.2.3.2 CBC Block
* Cipher option 2a.
*/
os_memset(conn->rl.write_iv, 0, conn->rl.iv_size);
}
return 0;
}
/**
* tlsv1_client_handshake - Process TLS handshake
* @conn: TLSv1 client connection data from tlsv1_client_init()
* @in_data: Input data from TLS peer
* @in_len: Input data length
* @out_len: Length of the output buffer.
* @appl_data: Pointer to application data pointer, or %NULL if dropped
* @appl_data_len: Pointer to variable that is set to appl_data length
* @need_more_data: Set to 1 if more data would be needed to complete
* processing
* Returns: Pointer to output data, %NULL on failure
*/
u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
const u8 *in_data, size_t in_len,
size_t *out_len, u8 **appl_data,
size_t *appl_data_len, int *need_more_data)
{
const u8 *pos, *end;
u8 *msg = NULL, *in_msg = NULL, *in_pos, *in_end, alert, ct;
size_t in_msg_len;
int no_appl_data;
int used;
if (need_more_data)
*need_more_data = 0;
if (conn->state == CLIENT_HELLO) {
if (in_len)
return NULL;
return tls_send_client_hello(conn, out_len);
}
if (conn->partial_input) {
if (wpabuf_resize(&conn->partial_input, in_len) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
"memory for pending record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
goto failed;
}
wpabuf_put_data(conn->partial_input, in_data, in_len);
in_data = wpabuf_head(conn->partial_input);
in_len = wpabuf_len(conn->partial_input);
}
if (in_data == NULL || in_len == 0)
return NULL;
pos = in_data;
end = in_data + in_len;
in_msg = os_malloc(in_len);
if (in_msg == NULL)
return NULL;
/* Each received packet may include multiple records */
while (pos < end) {
in_msg_len = in_len;
used = tlsv1_record_receive(&conn->rl, pos, end - pos,
in_msg, &in_msg_len, &alert);
if (used < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
"record failed");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
goto failed;
}
if (used == 0) {
struct wpabuf *partial;
wpa_printf(MSG_DEBUG, "TLSv1: Need more data");
partial = wpabuf_alloc_copy(pos, end - pos);
wpabuf_free(conn->partial_input);
conn->partial_input = partial;
if (conn->partial_input == NULL) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
"allocate memory for pending "
"record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
goto failed;
}
os_free(in_msg);
if (need_more_data)
*need_more_data = 1;
return NULL;
}
ct = pos[0];
in_pos = in_msg;
in_end = in_msg + in_msg_len;
/* Each received record may include multiple messages of the
* same ContentType. */
while (in_pos < in_end) {
in_msg_len = in_end - in_pos;
if (tlsv1_client_process_handshake(conn, ct, in_pos,
&in_msg_len,
appl_data,
appl_data_len) < 0)
goto failed;
in_pos += in_msg_len;
}
pos += used;
}
os_free(in_msg);
in_msg = NULL;
no_appl_data = appl_data == NULL || *appl_data == NULL;
msg = tlsv1_client_handshake_write(conn, out_len, no_appl_data);
failed:
os_free(in_msg);
if (conn->alert_level) {
wpabuf_free(conn->partial_input);
conn->partial_input = NULL;
conn->state = FAILED;
os_free(msg);
msg = tlsv1_client_send_alert(conn, conn->alert_level,
conn->alert_description,
out_len);
} else if (msg == NULL) {
msg = (u8 *)os_zalloc(1);
*out_len = 0;
}
if (need_more_data == NULL || !(*need_more_data)) {
wpabuf_free(conn->partial_input);
conn->partial_input = NULL;
}
return msg;
}
/**
* tlsv1_client_encrypt - Encrypt data into TLS tunnel
* @conn: TLSv1 client connection data from tlsv1_client_init()
* @in_data: Pointer to plaintext data to be encrypted
* @in_len: Input buffer length
* @out_data: Pointer to output buffer (encrypted TLS data)
* @out_len: Maximum out_data length
* Returns: Number of bytes written to out_data, -1 on failure
*
* This function is used after TLS handshake has been completed successfully to
* send data in the encrypted tunnel.
*/
int tlsv1_client_encrypt(struct tlsv1_client *conn,
const u8 *in_data, size_t in_len,
u8 *out_data, size_t out_len)
{
size_t rlen;
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
in_data, in_len);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
out_data, out_len, in_data, in_len, &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
return rlen;
}
/**
* tlsv1_client_decrypt - Decrypt data from TLS tunnel
* @conn: TLSv1 client connection data from tlsv1_client_init()
* @in_data: Pointer to input buffer (encrypted TLS data)
* @in_len: Input buffer length
* @need_more_data: Set to 1 if more data would be needed to complete
* processing
* Returns: Decrypted data or %NULL on failure
*
* This function is used after TLS handshake has been completed successfully to
* receive data from the encrypted tunnel.
*/
struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn,
const u8 *in_data, size_t in_len,
int *need_more_data)
{
const u8 *in_end, *pos;
int used;
u8 alert, *out_pos, ct;
size_t olen;
struct wpabuf *buf = NULL;
if (need_more_data)
*need_more_data = 0;
if (conn->partial_input) {
if (wpabuf_resize(&conn->partial_input, in_len) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
"memory for pending record");
alert = TLS_ALERT_INTERNAL_ERROR;
goto fail;
}
wpabuf_put_data(conn->partial_input, in_data, in_len);
in_data = wpabuf_head(conn->partial_input);
in_len = wpabuf_len(conn->partial_input);
}
pos = in_data;
in_end = in_data + in_len;
while (pos < in_end) {
ct = pos[0];
if (wpabuf_resize(&buf, in_end - pos) < 0) {
alert = TLS_ALERT_INTERNAL_ERROR;
goto fail;
}
out_pos = wpabuf_put(buf, 0);
olen = wpabuf_tailroom(buf);
used = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
out_pos, &olen, &alert);
if (used < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
"failed");
goto fail;
}
if (used == 0) {
struct wpabuf *partial;
wpa_printf(MSG_DEBUG, "TLSv1: Need more data");
partial = wpabuf_alloc_copy(pos, in_end - pos);
wpabuf_free(conn->partial_input);
conn->partial_input = partial;
if (conn->partial_input == NULL) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
"allocate memory for pending "
"record");
alert = TLS_ALERT_INTERNAL_ERROR;
goto fail;
}
if (need_more_data)
*need_more_data = 1;
return buf;
}
if (ct == TLS_CONTENT_TYPE_ALERT) {
if (olen < 2) {
wpa_printf(MSG_DEBUG, "TLSv1: Alert "
"underflow");
alert = TLS_ALERT_DECODE_ERROR;
goto fail;
}
wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
out_pos[0], out_pos[1]);
if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) {
/* Continue processing */
pos += used;
continue;
}
alert = out_pos[1];
goto fail;
}
if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
"0x%x when decrypting application data",
pos[0]);
alert = TLS_ALERT_UNEXPECTED_MESSAGE;
goto fail;
}
wpabuf_put(buf, olen);
pos += used;
}
wpabuf_free(conn->partial_input);
conn->partial_input = NULL;
return buf;
fail:
wpabuf_free(buf);
wpabuf_free(conn->partial_input);
conn->partial_input = NULL;
tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
return NULL;
}
/**
* tlsv1_client_global_init - Initialize TLSv1 client
* Returns: 0 on success, -1 on failure
*
* This function must be called before using any other TLSv1 client functions.
*/
int tlsv1_client_global_init(void)
{
return crypto_global_init();
}
/**
* tlsv1_client_global_deinit - Deinitialize TLSv1 client
*
* This function can be used to deinitialize the TLSv1 client that was
* initialized by calling tlsv1_client_global_init(). No TLSv1 client functions
* can be called after this before calling tlsv1_client_global_init() again.
*/
void tlsv1_client_global_deinit(void)
{
crypto_global_deinit();
}
/**
* tlsv1_client_init - Initialize TLSv1 client connection
* Returns: Pointer to TLSv1 client connection data or %NULL on failure
*/
struct tlsv1_client * tlsv1_client_init(void)
{
struct tlsv1_client *conn;
size_t count;
u16 *suites;
conn = (struct tlsv1_client *)os_zalloc(sizeof(*conn));
if (conn == NULL)
return NULL;
conn->state = CLIENT_HELLO;
if (tls_verify_hash_init(&conn->verify) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify "
"hash");
os_free(conn);
return NULL;
}
count = 0;
suites = conn->cipher_suites;
suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256;
suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
conn->num_cipher_suites = count;
conn->rl.tls_version = TLS_VERSION;
return conn;
}
/**
* tlsv1_client_deinit - Deinitialize TLSv1 client connection
* @conn: TLSv1 client connection data from tlsv1_client_init()
*/
void tlsv1_client_deinit(struct tlsv1_client *conn)
{
crypto_public_key_free(conn->server_rsa_key);
tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
tlsv1_record_change_write_cipher(&conn->rl);
tlsv1_record_change_read_cipher(&conn->rl);
tls_verify_hash_free(&conn->verify);
os_free(conn->client_hello_ext);
tlsv1_client_free_dh(conn);
tlsv1_cred_free(conn->cred);
wpabuf_free(conn->partial_input);
os_free(conn);
}
/**
* tlsv1_client_established - Check whether connection has been established
* @conn: TLSv1 client connection data from tlsv1_client_init()
* Returns: 1 if connection is established, 0 if not
*/
int tlsv1_client_established(struct tlsv1_client *conn)
{
return conn->state == ESTABLISHED;
}
/**
* tlsv1_client_prf - Use TLS-PRF to derive keying material
* @conn: TLSv1 client connection data from tlsv1_client_init()
* @label: Label (e.g., description of the key) for PRF
* @server_random_first: seed is 0 = client_random|server_random,
* 1 = server_random|client_random
* @out: Buffer for output data from TLS-PRF
* @out_len: Length of the output buffer
* Returns: 0 on success, -1 on failure
*/
int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
int server_random_first, u8 *out, size_t out_len)
{
u8 seed[2 * TLS_RANDOM_LEN];
if (conn->state != ESTABLISHED)
return -1;
if (server_random_first) {
os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random,
TLS_RANDOM_LEN);
} else {
os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
TLS_RANDOM_LEN);
}
return tls_prf(conn->rl.tls_version,
conn->master_secret, TLS_MASTER_SECRET_LEN,
label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
}
/**
* tlsv1_client_get_cipher - Get current cipher name
* @conn: TLSv1 client connection data from tlsv1_client_init()
* @buf: Buffer for the cipher name
* @buflen: buf size
* Returns: 0 on success, -1 on failure
*
* Get the name of the currently used cipher.
*/
int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
size_t buflen)
{
#ifndef ESPRESSIF_USE
char *cipher;
switch (conn->rl.cipher_suite) {
case TLS_RSA_WITH_RC4_128_MD5:
cipher = "RC4-MD5";
break;
case TLS_RSA_WITH_RC4_128_SHA:
cipher = "RC4-SHA";
break;
case TLS_RSA_WITH_DES_CBC_SHA:
cipher = "DES-CBC-SHA";
break;
case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
cipher = "DES-CBC3-SHA";
break;
case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
cipher = "ADH-AES-128-SHA256";
break;
case TLS_DH_anon_WITH_AES_128_CBC_SHA:
cipher = "ADH-AES-128-SHA";
break;
case TLS_RSA_WITH_AES_256_CBC_SHA:
cipher = "AES-256-SHA";
break;
case TLS_RSA_WITH_AES_256_CBC_SHA256:
cipher = "AES-256-SHA256";
break;
case TLS_RSA_WITH_AES_128_CBC_SHA:
cipher = "AES-128-SHA";
break;
case TLS_RSA_WITH_AES_128_CBC_SHA256:
cipher = "AES-128-SHA256";
break;
default:
return -1;
}
os_memcpy((u8 *)buf, (u8 *)cipher, buflen);
return 0;
#else
char cipher[20];
switch (conn->rl.cipher_suite) {
case TLS_RSA_WITH_RC4_128_MD5:
strcpy(cipher, "RC4-MD5");
break;
case TLS_RSA_WITH_RC4_128_SHA:
strcpy(cipher, "RC4-SHA");
break;
case TLS_RSA_WITH_DES_CBC_SHA:
strcpy(cipher, "DES-CBC-SHA");
break;
case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
strcpy(cipher, "DES-CBC3-SHA");
break;
case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
strcpy(cipher, "ADH-AES-128-SHA256");
break;
case TLS_DH_anon_WITH_AES_128_CBC_SHA:
strcpy(cipher, "ADH-AES-128-SHA");
break;
case TLS_RSA_WITH_AES_256_CBC_SHA:
strcpy(cipher, "AES-256-SHA");
break;
case TLS_RSA_WITH_AES_256_CBC_SHA256:
strcpy(cipher, "AES-256-SHA256");
break;
case TLS_RSA_WITH_AES_128_CBC_SHA:
strcpy(cipher, "AES-128-SHA");
break;
case TLS_RSA_WITH_AES_128_CBC_SHA256:
strcpy(cipher, "AES-128-SHA256");
break;
default:
return -1;
}
os_memcpy((u8 *)buf, (u8 *)cipher, buflen);
return 0;
#endif
}
/**
* tlsv1_client_shutdown - Shutdown TLS connection
* @conn: TLSv1 client connection data from tlsv1_client_init()
* Returns: 0 on success, -1 on failure
*/
int tlsv1_client_shutdown(struct tlsv1_client *conn)
{
conn->state = CLIENT_HELLO;
if (tls_verify_hash_init(&conn->verify) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify "
"hash");
return -1;
}
tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
tlsv1_record_change_write_cipher(&conn->rl);
tlsv1_record_change_read_cipher(&conn->rl);
conn->certificate_requested = 0;
crypto_public_key_free(conn->server_rsa_key);
conn->server_rsa_key = NULL;
conn->session_resumed = 0;
return 0;
}
/**
* tlsv1_client_resumed - Was session resumption used
* @conn: TLSv1 client connection data from tlsv1_client_init()
* Returns: 1 if current session used session resumption, 0 if not
*/
int tlsv1_client_resumed(struct tlsv1_client *conn)
{
return !!conn->session_resumed;
}
/**
* tlsv1_client_hello_ext - Set TLS extension for ClientHello
* @conn: TLSv1 client connection data from tlsv1_client_init()
* @ext_type: Extension type
* @data: Extension payload (%NULL to remove extension)
* @data_len: Extension payload length
* Returns: 0 on success, -1 on failure
*/
int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type,
const u8 *data, size_t data_len)
{
u8 *pos;
conn->session_ticket_included = 0;
os_free(conn->client_hello_ext);
conn->client_hello_ext = NULL;
conn->client_hello_ext_len = 0;
if (data == NULL || data_len == 0)
return 0;
pos = conn->client_hello_ext = os_malloc(6 + data_len);
if (pos == NULL)
return -1;
WPA_PUT_BE16(pos, 4 + data_len);
pos += 2;
WPA_PUT_BE16(pos, ext_type);
pos += 2;
WPA_PUT_BE16(pos, data_len);
pos += 2;
os_memcpy(pos, data, data_len);
conn->client_hello_ext_len = 6 + data_len;
if (ext_type == TLS_EXT_PAC_OPAQUE) {
conn->session_ticket_included = 1;
wpa_printf(MSG_DEBUG, "TLSv1: Using session ticket");
}
return 0;
}
/**
* tlsv1_client_get_keys - Get master key and random data from TLS connection
* @conn: TLSv1 client connection data from tlsv1_client_init()
* @keys: Structure of key/random data (filled on success)
* Returns: 0 on success, -1 on failure
*/
int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys)
{
os_memset(keys, 0, sizeof(*keys));
if (conn->state == CLIENT_HELLO)
return -1;
keys->client_random = conn->client_random;
keys->client_random_len = TLS_RANDOM_LEN;
if (conn->state != SERVER_HELLO) {
keys->server_random = conn->server_random;
keys->server_random_len = TLS_RANDOM_LEN;
keys->master_key = conn->master_secret;
keys->master_key_len = TLS_MASTER_SECRET_LEN;
}
return 0;
}
/**
* tlsv1_client_get_keyblock_size - Get TLS key_block size
* @conn: TLSv1 client connection data from tlsv1_client_init()
* Returns: Size of the key_block for the negotiated cipher suite or -1 on
* failure
*/
int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn)
{
if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO)
return -1;
return 2 * (conn->rl.hash_size + conn->rl.key_material_len +
conn->rl.iv_size);
}
/**
* tlsv1_client_set_cipher_list - Configure acceptable cipher suites
* @conn: TLSv1 client connection data from tlsv1_client_init()
* @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
* (TLS_CIPHER_*).
* Returns: 0 on success, -1 on failure
*/
int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers)
{
size_t count;
u16 *suites;
/* TODO: implement proper configuration of cipher suites */
if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) {
count = 0;
suites = conn->cipher_suites;
suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA256;
suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA256;
suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5;
suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA;
/*
* Cisco AP (at least 350 and 1200 series) local authentication
* server does not know how to search cipher suites from the
* list and seem to require that the last entry in the list is
* the one that it wants to use. However, TLS specification
* requires the list to be in the client preference order. As a
* workaround, add anon-DH AES-128-SHA1 again at the end of the
* list to allow the Cisco code to find it.
*/
suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
conn->num_cipher_suites = count;
}
return 0;
}
/**
* tlsv1_client_set_cred - Set client credentials
* @conn: TLSv1 client connection data from tlsv1_client_init()
* @cred: Credentials from tlsv1_cred_alloc()
* Returns: 0 on success, -1 on failure
*
* On success, the client takes ownership of the credentials block and caller
* must not free it. On failure, caller is responsible for freeing the
* credential block.
*/
int tlsv1_client_set_cred(struct tlsv1_client *conn,
struct tlsv1_credentials *cred)
{
tlsv1_cred_free(conn->cred);
conn->cred = cred;
return 0;
}
void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled)
{
conn->disable_time_checks = !enabled;
}
void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn,
tlsv1_client_session_ticket_cb cb,
void *ctx)
{
wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)",
cb, ctx);
conn->session_ticket_cb = cb;
conn->session_ticket_cb_ctx = ctx;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,901 @@
/*
* TLSv1 client - write handshake message
* Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "wpa/includes.h"
#include "wpa/common.h"
#include "crypto/md5.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
#include "crypto/random.h"
#include "wpa2/tls/tls.h"
#include "wpa2/tls/x509v3.h"
#include "wpa2/tls/tlsv1_common.h"
#include "wpa2/tls/tlsv1_record.h"
#include "wpa2/tls/tlsv1_client.h"
#include "wpa2/tls/tlsv1_client_i.h"
#include "wpa2/eap_peer/eap_i.h"
static size_t tls_client_cert_chain_der_len(struct tlsv1_client *conn)
{
size_t len = 0;
struct x509_certificate *cert;
if (conn->cred == NULL)
return 0;
cert = conn->cred->cert;
while (cert) {
len += 3 + cert->cert_len;
if (x509_certificate_self_signed(cert))
break;
cert = x509_certificate_get_subject(conn->cred->trusted_certs,
&cert->issuer);
}
return len;
}
u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len)
{
u8 *hello, *end, *pos, *hs_length, *hs_start, *rhdr;
struct os_time now;
size_t len, i;
wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello");
*out_len = 0;
os_get_time(&now);
WPA_PUT_BE32(conn->client_random, now.sec);
if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) {
wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
"client_random");
return NULL;
}
wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random",
conn->client_random, TLS_RANDOM_LEN);
len = 100 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len;
hello = os_malloc(len);
if (hello == NULL)
return NULL;
end = hello + len;
rhdr = hello;
pos = rhdr + TLS_RECORD_HEADER_LEN;
/* opaque fragment[TLSPlaintext.length] */
/* Handshake */
hs_start = pos;
/* HandshakeType msg_type */
*pos++ = TLS_HANDSHAKE_TYPE_CLIENT_HELLO;
/* uint24 length (to be filled) */
hs_length = pos;
pos += 3;
/* body - ClientHello */
/* ProtocolVersion client_version */
WPA_PUT_BE16(pos, TLS_VERSION);
pos += 2;
/* Random random: uint32 gmt_unix_time, opaque random_bytes */
os_memcpy(pos, conn->client_random, TLS_RANDOM_LEN);
pos += TLS_RANDOM_LEN;
/* SessionID session_id */
*pos++ = conn->session_id_len;
os_memcpy(pos, conn->session_id, conn->session_id_len);
pos += conn->session_id_len;
/* CipherSuite cipher_suites<2..2^16-1> */
WPA_PUT_BE16(pos, 2 * conn->num_cipher_suites);
pos += 2;
for (i = 0; i < conn->num_cipher_suites; i++) {
WPA_PUT_BE16(pos, conn->cipher_suites[i]);
pos += 2;
}
/* CompressionMethod compression_methods<1..2^8-1> */
*pos++ = 1;
*pos++ = TLS_COMPRESSION_NULL;
if (conn->client_hello_ext) {
os_memcpy(pos, conn->client_hello_ext,
conn->client_hello_ext_len);
pos += conn->client_hello_ext_len;
}
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
rhdr, end - rhdr, hs_start, pos - hs_start,
out_len) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
os_free(hello);
return NULL;
}
conn->state = SERVER_HELLO;
return hello;
}
static int tls_write_client_certificate(struct tlsv1_client *conn,
u8 **msgpos, u8 *end)
{
u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start;
size_t rlen;
struct x509_certificate *cert;
pos = *msgpos;
wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate");
rhdr = pos;
pos += TLS_RECORD_HEADER_LEN;
/* opaque fragment[TLSPlaintext.length] */
/* Handshake */
hs_start = pos;
/* HandshakeType msg_type */
*pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE;
/* uint24 length (to be filled) */
hs_length = pos;
pos += 3;
/* body - Certificate */
/* uint24 length (to be filled) */
cert_start = pos;
pos += 3;
cert = conn->cred ? conn->cred->cert : NULL;
while (cert) {
if (pos + 3 + cert->cert_len > end) {
wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space "
"for Certificate (cert_len=%lu left=%lu)",
(unsigned long) cert->cert_len,
(unsigned long) (end - pos));
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
WPA_PUT_BE24(pos, cert->cert_len);
pos += 3;
os_memcpy(pos, cert->cert_start, cert->cert_len);
pos += cert->cert_len;
if (x509_certificate_self_signed(cert))
break;
cert = x509_certificate_get_subject(conn->cred->trusted_certs,
&cert->issuer);
}
if (conn->cred == NULL || cert == conn->cred->cert || cert == NULL) {
/*
* Client was not configured with all the needed certificates
* to form a full certificate chain. The server may fail to
* validate the chain unless it is configured with all the
* missing CA certificates.
*/
wpa_printf(MSG_DEBUG, "TLSv1: Full client certificate chain "
"not configured - validation may fail");
}
WPA_PUT_BE24(cert_start, pos - cert_start - 3);
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
rhdr, end - rhdr, hs_start, pos - hs_start,
&rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
pos = rhdr + rlen;
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
*msgpos = pos;
return 0;
}
static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end)
{
/* ClientDiffieHellmanPublic */
u8 *csecret, *csecret_start, *dh_yc, *shared;
size_t csecret_len, dh_yc_len, shared_len;
csecret_len = conn->dh_p_len;
csecret = os_malloc(csecret_len);
if (csecret == NULL) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
"memory for Yc (Diffie-Hellman)");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
if (random_get_bytes(csecret, csecret_len)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
"data for Diffie-Hellman");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
os_free(csecret);
return -1;
}
if (os_memcmp(csecret, conn->dh_p, csecret_len) > 0)
csecret[0] = 0; /* make sure Yc < p */
csecret_start = csecret;
while (csecret_len > 1 && *csecret_start == 0) {
csecret_start++;
csecret_len--;
}
wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH client's secret value",
csecret_start, csecret_len);
/* Yc = g^csecret mod p */
dh_yc_len = conn->dh_p_len;
dh_yc = os_malloc(dh_yc_len);
if (dh_yc == NULL) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
"memory for Diffie-Hellman");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
os_free(csecret);
return -1;
}
if (wpa2_crypto_funcs.crypto_mod_exp) {
if(wpa2_crypto_funcs.crypto_mod_exp(conn->dh_g, conn->dh_g_len,
csecret_start, csecret_len,
conn->dh_p, conn->dh_p_len,
dh_yc, &dh_yc_len)) {
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
os_free(csecret);
os_free(dh_yc);
return -1;
}
} else {
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
os_free(csecret);
os_free(dh_yc);
wpa_printf(MSG_ERROR, "Fail to register crypto_mod_exp function!\r\n");
return -1;
}
wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)",
dh_yc, dh_yc_len);
WPA_PUT_BE16(*pos, dh_yc_len);
*pos += 2;
if (*pos + dh_yc_len > end) {
wpa_printf(MSG_DEBUG, "TLSv1: Not enough room in the "
"message buffer for Yc");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
os_free(csecret);
os_free(dh_yc);
return -1;
}
os_memcpy(*pos, dh_yc, dh_yc_len);
*pos += dh_yc_len;
os_free(dh_yc);
shared_len = conn->dh_p_len;
shared = os_malloc(shared_len);
if (shared == NULL) {
wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for "
"DH");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
os_free(csecret);
return -1;
}
/* shared = Ys^csecret mod p */
if (wpa2_crypto_funcs.crypto_mod_exp) {
if(wpa2_crypto_funcs.crypto_mod_exp(conn->dh_ys, conn->dh_ys_len,
csecret_start, csecret_len,
conn->dh_p, conn->dh_p_len,
shared, &shared_len)) {
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
os_free(csecret);
os_free(shared);
return -1;
}
} else {
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
os_free(csecret);
os_free(shared);
wpa_printf(MSG_ERROR, "Fail to register crypto_mod_exp function!\r\n");
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange",
shared, shared_len);
os_memset(csecret_start, 0, csecret_len);
os_free(csecret);
if (tls_derive_keys(conn, shared, shared_len)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
os_free(shared);
return -1;
}
os_memset(shared, 0, shared_len);
os_free(shared);
tlsv1_client_free_dh(conn);
return 0;
}
static int tlsv1_key_x_rsa(struct tlsv1_client *conn, u8 **pos, u8 *end)
{
u8 pre_master_secret[TLS_PRE_MASTER_SECRET_LEN];
size_t clen;
int res;
if (tls_derive_pre_master_secret(pre_master_secret) < 0 ||
tls_derive_keys(conn, pre_master_secret,
TLS_PRE_MASTER_SECRET_LEN)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
/* EncryptedPreMasterSecret */
if (conn->server_rsa_key == NULL) {
wpa_printf(MSG_DEBUG, "TLSv1: No server RSA key to "
"use for encrypting pre-master secret");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
/* RSA encrypted value is encoded with PKCS #1 v1.5 block type 2. */
*pos += 2;
clen = end - *pos;
res = crypto_public_key_encrypt_pkcs1_v15(
conn->server_rsa_key,
pre_master_secret, TLS_PRE_MASTER_SECRET_LEN,
*pos, &clen);
os_memset(pre_master_secret, 0, TLS_PRE_MASTER_SECRET_LEN);
if (res < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: RSA encryption failed");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
WPA_PUT_BE16(*pos - 2, clen);
wpa_hexdump(MSG_MSGDUMP, "TLSv1: Encrypted pre_master_secret",
*pos, clen);
*pos += clen;
return 0;
}
static int tls_write_client_key_exchange(struct tlsv1_client *conn,
u8 **msgpos, u8 *end)
{
u8 *pos, *rhdr, *hs_start, *hs_length;
size_t rlen;
tls_key_exchange keyx;
const struct tls_cipher_suite *suite;
suite = tls_get_cipher_suite(conn->rl.cipher_suite);
if (suite == NULL)
keyx = TLS_KEY_X_NULL;
else
keyx = suite->key_exchange;
pos = *msgpos;
wpa_printf(MSG_DEBUG, "TLSv1: Send ClientKeyExchange");
rhdr = pos;
pos += TLS_RECORD_HEADER_LEN;
/* opaque fragment[TLSPlaintext.length] */
/* Handshake */
hs_start = pos;
/* HandshakeType msg_type */
*pos++ = TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE;
/* uint24 length (to be filled) */
hs_length = pos;
pos += 3;
/* body - ClientKeyExchange */
if (keyx == TLS_KEY_X_DH_anon) {
if (tlsv1_key_x_anon_dh(conn, &pos, end) < 0)
return -1;
} else {
if (tlsv1_key_x_rsa(conn, &pos, end) < 0)
return -1;
}
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
rhdr, end - rhdr, hs_start, pos - hs_start,
&rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
pos = rhdr + rlen;
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
*msgpos = pos;
return 0;
}
static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
u8 **msgpos, u8 *end)
{
u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start;
size_t rlen, hlen, clen;
u8 hash[100], *hpos;
enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
pos = *msgpos;
wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateVerify");
rhdr = pos;
pos += TLS_RECORD_HEADER_LEN;
/* Handshake */
hs_start = pos;
/* HandshakeType msg_type */
*pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY;
/* uint24 length (to be filled) */
hs_length = pos;
pos += 3;
/*
* RFC 2246: 7.4.3 and 7.4.8:
* Signature signature
*
* RSA:
* digitally-signed struct {
* opaque md5_hash[16];
* opaque sha_hash[20];
* };
*
* DSA:
* digitally-signed struct {
* opaque sha_hash[20];
* };
*
* The hash values are calculated over all handshake messages sent or
* received starting at ClientHello up to, but not including, this
* CertificateVerify message, including the type and length fields of
* the handshake messages.
*/
hpos = hash;
#ifdef CONFIG_TLSV12
if (conn->rl.tls_version == TLS_VERSION_1_2) {
hlen = SHA256_MAC_LEN;
if (wpa2_crypto_funcs.crypto_hash_finish) {
if (conn->verify.sha256_cert == NULL ||
wpa2_crypto_funcs.crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) <
0) {
conn->verify.sha256_cert = NULL;
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
} else {
conn->verify.sha256_cert = NULL;
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__);
return -1;
}
conn->verify.sha256_cert = NULL;
/*
* RFC 3447, A.2.4 RSASSA-PKCS1-v1_5
*
* DigestInfo ::= SEQUENCE {
* digestAlgorithm DigestAlgorithm,
* digest OCTET STRING
* }
*
* SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11}
*
* DER encoded DigestInfo for SHA256 per RFC 3447:
* 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 ||
* H
*/
os_memmove(hash + 19, hash, hlen);
hlen += 19;
os_memcpy(hash, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65"
"\x03\x04\x02\x01\x05\x00\x04\x20", 19);
} else {
#endif /* CONFIG_TLSV12 */
if (alg == SIGN_ALG_RSA) {
hlen = MD5_MAC_LEN;
if (conn->verify.md5_cert == NULL ||
crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0)
{
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
conn->verify.md5_cert = NULL;
crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
conn->verify.sha1_cert = NULL;
return -1;
}
hpos += MD5_MAC_LEN;
} else
crypto_hash_finish(conn->verify.md5_cert, NULL, NULL);
conn->verify.md5_cert = NULL;
hlen = SHA1_MAC_LEN;
if (conn->verify.sha1_cert == NULL ||
crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) {
conn->verify.sha1_cert = NULL;
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
conn->verify.sha1_cert = NULL;
if (alg == SIGN_ALG_RSA)
hlen += MD5_MAC_LEN;
#ifdef CONFIG_TLSV12
}
#endif /* CONFIG_TLSV12 */
wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen);
#ifdef CONFIG_TLSV12
if (conn->rl.tls_version >= TLS_VERSION_1_2) {
/*
* RFC 5246, 4.7:
* TLS v1.2 adds explicit indication of the used signature and
* hash algorithms.
*
* struct {
* HashAlgorithm hash;
* SignatureAlgorithm signature;
* } SignatureAndHashAlgorithm;
*/
*pos++ = TLS_HASH_ALG_SHA256;
*pos++ = TLS_SIGN_ALG_RSA;
}
#endif /* CONFIG_TLSV12 */
/*
* RFC 2246, 4.7:
* In digital signing, one-way hash functions are used as input for a
* signing algorithm. A digitally-signed element is encoded as an
* opaque vector <0..2^16-1>, where the length is specified by the
* signing algorithm and key.
*
* In RSA signing, a 36-byte structure of two hashes (one SHA and one
* MD5) is signed (encrypted with the private key). It is encoded with
* PKCS #1 block type 0 or type 1 as described in [PKCS1].
*/
signed_start = pos; /* length to be filled */
pos += 2;
clen = end - pos;
if (conn->cred == NULL ||
crypto_private_key_sign_pkcs1(conn->cred->key, hash, hlen,
pos, &clen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to sign hash (PKCS #1)");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
WPA_PUT_BE16(signed_start, clen);
pos += clen;
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
rhdr, end - rhdr, hs_start, pos - hs_start,
&rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
pos = rhdr + rlen;
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
*msgpos = pos;
return 0;
}
static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn,
u8 **msgpos, u8 *end)
{
size_t rlen;
u8 payload[1];
wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
payload[0] = TLS_CHANGE_CIPHER_SPEC;
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
*msgpos, end - *msgpos, payload, sizeof(payload),
&rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
if (tlsv1_record_change_write_cipher(&conn->rl) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for "
"record layer");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
*msgpos += rlen;
return 0;
}
static int tls_write_client_finished(struct tlsv1_client *conn,
u8 **msgpos, u8 *end)
{
u8 *pos, *hs_start;
size_t rlen, hlen;
u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN];
u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
wpa_printf(MSG_DEBUG, "TLSv1: Send Finished");
/* Encrypted Handshake Message: Finished */
#ifdef CONFIG_TLSV12
if (conn->rl.tls_version >= TLS_VERSION_1_2) {
hlen = SHA256_MAC_LEN;
if (wpa2_crypto_funcs.crypto_hash_finish) {
if (conn->verify.sha256_client == NULL ||
wpa2_crypto_funcs.crypto_hash_finish(conn->verify.sha256_client, hash, &hlen)
< 0) {
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
conn->verify.sha256_client = NULL;
return -1;
}
} else {
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
conn->verify.sha256_client = NULL;
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__);
return -1;
}
conn->verify.sha256_client = NULL;
} else {
#endif /* CONFIG_TLSV12 */
hlen = MD5_MAC_LEN;
if (conn->verify.md5_client == NULL ||
crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) {
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
conn->verify.md5_client = NULL;
crypto_hash_finish(conn->verify.sha1_client, NULL, NULL);
conn->verify.sha1_client = NULL;
return -1;
}
conn->verify.md5_client = NULL;
hlen = SHA1_MAC_LEN;
if (conn->verify.sha1_client == NULL ||
crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN,
&hlen) < 0) {
conn->verify.sha1_client = NULL;
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
conn->verify.sha1_client = NULL;
hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
#ifdef CONFIG_TLSV12
}
#endif /* CONFIG_TLSV12 */
if (tls_prf(conn->rl.tls_version,
conn->master_secret, TLS_MASTER_SECRET_LEN,
"client finished", hash, hlen,
verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)",
verify_data + 1 + 3, TLS_VERIFY_DATA_LEN);
/* Handshake */
pos = hs_start = verify_data;
/* HandshakeType msg_type */
*pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
/* uint24 length */
WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN);
pos += 3;
pos += TLS_VERIFY_DATA_LEN; /* verify_data already in place */
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
*msgpos, end - *msgpos, hs_start, pos - hs_start,
&rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
*msgpos += rlen;
return 0;
}
static u8 * tls_send_client_key_exchange(struct tlsv1_client *conn,
size_t *out_len)
{
u8 *msg, *end, *pos;
size_t msglen;
*out_len = 0;
msglen = 2000;
if (conn->certificate_requested)
msglen += tls_client_cert_chain_der_len(conn);
msg = os_malloc(msglen);
if (msg == NULL)
return NULL;
pos = msg;
end = msg + msglen;
if (conn->certificate_requested) {
if (tls_write_client_certificate(conn, &pos, end) < 0) {
os_free(msg);
return NULL;
}
}
if (tls_write_client_key_exchange(conn, &pos, end) < 0 ||
(conn->certificate_requested && conn->cred && conn->cred->key &&
tls_write_client_certificate_verify(conn, &pos, end) < 0) ||
tls_write_client_change_cipher_spec(conn, &pos, end) < 0 ||
tls_write_client_finished(conn, &pos, end) < 0) {
os_free(msg);
return NULL;
}
*out_len = pos - msg;
conn->state = SERVER_CHANGE_CIPHER_SPEC;
return msg;
}
static u8 * tls_send_change_cipher_spec(struct tlsv1_client *conn,
size_t *out_len)
{
u8 *msg, *end, *pos;
*out_len = 0;
msg = os_malloc(1000);
if (msg == NULL)
return NULL;
pos = msg;
end = msg + 1000;
if (tls_write_client_change_cipher_spec(conn, &pos, end) < 0 ||
tls_write_client_finished(conn, &pos, end) < 0) {
os_free(msg);
return NULL;
}
*out_len = pos - msg;
wpa_printf(MSG_DEBUG, "TLSv1: Session resumption completed "
"successfully");
conn->state = ESTABLISHED;
return msg;
}
u8 * tlsv1_client_handshake_write(struct tlsv1_client *conn, size_t *out_len,
int no_appl_data)
{
switch (conn->state) {
case CLIENT_KEY_EXCHANGE:
return tls_send_client_key_exchange(conn, out_len);
case CHANGE_CIPHER_SPEC:
return tls_send_change_cipher_spec(conn, out_len);
case ACK_FINISHED:
wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed "
"successfully");
conn->state = ESTABLISHED;
*out_len = 0;
if (no_appl_data) {
/* Need to return something to get final TLS ACK. */
return os_malloc(1);
}
return NULL;
default:
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while "
"generating reply", conn->state);
return NULL;
}
}
u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level,
u8 description, size_t *out_len)
{
u8 *alert, *pos, *length;
wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description);
*out_len = 0;
alert = os_malloc(10);
if (alert == NULL)
return NULL;
pos = alert;
/* TLSPlaintext */
/* ContentType type */
*pos++ = TLS_CONTENT_TYPE_ALERT;
/* ProtocolVersion version */
WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version :
TLS_VERSION);
pos += 2;
/* uint16 length (to be filled) */
length = pos;
pos += 2;
/* opaque fragment[TLSPlaintext.length] */
/* Alert */
/* AlertLevel level */
*pos++ = level;
/* AlertDescription description */
*pos++ = description;
WPA_PUT_BE16(length, pos - length - 2);
*out_len = pos - alert;
return alert;
}

View File

@ -0,0 +1,337 @@
/*
* TLSv1 common routines
* Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "wpa/includes.h"
#include "wpa/common.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
#include "wpa2/tls/tls.h"
#include "wpa2/tls/x509v3.h"
#include "wpa2/tls/tlsv1_common.h"
#include "wpa2/eap_peer/eap_i.h"
/*
* TODO:
* RFC 2246 Section 9: Mandatory to implement TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
* Add support for commonly used cipher suites; don't bother with exportable
* suites.
*/
static const struct tls_cipher_suite tls_cipher_suites[] = {
{ TLS_NULL_WITH_NULL_NULL, TLS_KEY_X_NULL, TLS_CIPHER_NULL,
TLS_HASH_NULL },
{ TLS_RSA_WITH_RC4_128_MD5, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128,
TLS_HASH_MD5 },
{ TLS_RSA_WITH_RC4_128_SHA, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128,
TLS_HASH_SHA },
{ TLS_RSA_WITH_DES_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_DES_CBC,
TLS_HASH_SHA },
{ TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_RSA,
TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
{ TLS_DH_anon_WITH_RC4_128_MD5, TLS_KEY_X_DH_anon,
TLS_CIPHER_RC4_128, TLS_HASH_MD5 },
{ TLS_DH_anon_WITH_DES_CBC_SHA, TLS_KEY_X_DH_anon,
TLS_CIPHER_DES_CBC, TLS_HASH_SHA },
{ TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_DH_anon,
TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
{ TLS_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_128_CBC,
TLS_HASH_SHA },
{ TLS_DH_anon_WITH_AES_128_CBC_SHA, TLS_KEY_X_DH_anon,
TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA },
{ TLS_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC,
TLS_HASH_SHA },
{ TLS_DH_anon_WITH_AES_256_CBC_SHA, TLS_KEY_X_DH_anon,
TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA },
{ TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_RSA,
TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
{ TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_RSA,
TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 },
{ TLS_DH_anon_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DH_anon,
TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
{ TLS_DH_anon_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DH_anon,
TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 }
};
#define NUM_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
#define NUM_TLS_CIPHER_SUITES NUM_ELEMS(tls_cipher_suites)
static const struct tls_cipher_data tls_ciphers[] = {
{ TLS_CIPHER_NULL, TLS_CIPHER_STREAM, 0, 0, 0,
CRYPTO_CIPHER_NULL },
{ TLS_CIPHER_IDEA_CBC, TLS_CIPHER_BLOCK, 16, 16, 8,
CRYPTO_CIPHER_NULL },
{ TLS_CIPHER_RC2_CBC_40, TLS_CIPHER_BLOCK, 5, 16, 0,
CRYPTO_CIPHER_ALG_RC2 },
{ TLS_CIPHER_RC4_40, TLS_CIPHER_STREAM, 5, 16, 0,
CRYPTO_CIPHER_ALG_RC4 },
{ TLS_CIPHER_RC4_128, TLS_CIPHER_STREAM, 16, 16, 0,
CRYPTO_CIPHER_ALG_RC4 },
{ TLS_CIPHER_DES40_CBC, TLS_CIPHER_BLOCK, 5, 8, 8,
CRYPTO_CIPHER_ALG_DES },
{ TLS_CIPHER_DES_CBC, TLS_CIPHER_BLOCK, 8, 8, 8,
CRYPTO_CIPHER_ALG_DES },
{ TLS_CIPHER_3DES_EDE_CBC, TLS_CIPHER_BLOCK, 24, 24, 8,
CRYPTO_CIPHER_ALG_3DES },
{ TLS_CIPHER_AES_128_CBC, TLS_CIPHER_BLOCK, 16, 16, 16,
CRYPTO_CIPHER_ALG_AES },
{ TLS_CIPHER_AES_256_CBC, TLS_CIPHER_BLOCK, 32, 32, 16,
CRYPTO_CIPHER_ALG_AES }
};
#define NUM_TLS_CIPHER_DATA NUM_ELEMS(tls_ciphers)
/**
* tls_get_cipher_suite - Get TLS cipher suite
* @suite: Cipher suite identifier
* Returns: Pointer to the cipher data or %NULL if not found
*/
const struct tls_cipher_suite * tls_get_cipher_suite(u16 suite)
{
size_t i;
for (i = 0; i < NUM_TLS_CIPHER_SUITES; i++)
if (tls_cipher_suites[i].suite == suite)
return &tls_cipher_suites[i];
return NULL;
}
const struct tls_cipher_data * tls_get_cipher_data(tls_cipher cipher)
{
size_t i;
for (i = 0; i < NUM_TLS_CIPHER_DATA; i++)
if (tls_ciphers[i].cipher == cipher)
return &tls_ciphers[i];
return NULL;
}
int tls_server_key_exchange_allowed(tls_cipher cipher)
{
const struct tls_cipher_suite *suite;
/* RFC 2246, Section 7.4.3 */
suite = tls_get_cipher_suite(cipher);
if (suite == NULL)
return 0;
switch (suite->key_exchange) {
case TLS_KEY_X_DHE_DSS:
case TLS_KEY_X_DHE_DSS_EXPORT:
case TLS_KEY_X_DHE_RSA:
case TLS_KEY_X_DHE_RSA_EXPORT:
case TLS_KEY_X_DH_anon_EXPORT:
case TLS_KEY_X_DH_anon:
return 1;
case TLS_KEY_X_RSA_EXPORT:
return 1 /* FIX: public key len > 512 bits */;
default:
return 0;
}
}
/**
* tls_parse_cert - Parse DER encoded X.509 certificate and get public key
* @buf: ASN.1 DER encoded certificate
* @len: Length of the buffer
* @pk: Buffer for returning the allocated public key
* Returns: 0 on success, -1 on failure
*
* This functions parses an ASN.1 DER encoded X.509 certificate and retrieves
* the public key from it. The caller is responsible for freeing the public key
* by calling crypto_public_key_free().
*/
int tls_parse_cert(const u8 *buf, size_t len, struct crypto_public_key **pk)
{
struct x509_certificate *cert;
wpa_hexdump(MSG_MSGDUMP, "TLSv1: Parse ASN.1 DER certificate",
buf, len);
*pk = crypto_public_key_from_cert(buf, len);
if (*pk)
return 0;
cert = x509_certificate_parse(buf, len);
if (cert == NULL) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse X.509 "
"certificate");
return -1;
}
/* TODO
* verify key usage (must allow encryption)
*
* All certificate profiles, key and cryptographic formats are
* defined by the IETF PKIX working group [PKIX]. When a key
* usage extension is present, the digitalSignature bit must be
* set for the key to be eligible for signing, as described
* above, and the keyEncipherment bit must be present to allow
* encryption, as described above. The keyAgreement bit must be
* set on Diffie-Hellman certificates. (PKIX: RFC 3280)
*/
*pk = crypto_public_key_import(cert->public_key, cert->public_key_len);
x509_certificate_free(cert);
if (*pk == NULL) {
wpa_printf(MSG_ERROR, "TLSv1: Failed to import "
"server public key");
return -1;
}
return 0;
}
int tls_verify_hash_init(struct tls_verify_hash *verify)
{
tls_verify_hash_free(verify);
verify->md5_client = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
verify->md5_server = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
verify->md5_cert = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
verify->sha1_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
verify->sha1_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
verify->sha1_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
if (verify->md5_client == NULL || verify->md5_server == NULL ||
verify->md5_cert == NULL || verify->sha1_client == NULL ||
verify->sha1_server == NULL || verify->sha1_cert == NULL) {
tls_verify_hash_free(verify);
return -1;
}
#ifdef CONFIG_TLSV12
if (wpa2_crypto_funcs.crypto_hash_init) {
verify->sha256_client = wpa2_crypto_funcs.crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0);
verify->sha256_server = wpa2_crypto_funcs.crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0);
verify->sha256_cert = wpa2_crypto_funcs.crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0);
} else {
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash init function!\r\n", __FUNCTION__);
return -1;
}
if (verify->sha256_client == NULL ||
verify->sha256_server == NULL ||
verify->sha256_cert == NULL) {
tls_verify_hash_free(verify);
return -1;
}
#endif /* CONFIG_TLSV12 */
return 0;
}
void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf,
size_t len)
{
if (verify->md5_client && verify->sha1_client) {
crypto_hash_update(verify->md5_client, buf, len);
crypto_hash_update(verify->sha1_client, buf, len);
}
if (verify->md5_server && verify->sha1_server) {
crypto_hash_update(verify->md5_server, buf, len);
crypto_hash_update(verify->sha1_server, buf, len);
}
if (verify->md5_cert && verify->sha1_cert) {
crypto_hash_update(verify->md5_cert, buf, len);
crypto_hash_update(verify->sha1_cert, buf, len);
}
#ifdef CONFIG_TLSV12
if (wpa2_crypto_funcs.crypto_hash_update) {
if (verify->sha256_client)
wpa2_crypto_funcs.crypto_hash_update(verify->sha256_client, buf, len);
if (verify->sha256_server)
wpa2_crypto_funcs.crypto_hash_update(verify->sha256_server, buf, len);
if (verify->sha256_cert)
wpa2_crypto_funcs.crypto_hash_update(verify->sha256_cert, buf, len);
} else {
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash update function!\r\n", __FUNCTION__);
return;
}
#endif /* CONFIG_TLSV12 */
}
void tls_verify_hash_free(struct tls_verify_hash *verify)
{
crypto_hash_finish(verify->md5_client, NULL, NULL);
crypto_hash_finish(verify->md5_server, NULL, NULL);
crypto_hash_finish(verify->md5_cert, NULL, NULL);
crypto_hash_finish(verify->sha1_client, NULL, NULL);
crypto_hash_finish(verify->sha1_server, NULL, NULL);
crypto_hash_finish(verify->sha1_cert, NULL, NULL);
verify->md5_client = NULL;
verify->md5_server = NULL;
verify->md5_cert = NULL;
verify->sha1_client = NULL;
verify->sha1_server = NULL;
verify->sha1_cert = NULL;
#ifdef CONFIG_TLSV12
if (wpa2_crypto_funcs.crypto_hash_finish) {
wpa2_crypto_funcs.crypto_hash_finish(verify->sha256_client, NULL, NULL);
wpa2_crypto_funcs.crypto_hash_finish(verify->sha256_server, NULL, NULL);
wpa2_crypto_funcs.crypto_hash_finish(verify->sha256_cert, NULL, NULL);
verify->sha256_client = NULL;
verify->sha256_server = NULL;
verify->sha256_cert = NULL;
} else {
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash finish function!\r\n", __FUNCTION__);
return;
}
#endif /* CONFIG_TLSV12 */
}
int tls_version_ok(u16 ver)
{
if (ver == TLS_VERSION_1)
return 1;
#ifdef CONFIG_TLSV11
if (ver == TLS_VERSION_1_1)
return 1;
#endif /* CONFIG_TLSV11 */
#ifdef CONFIG_TLSV12
if (ver == TLS_VERSION_1_2)
return 1;
#endif /* CONFIG_TLSV12 */
return 0;
}
const char * tls_version_str(u16 ver)
{
switch (ver) {
case TLS_VERSION_1:
return "1.0";
case TLS_VERSION_1_1:
return "1.1";
case TLS_VERSION_1_2:
return "1.2";
}
return "?";
}
int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label,
const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
{
#ifdef CONFIG_TLSV12
if (ver >= TLS_VERSION_1_2) {
tls_prf_sha256(secret, secret_len, label, seed, seed_len,
out, outlen);
return 0;
}
#endif /* CONFIG_TLSV12 */
return tls_prf_sha1_md5(secret, secret_len, label, seed, seed_len, out,
outlen);
}

View File

@ -0,0 +1,505 @@
/*
* TLSv1 credentials
* Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "wpa/includes.h"
#include "wpa/common.h"
#include "wpa2/utils/base64.h"
#include "crypto/crypto.h"
#include "wpa2/tls/x509v3.h"
#include "wpa2/tls/tlsv1_cred.h"
struct tlsv1_credentials * tlsv1_cred_alloc(void)
{
struct tlsv1_credentials *cred;
cred = (struct tlsv1_credentials *)os_zalloc(sizeof(*cred));
return cred;
}
void tlsv1_cred_free(struct tlsv1_credentials *cred)
{
if (cred == NULL)
return;
x509_certificate_chain_free(cred->trusted_certs);
x509_certificate_chain_free(cred->cert);
crypto_private_key_free(cred->key);
os_free(cred->dh_p);
os_free(cred->dh_g);
os_free(cred);
}
static int tlsv1_add_cert_der(struct x509_certificate **chain,
const u8 *buf, size_t len)
{
struct x509_certificate *cert, *p;
char name[128];
cert = x509_certificate_parse(buf, len);
if (cert == NULL) {
wpa_printf(MSG_INFO, "TLSv1: %s - failed to parse certificate",
__func__);
return -1;
}
p = *chain;
while (p && p->next)
p = p->next;
if (p && x509_name_compare(&cert->subject, &p->issuer) == 0) {
/*
* The new certificate is the issuer of the last certificate in
* the chain - add the new certificate to the end.
*/
p->next = cert;
} else {
/* Add to the beginning of the chain */
cert->next = *chain;
*chain = cert;
}
x509_name_string(&cert->subject, name, sizeof(name));
wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name);
return 0;
}
static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----";
static const char *pem_cert_end = "-----END CERTIFICATE-----";
static const char *pem_key_begin = "-----BEGIN RSA PRIVATE KEY-----";
static const char *pem_key_end = "-----END RSA PRIVATE KEY-----";
static const char *pem_key2_begin = "-----BEGIN PRIVATE KEY-----";
static const char *pem_key2_end = "-----END PRIVATE KEY-----";
static const char *pem_key_enc_begin = "-----BEGIN ENCRYPTED PRIVATE KEY-----";
static const char *pem_key_enc_end = "-----END ENCRYPTED PRIVATE KEY-----";
static const u8 * search_tag(const char *tag, const u8 *buf, size_t len)
{
size_t i, plen;
plen = os_strlen(tag);
if (len < plen)
return NULL;
for (i = 0; i < len - plen; i++) {
if (os_memcmp(buf + i, tag, plen) == 0)
return buf + i;
}
return NULL;
}
static int tlsv1_add_cert(struct x509_certificate **chain,
const u8 *buf, size_t len)
{
const u8 *pos, *end;
unsigned char *der;
size_t der_len;
pos = search_tag(pem_cert_begin, buf, len);
if (!pos) {
wpa_printf(MSG_DEBUG, "TLSv1: No PEM certificate tag found - "
"assume DER format");
return tlsv1_add_cert_der(chain, buf, len);
}
wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format certificate into "
"DER format");
while (pos) {
pos += os_strlen(pem_cert_begin);
end = search_tag(pem_cert_end, pos, buf + len - pos);
if (end == NULL) {
wpa_printf(MSG_INFO, "TLSv1: Could not find PEM "
"certificate end tag (%s)", pem_cert_end);
return -1;
}
der = base64_decode(pos, end - pos, &der_len);
if (der == NULL) {
wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM "
"certificate");
return -1;
}
if (tlsv1_add_cert_der(chain, der, der_len) < 0) {
wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM "
"certificate after DER conversion");
os_free(der);
return -1;
}
os_free(der);
end += os_strlen(pem_cert_end);
pos = search_tag(pem_cert_begin, end, buf + len - end);
}
return 0;
}
static int tlsv1_set_cert_chain(struct x509_certificate **chain,
const char *cert, const u8 *cert_blob,
size_t cert_blob_len)
{
if (cert_blob)
return tlsv1_add_cert(chain, cert_blob, cert_blob_len);
if (cert) {
u8 *buf = NULL;
size_t len;
int ret;
//buf = (u8 *) os_readfile(cert, &len);
if (buf == NULL) {
wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
cert);
return -1;
}
ret = tlsv1_add_cert(chain, buf, len);
os_free(buf);
return ret;
}
return 0;
}
/**
* tlsv1_set_ca_cert - Set trusted CA certificate(s)
* @cred: TLSv1 credentials from tlsv1_cred_alloc()
* @cert: File or reference name for X.509 certificate in PEM or DER format
* @cert_blob: cert as inlined data or %NULL if not used
* @cert_blob_len: ca_cert_blob length
* @path: Path to CA certificates (not yet supported)
* Returns: 0 on success, -1 on failure
*/
int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert,
const u8 *cert_blob, size_t cert_blob_len,
const char *path)
{
if (tlsv1_set_cert_chain(&cred->trusted_certs, cert,
cert_blob, cert_blob_len) < 0)
return -1;
if (path) {
/* TODO: add support for reading number of certificate files */
wpa_printf(MSG_INFO, "TLSv1: Use of CA certificate directory "
"not yet supported");
return -1;
}
return 0;
}
/**
* tlsv1_set_cert - Set certificate
* @cred: TLSv1 credentials from tlsv1_cred_alloc()
* @cert: File or reference name for X.509 certificate in PEM or DER format
* @cert_blob: cert as inlined data or %NULL if not used
* @cert_blob_len: cert_blob length
* Returns: 0 on success, -1 on failure
*/
int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert,
const u8 *cert_blob, size_t cert_blob_len)
{
return tlsv1_set_cert_chain(&cred->cert, cert,
cert_blob, cert_blob_len);
}
static struct crypto_private_key * tlsv1_set_key_pem(const u8 *key, size_t len)
{
const u8 *pos, *end;
unsigned char *der;
size_t der_len;
struct crypto_private_key *pkey;
pos = search_tag(pem_key_begin, key, len);
if (!pos) {
pos = search_tag(pem_key2_begin, key, len);
if (!pos)
return NULL;
pos += os_strlen(pem_key2_begin);
end = search_tag(pem_key2_end, pos, key + len - pos);
if (!end)
return NULL;
} else {
const u8 *pos2;
pos += os_strlen(pem_key_begin);
end = search_tag(pem_key_end, pos, key + len - pos);
if (!end)
return NULL;
pos2 = search_tag("Proc-Type: 4,ENCRYPTED", pos, end - pos);
if (pos2) {
wpa_printf(MSG_DEBUG, "TLSv1: Unsupported private key "
"format (Proc-Type/DEK-Info)");
return NULL;
}
}
der = base64_decode(pos, end - pos, &der_len);
if (!der)
return NULL;
pkey = crypto_private_key_import(der, der_len, NULL);
os_free(der);
return pkey;
}
static struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key,
size_t len,
const char *passwd)
{
const u8 *pos, *end;
unsigned char *der;
size_t der_len;
struct crypto_private_key *pkey;
if (passwd == NULL)
return NULL;
pos = search_tag(pem_key_enc_begin, key, len);
if (!pos)
return NULL;
pos += os_strlen(pem_key_enc_begin);
end = search_tag(pem_key_enc_end, pos, key + len - pos);
if (!end)
return NULL;
der = base64_decode(pos, end - pos, &der_len);
if (!der)
return NULL;
pkey = crypto_private_key_import(der, der_len, passwd);
os_free(der);
return pkey;
}
static int tlsv1_set_key(struct tlsv1_credentials *cred,
const u8 *key, size_t len, const char *passwd)
{
cred->key = crypto_private_key_import(key, len, passwd);
if (cred->key == NULL)
cred->key = tlsv1_set_key_pem(key, len);
if (cred->key == NULL)
cred->key = tlsv1_set_key_enc_pem(key, len, passwd);
if (cred->key == NULL) {
wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key");
return -1;
}
return 0;
}
/**
* tlsv1_set_private_key - Set private key
* @cred: TLSv1 credentials from tlsv1_cred_alloc()
* @private_key: File or reference name for the key in PEM or DER format
* @private_key_passwd: Passphrase for decrypted private key, %NULL if no
* passphrase is used.
* @private_key_blob: private_key as inlined data or %NULL if not used
* @private_key_blob_len: private_key_blob length
* Returns: 0 on success, -1 on failure
*/
int tlsv1_set_private_key(struct tlsv1_credentials *cred,
const char *private_key,
const char *private_key_passwd,
const u8 *private_key_blob,
size_t private_key_blob_len)
{
crypto_private_key_free(cred->key);
cred->key = NULL;
if (private_key_blob)
return tlsv1_set_key(cred, private_key_blob,
private_key_blob_len,
private_key_passwd);
if (private_key) {
u8 *buf = NULL;
size_t len;
int ret;
//buf = (u8 *) os_readfile(private_key, &len);
if (buf == NULL) {
wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
private_key);
return -1;
}
ret = tlsv1_set_key(cred, buf, len, private_key_passwd);
os_free(buf);
return ret;
}
return 0;
}
static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred,
const u8 *dh, size_t len)
{
struct asn1_hdr hdr;
const u8 *pos, *end;
pos = dh;
end = dh + len;
/*
* DHParameter ::= SEQUENCE {
* prime INTEGER, -- p
* base INTEGER, -- g
* privateValueLength INTEGER OPTIONAL }
*/
/* DHParamer ::= SEQUENCE */
if (asn1_get_next(pos, len, &hdr) < 0 ||
hdr.class != ASN1_CLASS_UNIVERSAL ||
hdr.tag != ASN1_TAG_SEQUENCE) {
wpa_printf(MSG_DEBUG, "DH: DH parameters did not start with a "
"valid SEQUENCE - found class %d tag 0x%x",
hdr.class, hdr.tag);
return -1;
}
pos = hdr.payload;
/* prime INTEGER */
if (asn1_get_next(pos, end - pos, &hdr) < 0)
return -1;
if (hdr.class != ASN1_CLASS_UNIVERSAL ||
hdr.tag != ASN1_TAG_INTEGER) {
wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for p; "
"class=%d tag=0x%x", hdr.class, hdr.tag);
return -1;
}
wpa_hexdump(MSG_MSGDUMP, "DH: prime (p)", hdr.payload, hdr.length);
if (hdr.length == 0)
return -1;
os_free(cred->dh_p);
cred->dh_p = os_malloc(hdr.length);
if (cred->dh_p == NULL)
return -1;
os_memcpy(cred->dh_p, hdr.payload, hdr.length);
cred->dh_p_len = hdr.length;
pos = hdr.payload + hdr.length;
/* base INTEGER */
if (asn1_get_next(pos, end - pos, &hdr) < 0)
return -1;
if (hdr.class != ASN1_CLASS_UNIVERSAL ||
hdr.tag != ASN1_TAG_INTEGER) {
wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for g; "
"class=%d tag=0x%x", hdr.class, hdr.tag);
return -1;
}
wpa_hexdump(MSG_MSGDUMP, "DH: base (g)", hdr.payload, hdr.length);
if (hdr.length == 0)
return -1;
os_free(cred->dh_g);
cred->dh_g = os_malloc(hdr.length);
if (cred->dh_g == NULL)
return -1;
os_memcpy(cred->dh_g, hdr.payload, hdr.length);
cred->dh_g_len = hdr.length;
return 0;
}
static const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----";
static const char *pem_dhparams_end = "-----END DH PARAMETERS-----";
static int tlsv1_set_dhparams_blob(struct tlsv1_credentials *cred,
const u8 *buf, size_t len)
{
const u8 *pos, *end;
unsigned char *der;
size_t der_len;
pos = search_tag(pem_dhparams_begin, buf, len);
if (!pos) {
wpa_printf(MSG_DEBUG, "TLSv1: No PEM dhparams tag found - "
"assume DER format");
return tlsv1_set_dhparams_der(cred, buf, len);
}
wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format dhparams into DER "
"format");
pos += os_strlen(pem_dhparams_begin);
end = search_tag(pem_dhparams_end, pos, buf + len - pos);
if (end == NULL) {
wpa_printf(MSG_INFO, "TLSv1: Could not find PEM dhparams end "
"tag (%s)", pem_dhparams_end);
return -1;
}
der = base64_decode(pos, end - pos, &der_len);
if (der == NULL) {
wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams");
return -1;
}
if (tlsv1_set_dhparams_der(cred, der, der_len) < 0) {
wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM dhparams "
"DER conversion");
os_free(der);
return -1;
}
os_free(der);
return 0;
}
/**
* tlsv1_set_dhparams - Set Diffie-Hellman parameters
* @cred: TLSv1 credentials from tlsv1_cred_alloc()
* @dh_file: File or reference name for the DH params in PEM or DER format
* @dh_blob: DH params as inlined data or %NULL if not used
* @dh_blob_len: dh_blob length
* Returns: 0 on success, -1 on failure
*/
int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file,
const u8 *dh_blob, size_t dh_blob_len)
{
if (dh_blob)
return tlsv1_set_dhparams_blob(cred, dh_blob, dh_blob_len);
if (dh_file) {
u8 *buf = NULL;
size_t len;
int ret;
//buf = (u8 *) os_readfile(dh_file, &len);
if (buf == NULL) {
wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
dh_file);
return -1;
}
ret = tlsv1_set_dhparams_blob(cred, buf, len);
os_free(buf);
return ret;
}
return 0;
}

View File

@ -0,0 +1,553 @@
/*
* TLSv1 Record Protocol
* Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "wpa/includes.h"
#include "wpa/common.h"
#include "crypto/md5.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
#include "wpa2/tls/tlsv1_common.h"
#include "wpa2/tls/tlsv1_record.h"
#include "wpa2/eap_peer/eap_i.h"
/**
* tlsv1_record_set_cipher_suite - TLS record layer: Set cipher suite
* @rl: Pointer to TLS record layer data
* @cipher_suite: New cipher suite
* Returns: 0 on success, -1 on failure
*
* This function is used to prepare TLS record layer for cipher suite change.
* tlsv1_record_change_write_cipher() and
* tlsv1_record_change_read_cipher() functions can then be used to change the
* currently used ciphers.
*/
int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl,
u16 cipher_suite)
{
const struct tls_cipher_suite *suite;
const struct tls_cipher_data *data;
wpa_printf(MSG_DEBUG, "TLSv1: Selected cipher suite: 0x%04x",
cipher_suite);
rl->cipher_suite = cipher_suite;
suite = tls_get_cipher_suite(cipher_suite);
if (suite == NULL)
return -1;
if (suite->hash == TLS_HASH_MD5) {
rl->hash_alg = CRYPTO_HASH_ALG_HMAC_MD5;
rl->hash_size = MD5_MAC_LEN;
} else if (suite->hash == TLS_HASH_SHA) {
rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA1;
rl->hash_size = SHA1_MAC_LEN;
} else if (suite->hash == TLS_HASH_SHA256) {
rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA256;
rl->hash_size = SHA256_MAC_LEN;
}
data = tls_get_cipher_data(suite->cipher);
if (data == NULL)
return -1;
rl->key_material_len = data->key_material;
rl->iv_size = data->block_size;
rl->cipher_alg = data->alg;
return 0;
}
/**
* tlsv1_record_change_write_cipher - TLS record layer: Change write cipher
* @rl: Pointer to TLS record layer data
* Returns: 0 on success (cipher changed), -1 on failure
*
* This function changes TLS record layer to use the new cipher suite
* configured with tlsv1_record_set_cipher_suite() for writing.
*/
int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl)
{
wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New write cipher suite "
"0x%04x", rl->cipher_suite);
rl->write_cipher_suite = rl->cipher_suite;
os_memset(rl->write_seq_num, 0, TLS_SEQ_NUM_LEN);
if (rl->write_cbc) {
if (wpa2_crypto_funcs.crypto_cipher_deinit) {
wpa2_crypto_funcs.crypto_cipher_deinit(rl->write_cbc);
rl->write_cbc = NULL;
} else {
wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n");
return -1;
}
}
if (rl->cipher_alg != CRYPTO_CIPHER_NULL) {
if (wpa2_crypto_funcs.crypto_cipher_init) {
rl->write_cbc = wpa2_crypto_funcs.crypto_cipher_init(rl->cipher_alg,
rl->write_iv, rl->write_key,
rl->key_material_len);
} else {
wpa_printf(MSG_ERROR, "Fail to register crypto_cipher_init function!\r\n");
return -1;
}
if (rl->write_cbc == NULL) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize "
"cipher");
return -1;
}
}
return 0;
}
/**
* tlsv1_record_change_read_cipher - TLS record layer: Change read cipher
* @rl: Pointer to TLS record layer data
* Returns: 0 on success (cipher changed), -1 on failure
*
* This function changes TLS record layer to use the new cipher suite
* configured with tlsv1_record_set_cipher_suite() for reading.
*/
int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl)
{
wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New read cipher suite "
"0x%04x \n", rl->cipher_suite);
rl->read_cipher_suite = rl->cipher_suite;
os_memset(rl->read_seq_num, 0, TLS_SEQ_NUM_LEN);
if (rl->read_cbc) {
if (wpa2_crypto_funcs.crypto_cipher_deinit) {
wpa2_crypto_funcs.crypto_cipher_deinit(rl->read_cbc);
rl->read_cbc = NULL;
} else {
wpa_printf(MSG_ERROR, "Fail to register crypto cipher deinit function!\r\n");
return -1;
}
}
if (rl->cipher_alg != CRYPTO_CIPHER_NULL) {
if(wpa2_crypto_funcs.crypto_cipher_init) {
rl->read_cbc = wpa2_crypto_funcs.crypto_cipher_init(rl->cipher_alg,
rl->read_iv, rl->read_key,
rl->key_material_len);
} else {
wpa_printf(MSG_ERROR, "Fail to register crypto_cipher_init function!\r\n");
return -1;
}
if (rl->read_cbc == NULL) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize "
"cipher");
return -1;
}
}
return 0;
}
/**
* tlsv1_record_send - TLS record layer: Send a message
* @rl: Pointer to TLS record layer data
* @content_type: Content type (TLS_CONTENT_TYPE_*)
* @buf: Buffer for the generated TLS message (needs to have extra space for
* header, IV (TLS v1.1), and HMAC)
* @buf_size: Maximum buf size
* @payload: Payload to be sent
* @payload_len: Length of the payload
* @out_len: Buffer for returning the used buf length
* Returns: 0 on success, -1 on failure
*
* This function fills in the TLS record layer header, adds HMAC, and encrypts
* the data using the current write cipher.
*/
int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
size_t buf_size, const u8 *payload, size_t payload_len,
size_t *out_len)
{
u8 *pos, *ct_start, *length, *cpayload;
struct crypto_hash *hmac = NULL;
size_t clen;
int explicit_iv;
pos = buf;
if (pos + TLS_RECORD_HEADER_LEN > buf + buf_size)
return -1;
/* ContentType type */
ct_start = pos;
*pos++ = content_type;
/* ProtocolVersion version */
WPA_PUT_BE16(pos, rl->tls_version);
pos += 2;
/* uint16 length */
length = pos;
WPA_PUT_BE16(length, payload_len);
pos += 2;
cpayload = pos;
explicit_iv = rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL &&
rl->iv_size && rl->tls_version >= TLS_VERSION_1_1;
if (explicit_iv) {
/* opaque IV[Cipherspec.block_length] */
if (pos + rl->iv_size > buf + buf_size)
return -1;
/*
* Use random number R per the RFC 4346, 6.2.3.2 CBC Block
* Cipher option 2a.
*/
if (os_get_random(pos, rl->iv_size))
return -1;
pos += rl->iv_size;
}
/*
* opaque fragment[TLSPlaintext.length]
* (opaque content[TLSCompressed.length] in GenericBlockCipher)
*/
if (pos + payload_len > buf + buf_size)
return -1;
os_memmove(pos, payload, payload_len);
pos += payload_len;
if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
/*
* MAC calculated over seq_num + TLSCompressed.type +
* TLSCompressed.version + TLSCompressed.length +
* TLSCompressed.fragment
*/
if (wpa2_crypto_funcs.crypto_hash_init) {
hmac = wpa2_crypto_funcs.crypto_hash_init(rl->hash_alg, rl->write_mac_secret, rl->hash_size);
} else {
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash init!\r\n", __FUNCTION__);
return -1;
}
if (hmac == NULL) {
wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
"to initialize HMAC");
return -1;
}
if (wpa2_crypto_funcs.crypto_hash_update) {
wpa2_crypto_funcs.crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN);
/* type + version + length + fragment */
wpa2_crypto_funcs.crypto_hash_update(hmac, ct_start, TLS_RECORD_HEADER_LEN);
wpa2_crypto_funcs.crypto_hash_update(hmac, payload, payload_len);
} else {
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash update!\r\n", __FUNCTION__);
return -1;
}
clen = buf + buf_size - pos;
if (clen < rl->hash_size) {
wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not "
"enough room for MAC");
if (wpa2_crypto_funcs.crypto_hash_finish) {
wpa2_crypto_funcs.crypto_hash_finish(hmac, NULL, NULL);
} else {
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash finish function!\r\n", __FUNCTION__);
return -1;
}
return -1;
}
if (wpa2_crypto_funcs.crypto_hash_finish) {
if ((int)wpa2_crypto_funcs.crypto_hash_finish(hmac, pos, (int *)&clen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed to calculate HMAC");
return -1;
}
} else {
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n",__FUNCTION__);
return -1;
}
wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Write HMAC",
pos, clen);
pos += clen;
if (rl->iv_size) {
size_t len = pos - cpayload;
size_t pad;
pad = (len + 1) % rl->iv_size;
if (pad)
pad = rl->iv_size - pad;
if (pos + pad + 1 > buf + buf_size) {
wpa_printf(MSG_DEBUG, "TLSv1: No room for "
"block cipher padding");
return -1;
}
os_memset(pos, pad, pad + 1);
pos += pad + 1;
}
if (wpa2_crypto_funcs.crypto_cipher_encrypt) {
if ((int)wpa2_crypto_funcs.crypto_cipher_encrypt(rl->write_cbc, cpayload,
cpayload, pos - cpayload) < 0)
return -1;
} else {
wpa_printf(MSG_ERROR, "Fail to register crypto_cipher_encrypt function!\r\n");
return -1;
}
}
WPA_PUT_BE16(length, pos - length - 2);
inc_byte_array(rl->write_seq_num, TLS_SEQ_NUM_LEN);
*out_len = pos - buf;
return 0;
}
/**
* tlsv1_record_receive - TLS record layer: Process a received message
* @rl: Pointer to TLS record layer data
* @in_data: Received data
* @in_len: Length of the received data
* @out_data: Buffer for output data (must be at least as long as in_data)
* @out_len: Set to maximum out_data length by caller; used to return the
* length of the used data
* @alert: Buffer for returning an alert value on failure
* Returns: Number of bytes used from in_data on success, 0 if record was not
* complete (more data needed), or -1 on failure
*
* This function decrypts the received message, verifies HMAC and TLS record
* layer header.
*/
int tlsv1_record_receive(struct tlsv1_record_layer *rl,
const u8 *in_data, size_t in_len,
u8 *out_data, size_t *out_len, u8 *alert)
{
size_t i, rlen, hlen;
u8 padlen;
struct crypto_hash *hmac = NULL;
u8 len[2], hash[100];
int force_mac_error = 0;
u8 ct;
if (in_len < TLS_RECORD_HEADER_LEN) {
wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu) - "
"need more data",
(unsigned long) in_len);
wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
in_data, in_len);
return 0;
}
ct = in_data[0];
rlen = WPA_GET_BE16(in_data + 3);
wpa_printf(MSG_DEBUG, "TLSv1: Received content type %d version %d.%d "
"length %d", ct, in_data[1], in_data[2], (int) rlen);
/*
* TLS v1.0 and v1.1 RFCs were not exactly clear on the use of the
* protocol version in record layer. As such, accept any {03,xx} value
* to remain compatible with existing implementations.
*/
if (in_data[1] != 0x03) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version "
"%u.%u", in_data[1], in_data[2]);
*alert = TLS_ALERT_PROTOCOL_VERSION;
return -1;
}
/* TLSCiphertext must not be more than 2^14+2048 bytes */
if (TLS_RECORD_HEADER_LEN + rlen > 18432) {
wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)",
(unsigned long) (TLS_RECORD_HEADER_LEN + rlen));
*alert = TLS_ALERT_RECORD_OVERFLOW;
return -1;
}
in_data += TLS_RECORD_HEADER_LEN;
in_len -= TLS_RECORD_HEADER_LEN;
if (rlen > in_len) {
wpa_printf(MSG_DEBUG, "TLSv1: Not all record data included "
"(rlen=%lu > in_len=%lu)",
(unsigned long) rlen, (unsigned long) in_len);
return 0;
}
wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
in_data, rlen);
if (ct != TLS_CONTENT_TYPE_HANDSHAKE &&
ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC &&
ct != TLS_CONTENT_TYPE_ALERT &&
ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
wpa_printf(MSG_DEBUG, "TLSv1: Ignore record with unknown "
"content type 0x%x", ct);
*alert = TLS_ALERT_UNEXPECTED_MESSAGE;
return -1;
}
in_len = rlen;
if (*out_len < in_len) {
wpa_printf(MSG_DEBUG, "TLSv1: Not enough output buffer for "
"processing received record");
*alert = TLS_ALERT_INTERNAL_ERROR;
return -1;
}
if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
size_t plen;
if (wpa2_crypto_funcs.crypto_cipher_decrypt) {
if ((int)wpa2_crypto_funcs.crypto_cipher_decrypt(rl->read_cbc, in_data,
out_data, in_len) < 0) {
*alert = TLS_ALERT_DECRYPTION_FAILED;
return -1;
}
} else {
wpa_printf(MSG_ERROR, "Fail to register crypto cipher decrypt function. \r\n");
*alert = TLS_ALERT_DECRYPTION_FAILED;
return -1;
}
plen = in_len;
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted "
"data", out_data, plen);
if (rl->iv_size) {
/*
* TLS v1.0 defines different alert values for various
* failures. That may information to aid in attacks, so
* use the same bad_record_mac alert regardless of the
* issues.
*
* In addition, instead of returning immediately on
* error, run through the MAC check to make timing
* attacks more difficult.
*/
if (rl->tls_version >= TLS_VERSION_1_1) {
/* Remove opaque IV[Cipherspec.block_length] */
if (plen < rl->iv_size) {
wpa_printf(MSG_DEBUG, "TLSv1.1: Not "
"enough room for IV");
force_mac_error = 1;
goto check_mac;
}
os_memmove(out_data, out_data + rl->iv_size,
plen - rl->iv_size);
plen -= rl->iv_size;
}
/* Verify and remove padding */
if (plen == 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Too short record"
" (no pad)");
force_mac_error = 1;
goto check_mac;
}
padlen = out_data[plen - 1];
if (padlen >= plen) {
wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad "
"length (%u, plen=%lu) in "
"received record",
padlen, (unsigned long) plen);
force_mac_error = 1;
goto check_mac;
}
for (i = plen - padlen - 1; i < plen - 1; i++) {
if (out_data[i] != padlen) {
wpa_hexdump(MSG_DEBUG,
"TLSv1: Invalid pad in "
"received record",
out_data + plen - padlen -
1, padlen + 1);
force_mac_error = 1;
goto check_mac;
}
}
plen -= padlen + 1;
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - "
"Decrypted data with IV and padding "
"removed", out_data, plen);
}
check_mac:
if (plen < rl->hash_size) {
wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no "
"hash value");
*alert = TLS_ALERT_BAD_RECORD_MAC;
return -1;
}
plen -= rl->hash_size;
if (wpa2_crypto_funcs.crypto_hash_init) {
hmac = wpa2_crypto_funcs.crypto_hash_init(rl->hash_alg, rl->read_mac_secret, rl->hash_size);
} else {
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_init function!\r\n", __FUNCTION__);
return -1;
}
if (hmac == NULL) {
wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
"to initialize HMAC");
*alert = TLS_ALERT_INTERNAL_ERROR;
return -1;
}
if (wpa2_crypto_funcs.crypto_hash_update) {
wpa2_crypto_funcs.crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN);
/* type + version + length + fragment */
wpa2_crypto_funcs.crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3);
WPA_PUT_BE16(len, plen);
wpa2_crypto_funcs.crypto_hash_update(hmac, len, 2);
wpa2_crypto_funcs.crypto_hash_update(hmac, out_data, plen);
} else {
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto hash update function!\r\n", __FUNCTION__);
return -1;
}
hlen = sizeof(hash);
if (wpa2_crypto_funcs.crypto_hash_finish) {
if ((int)wpa2_crypto_funcs.crypto_hash_finish(hmac, hash, (int *)&hlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed to calculate HMAC");
*alert = TLS_ALERT_INTERNAL_ERROR;
return -1;
}
} else {
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__);
*alert = TLS_ALERT_INTERNAL_ERROR;
return -1;
}
if (hlen != rl->hash_size ||
os_memcmp(hash, out_data + plen, hlen) != 0 ||
force_mac_error) {
wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in "
"received message (force_mac_error=%d)",
force_mac_error);
*alert = TLS_ALERT_BAD_RECORD_MAC;
return -1;
}
*out_len = plen;
} else {
os_memcpy(out_data, in_data, in_len);
*out_len = in_len;
}
/* TLSCompressed must not be more than 2^14+1024 bytes */
if (TLS_RECORD_HEADER_LEN + *out_len > 17408) {
wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)",
(unsigned long) (TLS_RECORD_HEADER_LEN + *out_len));
*alert = TLS_ALERT_RECORD_OVERFLOW;
return -1;
}
inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN);
return TLS_RECORD_HEADER_LEN + rlen;
}

View File

@ -0,0 +1,656 @@
/*
* TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246)
* Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "wpa/includes.h"
#include "wpa/common.h"
#include "crypto/sha1.h"
#include "wpa2/tls/tls.h"
#include "wpa2/tls/tlsv1_common.h"
#include "wpa2/tls/tlsv1_record.h"
#include "wpa2/tls/tlsv1_server.h"
#include "wpa2/tls/tlsv1_server_i.h"
/* TODO:
* Support for a message fragmented across several records (RFC 2246, 6.2.1)
*/
void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description)
{
conn->alert_level = level;
conn->alert_description = description;
}
int tlsv1_server_derive_keys(struct tlsv1_server *conn,
const u8 *pre_master_secret,
size_t pre_master_secret_len)
{
u8 seed[2 * TLS_RANDOM_LEN];
u8 key_block[TLS_MAX_KEY_BLOCK_LEN];
u8 *pos;
size_t key_block_len;
if (pre_master_secret) {
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret",
pre_master_secret, pre_master_secret_len);
os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
TLS_RANDOM_LEN);
if (tls_prf(conn->rl.tls_version,
pre_master_secret, pre_master_secret_len,
"master secret", seed, 2 * TLS_RANDOM_LEN,
conn->master_secret, TLS_MASTER_SECRET_LEN)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive "
"master_secret");
return -1;
}
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret",
conn->master_secret, TLS_MASTER_SECRET_LEN);
}
os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN);
key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len +
conn->rl.iv_size);
if (tls_prf(conn->rl.tls_version,
conn->master_secret, TLS_MASTER_SECRET_LEN,
"key expansion", seed, 2 * TLS_RANDOM_LEN,
key_block, key_block_len)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block");
return -1;
}
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block",
key_block, key_block_len);
pos = key_block;
/* client_write_MAC_secret */
os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size);
pos += conn->rl.hash_size;
/* server_write_MAC_secret */
os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size);
pos += conn->rl.hash_size;
/* client_write_key */
os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len);
pos += conn->rl.key_material_len;
/* server_write_key */
os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len);
pos += conn->rl.key_material_len;
/* client_write_IV */
os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
pos += conn->rl.iv_size;
/* server_write_IV */
os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size);
pos += conn->rl.iv_size;
return 0;
}
/**
* tlsv1_server_handshake - Process TLS handshake
* @conn: TLSv1 server connection data from tlsv1_server_init()
* @in_data: Input data from TLS peer
* @in_len: Input data length
* @out_len: Length of the output buffer.
* Returns: Pointer to output data, %NULL on failure
*/
u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
const u8 *in_data, size_t in_len,
size_t *out_len)
{
const u8 *pos, *end;
u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct;
size_t in_msg_len;
int used;
if (in_data == NULL || in_len == 0) {
wpa_printf(MSG_DEBUG, "TLSv1: No input data to server");
return NULL;
}
pos = in_data;
end = in_data + in_len;
in_msg = os_malloc(in_len);
if (in_msg == NULL)
return NULL;
/* Each received packet may include multiple records */
while (pos < end) {
in_msg_len = in_len;
used = tlsv1_record_receive(&conn->rl, pos, end - pos,
in_msg, &in_msg_len, &alert);
if (used < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
"record failed");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
goto failed;
}
if (used == 0) {
/* need more data */
wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
"yet supported");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
goto failed;
}
ct = pos[0];
in_pos = in_msg;
in_end = in_msg + in_msg_len;
/* Each received record may include multiple messages of the
* same ContentType. */
while (in_pos < in_end) {
in_msg_len = in_end - in_pos;
if (tlsv1_server_process_handshake(conn, ct, in_pos,
&in_msg_len) < 0)
goto failed;
in_pos += in_msg_len;
}
pos += used;
}
os_free(in_msg);
in_msg = NULL;
msg = tlsv1_server_handshake_write(conn, out_len);
failed:
os_free(in_msg);
if (conn->alert_level) {
if (conn->state == FAILED) {
/* Avoid alert loops */
wpa_printf(MSG_DEBUG, "TLSv1: Drop alert loop");
os_free(msg);
return NULL;
}
conn->state = FAILED;
os_free(msg);
msg = tlsv1_server_send_alert(conn, conn->alert_level,
conn->alert_description,
out_len);
}
return msg;
}
/**
* tlsv1_server_encrypt - Encrypt data into TLS tunnel
* @conn: TLSv1 server connection data from tlsv1_server_init()
* @in_data: Pointer to plaintext data to be encrypted
* @in_len: Input buffer length
* @out_data: Pointer to output buffer (encrypted TLS data)
* @out_len: Maximum out_data length
* Returns: Number of bytes written to out_data, -1 on failure
*
* This function is used after TLS handshake has been completed successfully to
* send data in the encrypted tunnel.
*/
int tlsv1_server_encrypt(struct tlsv1_server *conn,
const u8 *in_data, size_t in_len,
u8 *out_data, size_t out_len)
{
size_t rlen;
wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
in_data, in_len);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
out_data, out_len, in_data, in_len, &rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
return rlen;
}
/**
* tlsv1_server_decrypt - Decrypt data from TLS tunnel
* @conn: TLSv1 server connection data from tlsv1_server_init()
* @in_data: Pointer to input buffer (encrypted TLS data)
* @in_len: Input buffer length
* @out_data: Pointer to output buffer (decrypted data from TLS tunnel)
* @out_len: Maximum out_data length
* Returns: Number of bytes written to out_data, -1 on failure
*
* This function is used after TLS handshake has been completed successfully to
* receive data from the encrypted tunnel.
*/
int tlsv1_server_decrypt(struct tlsv1_server *conn,
const u8 *in_data, size_t in_len,
u8 *out_data, size_t out_len)
{
const u8 *in_end, *pos;
int used;
u8 alert, *out_end, *out_pos, ct;
size_t olen;
pos = in_data;
in_end = in_data + in_len;
out_pos = out_data;
out_end = out_data + out_len;
while (pos < in_end) {
ct = pos[0];
olen = out_end - out_pos;
used = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
out_pos, &olen, &alert);
if (used < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
"failed");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
return -1;
}
if (used == 0) {
/* need more data */
wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
"yet supported");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
return -1;
}
if (ct == TLS_CONTENT_TYPE_ALERT) {
if (olen < 2) {
wpa_printf(MSG_DEBUG, "TLSv1: Alert "
"underflow");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_DECODE_ERROR);
return -1;
}
wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
out_pos[0], out_pos[1]);
if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) {
/* Continue processing */
pos += used;
continue;
}
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
out_pos[1]);
return -1;
}
if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
"0x%x", pos[0]);
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_UNEXPECTED_MESSAGE);
return -1;
}
out_pos += olen;
if (out_pos > out_end) {
wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough "
"for processing the received record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
pos += used;
}
return out_pos - out_data;
}
/**
* tlsv1_server_global_init - Initialize TLSv1 server
* Returns: 0 on success, -1 on failure
*
* This function must be called before using any other TLSv1 server functions.
*/
int tlsv1_server_global_init(void)
{
return crypto_global_init();
}
/**
* tlsv1_server_global_deinit - Deinitialize TLSv1 server
*
* This function can be used to deinitialize the TLSv1 server that was
* initialized by calling tlsv1_server_global_init(). No TLSv1 server functions
* can be called after this before calling tlsv1_server_global_init() again.
*/
void tlsv1_server_global_deinit(void)
{
crypto_global_deinit();
}
/**
* tlsv1_server_init - Initialize TLSv1 server connection
* @cred: Pointer to server credentials from tlsv1_server_cred_alloc()
* Returns: Pointer to TLSv1 server connection data or %NULL on failure
*/
struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred)
{
struct tlsv1_server *conn;
size_t count;
u16 *suites;
conn = (struct tlsv1_server *)os_zalloc(sizeof(*conn));
if (conn == NULL)
return NULL;
conn->cred = cred;
conn->state = CLIENT_HELLO;
if (tls_verify_hash_init(&conn->verify) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify "
"hash");
os_free(conn);
return NULL;
}
count = 0;
suites = conn->cipher_suites;
suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
conn->num_cipher_suites = count;
return conn;
}
static void tlsv1_server_clear_data(struct tlsv1_server *conn)
{
tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
tlsv1_record_change_write_cipher(&conn->rl);
tlsv1_record_change_read_cipher(&conn->rl);
tls_verify_hash_free(&conn->verify);
crypto_public_key_free(conn->client_rsa_key);
conn->client_rsa_key = NULL;
os_free(conn->session_ticket);
conn->session_ticket = NULL;
conn->session_ticket_len = 0;
conn->use_session_ticket = 0;
os_free(conn->dh_secret);
conn->dh_secret = NULL;
conn->dh_secret_len = 0;
}
/**
* tlsv1_server_deinit - Deinitialize TLSv1 server connection
* @conn: TLSv1 server connection data from tlsv1_server_init()
*/
void tlsv1_server_deinit(struct tlsv1_server *conn)
{
tlsv1_server_clear_data(conn);
os_free(conn);
}
/**
* tlsv1_server_established - Check whether connection has been established
* @conn: TLSv1 server connection data from tlsv1_server_init()
* Returns: 1 if connection is established, 0 if not
*/
int tlsv1_server_established(struct tlsv1_server *conn)
{
return conn->state == ESTABLISHED;
}
/**
* tlsv1_server_prf - Use TLS-PRF to derive keying material
* @conn: TLSv1 server connection data from tlsv1_server_init()
* @label: Label (e.g., description of the key) for PRF
* @server_random_first: seed is 0 = client_random|server_random,
* 1 = server_random|client_random
* @out: Buffer for output data from TLS-PRF
* @out_len: Length of the output buffer
* Returns: 0 on success, -1 on failure
*/
int tlsv1_server_prf(struct tlsv1_server *conn, const char *label,
int server_random_first, u8 *out, size_t out_len)
{
u8 seed[2 * TLS_RANDOM_LEN];
if (conn->state != ESTABLISHED)
return -1;
if (server_random_first) {
os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random,
TLS_RANDOM_LEN);
} else {
os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
TLS_RANDOM_LEN);
}
return tls_prf(conn->rl.tls_version,
conn->master_secret, TLS_MASTER_SECRET_LEN,
label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
}
/**
* tlsv1_server_get_cipher - Get current cipher name
* @conn: TLSv1 server connection data from tlsv1_server_init()
* @buf: Buffer for the cipher name
* @buflen: buf size
* Returns: 0 on success, -1 on failure
*
* Get the name of the currently used cipher.
*/
int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf,
size_t buflen)
{
#ifndef ESPRESSIF_USE
char *cipher;
switch (conn->rl.cipher_suite) {
case TLS_RSA_WITH_RC4_128_MD5:
cipher = "RC4-MD5";
break;
case TLS_RSA_WITH_RC4_128_SHA:
cipher = "RC4-SHA";
break;
case TLS_RSA_WITH_DES_CBC_SHA:
cipher = "DES-CBC-SHA";
break;
case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
cipher = "DES-CBC3-SHA";
break;
case TLS_DH_anon_WITH_AES_128_CBC_SHA:
cipher = "ADH-AES-128-SHA";
break;
case TLS_RSA_WITH_AES_256_CBC_SHA:
cipher = "AES-256-SHA";
break;
case TLS_RSA_WITH_AES_128_CBC_SHA:
cipher = "AES-128-SHA";
break;
default:
return -1;
}
//if (os_strlcpy(buf, cipher, buflen) >= buflen)
// return -1;
os_memcpy((u8 *)buf, (u8 *)cipher, buflen);
return 0;
#else
char cipher[20];
switch (conn->rl.cipher_suite) {
case TLS_RSA_WITH_RC4_128_MD5:
strcpy(cipher, "RC4-MD5");
break;
case TLS_RSA_WITH_RC4_128_SHA:
strcpy(cipher, "RC4-SHA");
break;
case TLS_RSA_WITH_DES_CBC_SHA:
strcpy(cipher, "DES-CBC-SHA");
break;
case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
strcpy(cipher, "DES-CBC3-SHA");
break;
case TLS_DH_anon_WITH_AES_128_CBC_SHA:
strcpy(cipher, "ADH-AES-128-SHA");
break;
case TLS_RSA_WITH_AES_256_CBC_SHA:
strcpy(cipher, "AES-256-SHA");
break;
case TLS_RSA_WITH_AES_128_CBC_SHA:
strcpy(cipher, "AES-128-SHA");
break;
default:
return -1;
}
os_memcpy((u8 *)buf, (u8 *)cipher, buflen);
return 0;
#endif
}
/**
* tlsv1_server_shutdown - Shutdown TLS connection
* @conn: TLSv1 server connection data from tlsv1_server_init()
* Returns: 0 on success, -1 on failure
*/
int tlsv1_server_shutdown(struct tlsv1_server *conn)
{
conn->state = CLIENT_HELLO;
if (tls_verify_hash_init(&conn->verify) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify "
"hash");
return -1;
}
tlsv1_server_clear_data(conn);
return 0;
}
/**
* tlsv1_server_resumed - Was session resumption used
* @conn: TLSv1 server connection data from tlsv1_server_init()
* Returns: 1 if current session used session resumption, 0 if not
*/
int tlsv1_server_resumed(struct tlsv1_server *conn)
{
return 0;
}
/**
* tlsv1_server_get_keys - Get master key and random data from TLS connection
* @conn: TLSv1 server connection data from tlsv1_server_init()
* @keys: Structure of key/random data (filled on success)
* Returns: 0 on success, -1 on failure
*/
int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys)
{
os_memset(keys, 0, sizeof(*keys));
if (conn->state == CLIENT_HELLO)
return -1;
keys->client_random = conn->client_random;
keys->client_random_len = TLS_RANDOM_LEN;
if (conn->state != SERVER_HELLO) {
keys->server_random = conn->server_random;
keys->server_random_len = TLS_RANDOM_LEN;
keys->master_key = conn->master_secret;
keys->master_key_len = TLS_MASTER_SECRET_LEN;
}
return 0;
}
/**
* tlsv1_server_get_keyblock_size - Get TLS key_block size
* @conn: TLSv1 server connection data from tlsv1_server_init()
* Returns: Size of the key_block for the negotiated cipher suite or -1 on
* failure
*/
int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn)
{
if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO)
return -1;
return 2 * (conn->rl.hash_size + conn->rl.key_material_len +
conn->rl.iv_size);
}
/**
* tlsv1_server_set_cipher_list - Configure acceptable cipher suites
* @conn: TLSv1 server connection data from tlsv1_server_init()
* @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
* (TLS_CIPHER_*).
* Returns: 0 on success, -1 on failure
*/
int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers)
{
size_t count;
u16 *suites;
/* TODO: implement proper configuration of cipher suites */
if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) {
count = 0;
suites = conn->cipher_suites;
suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5;
suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA;
conn->num_cipher_suites = count;
}
return 0;
}
int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer)
{
conn->verify_peer = verify_peer;
return 0;
}
void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn,
tlsv1_server_session_ticket_cb cb,
void *ctx)
{
wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)",
cb, ctx);
conn->session_ticket_cb = cb;
conn->session_ticket_cb_ctx = ctx;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,819 @@
/*
* TLSv1 server - write handshake message
* Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "wpa/includes.h"
#include "wpa/common.h"
#include "crypto/md5.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
#include "crypto/random.h"
#include "wpa2/tls/tls.h"
#include "wpa2/tls/x509v3.h"
#include "wpa2/tls/tlsv1_common.h"
#include "wpa2/tls/tlsv1_record.h"
#include "wpa2/tls/tlsv1_server.h"
#include "wpa2/tls/tlsv1_server_i.h"
#include "wpa2/eap_peer/eap_i.h"
static size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn)
{
size_t len = 0;
struct x509_certificate *cert;
cert = conn->cred->cert;
while (cert) {
len += 3 + cert->cert_len;
if (x509_certificate_self_signed(cert))
break;
cert = x509_certificate_get_subject(conn->cred->trusted_certs,
&cert->issuer);
}
return len;
}
static int tls_write_server_hello(struct tlsv1_server *conn,
u8 **msgpos, u8 *end)
{
u8 *pos, *rhdr, *hs_start, *hs_length;
struct os_time now;
size_t rlen;
pos = *msgpos;
wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHello");
rhdr = pos;
pos += TLS_RECORD_HEADER_LEN;
os_get_time(&now);
WPA_PUT_BE32(conn->server_random, now.sec);
if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) {
wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
"server_random");
return -1;
}
wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random",
conn->server_random, TLS_RANDOM_LEN);
conn->session_id_len = TLS_SESSION_ID_MAX_LEN;
if (random_get_bytes(conn->session_id, conn->session_id_len)) {
wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
"session_id");
return -1;
}
wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id",
conn->session_id, conn->session_id_len);
/* opaque fragment[TLSPlaintext.length] */
/* Handshake */
hs_start = pos;
/* HandshakeType msg_type */
*pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO;
/* uint24 length (to be filled) */
hs_length = pos;
pos += 3;
/* body - ServerHello */
/* ProtocolVersion server_version */
WPA_PUT_BE16(pos, conn->rl.tls_version);
pos += 2;
/* Random random: uint32 gmt_unix_time, opaque random_bytes */
os_memcpy(pos, conn->server_random, TLS_RANDOM_LEN);
pos += TLS_RANDOM_LEN;
/* SessionID session_id */
*pos++ = conn->session_id_len;
os_memcpy(pos, conn->session_id, conn->session_id_len);
pos += conn->session_id_len;
/* CipherSuite cipher_suite */
WPA_PUT_BE16(pos, conn->cipher_suite);
pos += 2;
/* CompressionMethod compression_method */
*pos++ = TLS_COMPRESSION_NULL;
if (conn->session_ticket && conn->session_ticket_cb) {
int res = conn->session_ticket_cb(
conn->session_ticket_cb_ctx,
conn->session_ticket, conn->session_ticket_len,
conn->client_random, conn->server_random,
conn->master_secret);
if (res < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback "
"indicated failure");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_HANDSHAKE_FAILURE);
return -1;
}
conn->use_session_ticket = res;
if (conn->use_session_ticket) {
if (tlsv1_server_derive_keys(conn, NULL, 0) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
"derive keys");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
}
/*
* RFC 4507 specifies that server would include an empty
* SessionTicket extension in ServerHello and a
* NewSessionTicket message after the ServerHello. However,
* EAP-FAST (RFC 4851), i.e., the only user of SessionTicket
* extension at the moment, does not use such extensions.
*
* TODO: Add support for configuring RFC 4507 behavior and make
* EAP-FAST disable it.
*/
}
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
rhdr, end - rhdr, hs_start, pos - hs_start,
&rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
pos = rhdr + rlen;
*msgpos = pos;
return 0;
}
static int tls_write_server_certificate(struct tlsv1_server *conn,
u8 **msgpos, u8 *end)
{
u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start;
size_t rlen;
struct x509_certificate *cert;
const struct tls_cipher_suite *suite;
suite = tls_get_cipher_suite(conn->rl.cipher_suite);
if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) {
wpa_printf(MSG_DEBUG, "TLSv1: Do not send Certificate when "
"using anonymous DH");
return 0;
}
pos = *msgpos;
wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate");
rhdr = pos;
pos += TLS_RECORD_HEADER_LEN;
/* opaque fragment[TLSPlaintext.length] */
/* Handshake */
hs_start = pos;
/* HandshakeType msg_type */
*pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE;
/* uint24 length (to be filled) */
hs_length = pos;
pos += 3;
/* body - Certificate */
/* uint24 length (to be filled) */
cert_start = pos;
pos += 3;
cert = conn->cred->cert;
while (cert) {
if (pos + 3 + cert->cert_len > end) {
wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space "
"for Certificate (cert_len=%lu left=%lu)",
(unsigned long) cert->cert_len,
(unsigned long) (end - pos));
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
WPA_PUT_BE24(pos, cert->cert_len);
pos += 3;
os_memcpy(pos, cert->cert_start, cert->cert_len);
pos += cert->cert_len;
if (x509_certificate_self_signed(cert))
break;
cert = x509_certificate_get_subject(conn->cred->trusted_certs,
&cert->issuer);
}
if (cert == conn->cred->cert || cert == NULL) {
/*
* Server was not configured with all the needed certificates
* to form a full certificate chain. The client may fail to
* validate the chain unless it is configured with all the
* missing CA certificates.
*/
wpa_printf(MSG_DEBUG, "TLSv1: Full server certificate chain "
"not configured - validation may fail");
}
WPA_PUT_BE24(cert_start, pos - cert_start - 3);
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
rhdr, end - rhdr, hs_start, pos - hs_start,
&rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
pos = rhdr + rlen;
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
*msgpos = pos;
return 0;
}
static int tls_write_server_key_exchange(struct tlsv1_server *conn,
u8 **msgpos, u8 *end)
{
tls_key_exchange keyx;
const struct tls_cipher_suite *suite;
u8 *pos, *rhdr, *hs_start, *hs_length;
size_t rlen;
u8 *dh_ys;
size_t dh_ys_len;
suite = tls_get_cipher_suite(conn->rl.cipher_suite);
if (suite == NULL)
keyx = TLS_KEY_X_NULL;
else
keyx = suite->key_exchange;
if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) {
wpa_printf(MSG_DEBUG, "TLSv1: No ServerKeyExchange needed");
return 0;
}
if (keyx != TLS_KEY_X_DH_anon) {
/* TODO? */
wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not yet "
"supported with key exchange type %d", keyx);
return -1;
}
if (conn->cred == NULL || conn->cred->dh_p == NULL ||
conn->cred->dh_g == NULL) {
wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available for "
"ServerKeyExhcange");
return -1;
}
os_free(conn->dh_secret);
conn->dh_secret_len = conn->cred->dh_p_len;
conn->dh_secret = os_malloc(conn->dh_secret_len);
if (conn->dh_secret == NULL) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
"memory for secret (Diffie-Hellman)");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
if (random_get_bytes(conn->dh_secret, conn->dh_secret_len)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
"data for Diffie-Hellman");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
os_free(conn->dh_secret);
conn->dh_secret = NULL;
return -1;
}
if (os_memcmp(conn->dh_secret, conn->cred->dh_p, conn->dh_secret_len) >
0)
conn->dh_secret[0] = 0; /* make sure secret < p */
pos = conn->dh_secret;
while (pos + 1 < conn->dh_secret + conn->dh_secret_len && *pos == 0)
pos++;
if (pos != conn->dh_secret) {
os_memmove(conn->dh_secret, pos,
conn->dh_secret_len - (pos - conn->dh_secret));
conn->dh_secret_len -= pos - conn->dh_secret;
}
wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH server's secret value",
conn->dh_secret, conn->dh_secret_len);
/* Ys = g^secret mod p */
dh_ys_len = conn->cred->dh_p_len;
dh_ys = os_malloc(dh_ys_len);
if (dh_ys == NULL) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate memory for "
"Diffie-Hellman");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
if(wpa2_crypto_funcs.crypto_mod_exp) {
if (wpa2_crypto_funcs.crypto_mod_exp(conn->cred->dh_g, conn->cred->dh_g_len,
conn->dh_secret, conn->dh_secret_len,
conn->cred->dh_p, conn->cred->dh_p_len,
dh_ys, &dh_ys_len)) {
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
os_free(dh_ys);
return -1;
}
} else {
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
os_free(dh_ys);
wpa_printf(MSG_ERROR, "Fail to register crypto_mod_exp function!\r\n");
return -1;
}
wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)",
dh_ys, dh_ys_len);
/*
* struct {
* select (KeyExchangeAlgorithm) {
* case diffie_hellman:
* ServerDHParams params;
* Signature signed_params;
* case rsa:
* ServerRSAParams params;
* Signature signed_params;
* };
* } ServerKeyExchange;
*
* struct {
* opaque dh_p<1..2^16-1>;
* opaque dh_g<1..2^16-1>;
* opaque dh_Ys<1..2^16-1>;
* } ServerDHParams;
*/
pos = *msgpos;
wpa_printf(MSG_DEBUG, "TLSv1: Send ServerKeyExchange");
rhdr = pos;
pos += TLS_RECORD_HEADER_LEN;
/* opaque fragment[TLSPlaintext.length] */
/* Handshake */
hs_start = pos;
/* HandshakeType msg_type */
*pos++ = TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE;
/* uint24 length (to be filled) */
hs_length = pos;
pos += 3;
/* body - ServerDHParams */
/* dh_p */
if (pos + 2 + conn->cred->dh_p_len > end) {
wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
"dh_p");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
os_free(dh_ys);
return -1;
}
WPA_PUT_BE16(pos, conn->cred->dh_p_len);
pos += 2;
os_memcpy(pos, conn->cred->dh_p, conn->cred->dh_p_len);
pos += conn->cred->dh_p_len;
/* dh_g */
if (pos + 2 + conn->cred->dh_g_len > end) {
wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
"dh_g");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
os_free(dh_ys);
return -1;
}
WPA_PUT_BE16(pos, conn->cred->dh_g_len);
pos += 2;
os_memcpy(pos, conn->cred->dh_g, conn->cred->dh_g_len);
pos += conn->cred->dh_g_len;
/* dh_Ys */
if (pos + 2 + dh_ys_len > end) {
wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
"dh_Ys");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
os_free(dh_ys);
return -1;
}
WPA_PUT_BE16(pos, dh_ys_len);
pos += 2;
os_memcpy(pos, dh_ys, dh_ys_len);
pos += dh_ys_len;
os_free(dh_ys);
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
rhdr, end - rhdr, hs_start, pos - hs_start,
&rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
pos = rhdr + rlen;
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
*msgpos = pos;
return 0;
}
static int tls_write_server_certificate_request(struct tlsv1_server *conn,
u8 **msgpos, u8 *end)
{
u8 *pos, *rhdr, *hs_start, *hs_length;
size_t rlen;
if (!conn->verify_peer) {
wpa_printf(MSG_DEBUG, "TLSv1: No CertificateRequest needed");
return 0;
}
pos = *msgpos;
wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateRequest");
rhdr = pos;
pos += TLS_RECORD_HEADER_LEN;
/* opaque fragment[TLSPlaintext.length] */
/* Handshake */
hs_start = pos;
/* HandshakeType msg_type */
*pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST;
/* uint24 length (to be filled) */
hs_length = pos;
pos += 3;
/* body - CertificateRequest */
/*
* enum {
* rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4),
* (255)
* } ClientCertificateType;
* ClientCertificateType certificate_types<1..2^8-1>
*/
*pos++ = 1;
*pos++ = 1; /* rsa_sign */
/*
* opaque DistinguishedName<1..2^16-1>
* DistinguishedName certificate_authorities<3..2^16-1>
*/
/* TODO: add support for listing DNs for trusted CAs */
WPA_PUT_BE16(pos, 0);
pos += 2;
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
rhdr, end - rhdr, hs_start, pos - hs_start,
&rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
pos = rhdr + rlen;
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
*msgpos = pos;
return 0;
}
static int tls_write_server_hello_done(struct tlsv1_server *conn,
u8 **msgpos, u8 *end)
{
u8 *pos;
size_t rlen;
u8 payload[4];
wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone");
/* opaque fragment[TLSPlaintext.length] */
/* Handshake */
pos = payload;
/* HandshakeType msg_type */
*pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE;
/* uint24 length */
WPA_PUT_BE24(pos, 0);
pos += 3;
/* body - ServerHelloDone (empty) */
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
*msgpos, end - *msgpos, payload, pos - payload,
&rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
tls_verify_hash_add(&conn->verify, payload, pos - payload);
*msgpos += rlen;
return 0;
}
static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn,
u8 **msgpos, u8 *end)
{
size_t rlen;
u8 payload[1];
wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
payload[0] = TLS_CHANGE_CIPHER_SPEC;
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
*msgpos, end - *msgpos, payload, sizeof(payload),
&rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
if (tlsv1_record_change_write_cipher(&conn->rl) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for "
"record layer");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
*msgpos += rlen;
return 0;
}
static int tls_write_server_finished(struct tlsv1_server *conn,
u8 **msgpos, u8 *end)
{
u8 *pos, *hs_start;
size_t rlen, hlen;
u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN];
u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
pos = *msgpos;
wpa_printf(MSG_DEBUG, "TLSv1: Send Finished");
/* Encrypted Handshake Message: Finished */
#ifdef CONFIG_TLSV12
if (conn->rl.tls_version >= TLS_VERSION_1_2) {
hlen = SHA256_MAC_LEN;
if (wpa2_crypto_funcs.crypto_hash_finish) {
if (conn->verify.sha256_server == NULL ||
wpa2_crypto_funcs.crypto_hash_finish(conn->verify.sha256_server, hash, &hlen)
< 0) {
conn->verify.sha256_server = NULL;
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
} else {
conn->verify.sha256_server = NULL;
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
wpa_printf(MSG_ERROR, "In function %s, fail to register crypto_hash_finish function!\r\n", __FUNCTION__);
return -1;
}
conn->verify.sha256_server = NULL;
} else {
#endif /* CONFIG_TLSV12 */
hlen = MD5_MAC_LEN;
if (conn->verify.md5_server == NULL ||
crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) {
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
conn->verify.md5_server = NULL;
crypto_hash_finish(conn->verify.sha1_server, NULL, NULL);
conn->verify.sha1_server = NULL;
return -1;
}
conn->verify.md5_server = NULL;
hlen = SHA1_MAC_LEN;
if (conn->verify.sha1_server == NULL ||
crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN,
&hlen) < 0) {
conn->verify.sha1_server = NULL;
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
conn->verify.sha1_server = NULL;
hlen = MD5_MAC_LEN + SHA1_MAC_LEN;
#ifdef CONFIG_TLSV12
}
#endif /* CONFIG_TLSV12 */
if (tls_prf(conn->rl.tls_version,
conn->master_secret, TLS_MASTER_SECRET_LEN,
"server finished", hash, hlen,
verify_data + 1 + 3, TLS_VERIFY_DATA_LEN)) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
verify_data + 1 + 3, TLS_VERIFY_DATA_LEN);
/* Handshake */
pos = hs_start = verify_data;
/* HandshakeType msg_type */
*pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
/* uint24 length */
WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN);
pos += 3;
pos += TLS_VERIFY_DATA_LEN;
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
*msgpos, end - *msgpos, hs_start, pos - hs_start,
&rlen) < 0) {
wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
}
*msgpos += rlen;
return 0;
}
static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len)
{
u8 *msg, *end, *pos;
size_t msglen;
*out_len = 0;
msglen = 1000 + tls_server_cert_chain_der_len(conn);
msg = os_malloc(msglen);
if (msg == NULL)
return NULL;
pos = msg;
end = msg + msglen;
if (tls_write_server_hello(conn, &pos, end) < 0) {
os_free(msg);
return NULL;
}
if (conn->use_session_ticket) {
/* Abbreviated handshake using session ticket; RFC 4507 */
if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 ||
tls_write_server_finished(conn, &pos, end) < 0) {
os_free(msg);
return NULL;
}
*out_len = pos - msg;
conn->state = CHANGE_CIPHER_SPEC;
return msg;
}
/* Full handshake */
if (tls_write_server_certificate(conn, &pos, end) < 0 ||
tls_write_server_key_exchange(conn, &pos, end) < 0 ||
tls_write_server_certificate_request(conn, &pos, end) < 0 ||
tls_write_server_hello_done(conn, &pos, end) < 0) {
os_free(msg);
return NULL;
}
*out_len = pos - msg;
conn->state = CLIENT_CERTIFICATE;
return msg;
}
static u8 * tls_send_change_cipher_spec(struct tlsv1_server *conn,
size_t *out_len)
{
u8 *msg, *end, *pos;
*out_len = 0;
msg = os_malloc(1000);
if (msg == NULL)
return NULL;
pos = msg;
end = msg + 1000;
if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 ||
tls_write_server_finished(conn, &pos, end) < 0) {
os_free(msg);
return NULL;
}
*out_len = pos - msg;
wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed successfully");
conn->state = ESTABLISHED;
return msg;
}
u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len)
{
switch (conn->state) {
case SERVER_HELLO:
return tls_send_server_hello(conn, out_len);
case SERVER_CHANGE_CIPHER_SPEC:
return tls_send_change_cipher_spec(conn, out_len);
default:
if (conn->state == ESTABLISHED && conn->use_session_ticket) {
/* Abbreviated handshake was already completed. */
return NULL;
}
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while "
"generating reply", conn->state);
return NULL;
}
}
u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level,
u8 description, size_t *out_len)
{
u8 *alert, *pos, *length;
wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description);
*out_len = 0;
alert = os_malloc(10);
if (alert == NULL)
return NULL;
pos = alert;
/* TLSPlaintext */
/* ContentType type */
*pos++ = TLS_CONTENT_TYPE_ALERT;
/* ProtocolVersion version */
WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version :
TLS_VERSION);
pos += 2;
/* uint16 length (to be filled) */
length = pos;
pos += 2;
/* opaque fragment[TLSPlaintext.length] */
/* Alert */
/* AlertLevel level */
*pos++ = level;
/* AlertDescription description */
*pos++ = description;
WPA_PUT_BE16(length, pos - length - 2);
*out_len = pos - alert;
return alert;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
MODULE=esp_idf_wpa_supplicant_wpa2_utils
include $(RIOTBASE)/Makefile.base
# we have to do it in that way to avoid that $(RIOTBASE)/sys/include/crypto
# is found first
INCLUDES = -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/wpa_supplicant/include
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/wpa_supplicant/include/wpa
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/wpa_supplicant/port/include
INCLUDES += -I$(ESP32_SDK_DIR)/components/wpa_supplicant/include
INCLUDES += -I$(ESP32_SDK_DIR)/components/wpa_supplicant/port/include
CFLAGS += -D__ets__ -DESPRESSIF_USE -DESP32_IDF_CODE=1
include $(RIOTCPU)/$(CPU)/Makefile.include
INCLUDES += -I$(RIOTBASE)/core/include
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/include/log
INCLUDES += -I$(RIOTCPU)/$(CPU)/include
INCLUDES += -I$(RIOTBOARD)/$(BOARD)/include

View File

@ -0,0 +1,155 @@
/*
* Base64 encoding/decoding (RFC1341)
* Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "wpa/includes.h"
#include "os.h"
#include "wpa2/utils/base64.h"
static const unsigned char base64_table[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/**
* base64_encode - Base64 encode
* @src: Data to be encoded
* @len: Length of the data to be encoded
* @out_len: Pointer to output length variable, or %NULL if not used
* Returns: Allocated buffer of out_len bytes of encoded data,
* or %NULL on failure
*
* Caller is responsible for freeing the returned buffer. Returned buffer is
* nul terminated to make it easier to use as a C string. The nul terminator is
* not included in out_len.
*/
unsigned char * base64_encode(const unsigned char *src, size_t len,
size_t *out_len)
{
unsigned char *out, *pos;
const unsigned char *end, *in;
size_t olen;
int line_len;
olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
olen += olen / 72; /* line feeds */
olen++; /* nul termination */
if (olen < len)
return NULL; /* integer overflow */
out = os_malloc(olen);
if (out == NULL)
return NULL;
end = src + len;
in = src;
pos = out;
line_len = 0;
while (end - in >= 3) {
*pos++ = base64_table[in[0] >> 2];
*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
*pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
*pos++ = base64_table[in[2] & 0x3f];
in += 3;
line_len += 4;
if (line_len >= 72) {
*pos++ = '\n';
line_len = 0;
}
}
if (end - in) {
*pos++ = base64_table[in[0] >> 2];
if (end - in == 1) {
*pos++ = base64_table[(in[0] & 0x03) << 4];
*pos++ = '=';
} else {
*pos++ = base64_table[((in[0] & 0x03) << 4) |
(in[1] >> 4)];
*pos++ = base64_table[(in[1] & 0x0f) << 2];
}
*pos++ = '=';
line_len += 4;
}
if (line_len)
*pos++ = '\n';
*pos = '\0';
if (out_len)
*out_len = pos - out;
return out;
}
/**
* base64_decode - Base64 decode
* @src: Data to be decoded
* @len: Length of the data to be decoded
* @out_len: Pointer to output length variable
* Returns: Allocated buffer of out_len bytes of decoded data,
* or %NULL on failure
*
* Caller is responsible for freeing the returned buffer.
*/
unsigned char * base64_decode(const unsigned char *src, size_t len,
size_t *out_len)
{
unsigned char dtable[256], *out, *pos, block[4], tmp;
size_t i, count, olen;
int pad = 0;
os_memset(dtable, 0x80, 256);
for (i = 0; i < sizeof(base64_table) - 1; i++)
dtable[base64_table[i]] = (unsigned char) i;
dtable['='] = 0;
count = 0;
for (i = 0; i < len; i++) {
if (dtable[src[i]] != 0x80)
count++;
}
if (count == 0 || count % 4)
return NULL;
olen = count / 4 * 3;
pos = out = os_malloc(olen);
if (out == NULL)
return NULL;
count = 0;
for (i = 0; i < len; i++) {
tmp = dtable[src[i]];
if (tmp == 0x80)
continue;
if (src[i] == '=')
pad++;
block[count] = tmp;
count++;
if (count == 4) {
*pos++ = (block[0] << 2) | (block[1] >> 4);
*pos++ = (block[1] << 4) | (block[2] >> 2);
*pos++ = (block[2] << 6) | block[3];
count = 0;
if (pad) {
if (pad == 1)
pos--;
else if (pad == 2)
pos -= 2;
else {
/* Invalid padding */
os_free(out);
return NULL;
}
break;
}
}
}
*out_len = pos - out;
return out;
}

View File

@ -0,0 +1,114 @@
/*
* External password backend
* Copyright (c) 2012, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "wpa/includes.h"
#include "wpa/common.h"
#include "wpa2/utils/ext_password_i.h"
#ifdef CONFIG_EXT_PASSWORD_TEST
extern struct ext_password_backend ext_password_test;
#endif /* CONFIG_EXT_PASSWORD_TEST */
#ifdef CONFIG_EXT_PASSWORD
static const struct ext_password_backend *backends[] = {
#ifdef CONFIG_EXT_PASSWORD_TEST
&ext_password_test,
#endif /* CONFIG_EXT_PASSWORD_TEST */
NULL
};
#endif
struct ext_password_data {
const struct ext_password_backend *backend;
void *priv;
};
#ifdef CONFIG_EXT_PASSWORD
struct ext_password_data * ext_password_init(const char *backend,
const char *params)
{
struct ext_password_data *data;
int i;
data = (struct ext_password_data *)os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
for (i = 0; backends[i]; i++) {
if (os_strcmp(backends[i]->name, backend) == 0) {
data->backend = backends[i];
break;
}
}
if (!data->backend) {
os_free(data);
return NULL;
}
data->priv = data->backend->init(params);
if (data->priv == NULL) {
os_free(data);
return NULL;
}
return data;
}
void ext_password_deinit(struct ext_password_data *data)
{
if (data && data->backend && data->priv)
data->backend->deinit(data->priv);
os_free(data);
}
struct wpabuf * ext_password_get(struct ext_password_data *data,
const char *name)
{
if (data == NULL)
return NULL;
return data->backend->get(data->priv, name);
}
#endif /* CONFIG_EXT_PASSWORD */
struct wpabuf * ext_password_alloc(size_t len)
{
struct wpabuf *buf;
buf = wpabuf_alloc(len);
if (buf == NULL)
return NULL;
#ifdef __linux__
if (mlock(wpabuf_head(buf), wpabuf_len(buf)) < 0) {
wpa_printf(MSG_ERROR, "EXT PW: mlock failed: %s",
strerror(errno));
}
#endif /* __linux__ */
return buf;
}
#ifdef CONFIG_EXT_PASSWORD
void ext_password_free(struct wpabuf *pw)
{
if (pw == NULL)
return;
os_memset(wpabuf_mhead(pw), 0, wpabuf_len(pw));
#ifdef __linux__
if (munlock(wpabuf_head(pw), wpabuf_len(pw)) < 0) {
wpa_printf(MSG_ERROR, "EXT PW: munlock failed: %s",
strerror(errno));
}
#endif /* __linux__ */
wpabuf_free(pw);
}
#endif /* CONFIG_EXT_PASSWORD */

View File

@ -687,7 +687,7 @@ Furthermore, the following configuration parameters have to be defined:
Parameter | Default | Description Parameter | Default | Description
:-------------------|:--------------------------|:------------ :-------------------|:--------------------------|:------------
#ESP_WIFI_SSID | "RIOT_AP" | SSID of the AP to be used. #ESP_WIFI_SSID | "RIOT_AP" | SSID of the AP to be used.
#ESP_WIFI_PASS | "ThisistheRIOTporttoESP" | Passphrase used for the AP as clear text (max. 64 chars). #ESP_WIFI_PASS | - | Passphrase used for the AP as clear text (max. 64 chars).
#ESP_WIFI_STACKSIZE | #THREAD_STACKSIZE_DEFAULT |Stack size used for the WiFi netdev driver thread. #ESP_WIFI_STACKSIZE | #THREAD_STACKSIZE_DEFAULT |Stack size used for the WiFi netdev driver thread.
</center> </center>
@ -702,7 +702,11 @@ CFLAGS='-DESP_WIFI_SSID=\"MySSID\" -DESP_WIFI_PASS=\"MyPassphrase\"' \
make -C examples/gnrc_networking BOARD=... make -C examples/gnrc_networking BOARD=...
``` ```
@note The Wifi network interface (module `esp_wifi`) and the @note
- Module `esp_wifi` is not enabled automatically when module
`netdev_default` is used.
- Leave 'ESP_WIFI_PASS' undefined to connect to an open WiFi access point.
- The Wifi network interface (module `esp_wifi`) and the
[ESP-NOW network interface](#esp8266_esp_now_network_interface) [ESP-NOW network interface](#esp8266_esp_now_network_interface)
(module `esp_now`) can be used simultaneously, for example, to realize a (module `esp_now`) can be used simultaneously, for example, to realize a
border router for a mesh network which uses ESP-NOW. border router for a mesh network which uses ESP-NOW.

View File

@ -7,6 +7,11 @@ FLASH_MODE ?= dout
# SPECIAL module dependencies # SPECIAL module dependencies
# cannot be done in Makefile.dep since Makefile.dep is included too late # cannot be done in Makefile.dep since Makefile.dep is included too late
ifneq (,$(filter esp_wifi_enterprise,$(USEMODULE)))
FEATURES_REQUIRED += esp_wifi_enterprise
USEMODULE += esp_wifi
endif
ifneq (,$(filter netdev_default gnrc_netdev_default,$(USEMODULE))) ifneq (,$(filter netdev_default gnrc_netdev_default,$(USEMODULE)))
ifneq (,$(filter lwip,$(USEMODULE))) ifneq (,$(filter lwip,$(USEMODULE)))
# for lwip, use esp_wifi as default netdev if no other netdev is enabled # for lwip, use esp_wifi as default netdev if no other netdev is enabled

View File

@ -14,20 +14,39 @@
* @brief Network device driver for the ESP SoC WiFi interface * @brief Network device driver for the ESP SoC WiFi interface
* *
* @author Gunar Schorcht <gunar@schorcht.net> * @author Gunar Schorcht <gunar@schorcht.net>
*/
This module realizes a `netdev` interface for the built-in WiFi interface of This module realizes a `netdev` interface for the built-in WiFi interface of
ESP SoCs. To enable the WiFi interface, module `esp_wifi` has to be used. ESP SoCs.
@note On the ESP32 this `netdev` driver supports both WPA2 Personal Mode and
WPA2 Enterprise Mode. On ESP8266, only WPA2 Personal mode is currently
supported.
### WPA2 Personal Mode
To use the WiFi `netdev` driver in WPA2 Personal Mode with a
preshared key (PSK), module `esp_wifi` has to be enabled.
```
USEMODULE += esp_wifi
```
Furthermore, the following configuration parameters have to be defined: Furthermore, the following configuration parameters have to be defined:
Configuration Parameter | Description <center>
------------------------|------------
ESP_WIFI_SSID | SSID of the AP to be used. Parameter | Default | Description
ESP_WIFI_PASS | Passphrase used for the AP as clear text (max. 64 chars). :------------------|:--------------------------|:------------
ESP_WIFI_STACKSIZE | Stack size used for the WiFi netdev driver thread. ESP_WIFI_SSID | "RIOT_AP" | SSID of the AP to be used.
ESP_WIFI_PASS | - | Passphrase used for the AP as clear text (max. 64 chars).
ESP_WIFI_STACKSIZE | #THREAD_STACKSIZE_DEFAULT | Stack size used for the WiFi netdev driver thread.
</center>
These configuration parameter definitions, as well as enabling the `esp_wifi` These configuration parameter definitions, as well as enabling the `esp_wifi`
module, can be done either in the makefile of the project or at make command module, can be done either in the makefile of the project or at make command
line, e.g.: line, for example:
``` ```
USEMODULE=esp_wifi \ USEMODULE=esp_wifi \
@ -35,7 +54,63 @@ CFLAGS='-DESP_WIFI_SSID=\"MySSID\" -DESP_WIFI_PASS=\"MyPassphrase\"' \
make -C examples/gnrc_networking BOARD=... make -C examples/gnrc_networking BOARD=...
``` ```
@note The Wifi network interface (module `esp_wifi`) and the @note
- Module `esp_wifi` is not enabled automatically when module
`netdev_default` is used.
- Leave 'ESP_WIFI_PASS' undefined to connect to an open WiFi access point.
- The Wifi network interface (module `esp_wifi`) and the
ESP-NOW network interface (module `esp_now`)
can be used simultaneously, for example, to realize a border router for
a mesh network which uses ESP-NOW.
### WPA2 Enterprise Mode
To use the WiFi `netdev` driver in WPA2 Enterprise Mode with IEEE 802.1X/EAP
authentication, module `esp_wifi_enterprise` has to be enabled.
```
USEMODULE += esp_wifi_enterprise
```
It supports the following EAP authentication methods:
- PEAPv0
- PEAPv1
- TTLS
As inner (phase 2) EAP authentication method, only MSCHAPv2 is supported.
To use module `esp_wifi_enterprise` with these authentication methods, the
following configuration parameters have to be defined:
<center>
Parameter | Default | Description
:------------------|:----------|:------------
ESP_WIFI_SSID | "RIOT_AP" | SSID of the AP to be used.
ESP_WIFI_EAP_ID | none | Optional anonymous identity used in phase 1 (outer) EAP authentication. If it is not defined, the user name defined for phase 2 (inner) EAP authentication is used as idendity in phase 1.
ESP_WIFI_EAP_USER | none | User name used in phase 2 (inner) EAP authentication.
ESP_WIFI_EAP_PASS | none | Password used in phase 2 (inner) EAP authentication.
ESP_WIFI_STACKSIZE | #THREAD_STACKSIZE_DEFAULT | Stack size used for the WiFi netdev driver thread.
</center>
These configuration parameter definitions, as well as enabling the `esp_wifi`
module, can be done either in the makefile of the project or at make command
line, for example:
```
USEMODULE=esp_wifi_enterprise \
CFLAGS='-DESP_WIFI_SSID=\"MySSID\" -DESP_WIFI_EAP_ID=\"anonymous\" -DESP_WIFI_EAP_USER=\"MyUserName\" -DESP_WIFI_EAP_PASS=\"MyPassphrase\"' \
make -C examples/gnrc_networking BOARD=...
```
@note
- Since there is no possibility to add the CA certificate to the RIOT image,
the verification of the AP certificate is not yet supported.
- Module `esp_wifi_enterprise` is not enabled automatically when module
`netdev_default` is used.
- The Wifi network interface (module `esp_wifi_enterprise`) and the
ESP-NOW network interface (module `esp_now`) ESP-NOW network interface (module `esp_now`)
can be used simultaneously, for example, to realize a border router for can be used simultaneously, for example, to realize a border router for
a mesh network which uses ESP-NOW. a mesh network which uses ESP-NOW.

View File

@ -39,6 +39,10 @@
#include "nvs_flash/include/nvs_flash.h" #include "nvs_flash/include/nvs_flash.h"
#ifdef MODULE_ESP_WIFI_ENTERPRISE
#include "esp_wpa2.h"
#endif
#include "esp_wifi_params.h" #include "esp_wifi_params.h"
#include "esp_wifi_netdev.h" #include "esp_wifi_netdev.h"
@ -688,14 +692,16 @@ static const netdev_driver_t _esp_wifi_driver =
static wifi_config_t wifi_config_sta = { static wifi_config_t wifi_config_sta = {
.sta = { .sta = {
.ssid = ESP_WIFI_SSID, .ssid = ESP_WIFI_SSID,
#ifdef ESP_WIFI_PASS #if !defined(MODULE_ESP_WIFI_ENTERPRISE) && defined(ESP_WIFI_PASS)
.password = ESP_WIFI_PASS, .password = ESP_WIFI_PASS,
#endif #endif
.channel = 0, .channel = 0,
.scan_method = WIFI_ALL_CHANNEL_SCAN, .scan_method = WIFI_ALL_CHANNEL_SCAN,
.sort_method = WIFI_CONNECT_AP_BY_SIGNAL, .sort_method = WIFI_CONNECT_AP_BY_SIGNAL,
.threshold.rssi = -127, .threshold.rssi = -127,
#ifdef ESP_WIFI_PASS #if defined(MODULE_ESP_WIFI_ENTERPRISE)
.threshold.authmode = WIFI_AUTH_WPA2_ENTERPRISE
#elif defined(ESP_WIFI_PASS)
.threshold.authmode = WIFI_AUTH_WPA_WPA2_PSK .threshold.authmode = WIFI_AUTH_WPA_WPA2_PSK
#else #else
.threshold.authmode = WIFI_AUTH_OPEN .threshold.authmode = WIFI_AUTH_OPEN
@ -804,6 +810,27 @@ void esp_wifi_setup (esp_wifi_netdev_t* dev)
return; return;
} }
#ifdef MODULE_ESP_WIFI_ENTERPRISE
esp_wpa2_config_t wifi_config_wpa2 = WPA2_CONFIG_INIT_DEFAULT();
#ifdef ESP_WIFI_EAP_ID
esp_wifi_sta_wpa2_ent_set_identity((const unsigned char *)ESP_WIFI_EAP_ID,
strlen(ESP_WIFI_EAP_ID));
#endif
#if defined(ESP_WIFI_EAP_USER) && defined(ESP_WIFI_EAP_PASS)
ESP_WIFI_DEBUG("eap_user=%s eap_pass=%s\n",
ESP_WIFI_EAP_USER, ESP_WIFI_EAP_PASS);
esp_wifi_sta_wpa2_ent_set_username((const unsigned char *)ESP_WIFI_EAP_USER,
strlen(ESP_WIFI_EAP_USER));
esp_wifi_sta_wpa2_ent_set_password((const unsigned char *)ESP_WIFI_EAP_PASS,
strlen(ESP_WIFI_EAP_PASS));
#else
#error ESP_WIFI_EAP_USER and ESP_WIFI_EAP_PASS have to define the user name \
and the password for EAP phase 2 authentication in esp_wifi_enterprise
#endif
esp_wifi_sta_wpa2_ent_enable(&wifi_config_wpa2);
#endif
/* start the WiFi driver */ /* start the WiFi driver */
result = esp_wifi_start(); result = esp_wifi_start();
if (result != ESP_OK) { if (result != ESP_OK) {