From bd1e9b5ea70de26e828652d0383d246143553728 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Fri, 5 Jun 2020 14:20:04 +0200 Subject: [PATCH 1/7] openssl: basic support for errors and bio objects Closes https://github.com/espressif/esp-idf/issues/3406 --- components/openssl/CMakeLists.txt | 2 + components/openssl/Kconfig | 6 + components/openssl/OpenSSL-APIs.rst | 4 +- .../openssl/include/internal/ssl_pkey.h | 46 ++++ .../openssl/include/internal/ssl_stack.h | 43 ++++ .../openssl/include/internal/ssl_types.h | 38 ++- .../openssl/include/internal/ssl_x509.h | 45 +--- components/openssl/include/openssl/bio.h | 179 ++++++++++++++ .../openssl/include/openssl/openssl_err.h | 228 ++++++++++++++++++ components/openssl/include/openssl/ssl.h | 153 +++++++++--- components/openssl/library/ssl_bio.c | 210 ++++++++++++++++ components/openssl/library/ssl_err.c | 120 +++++++++ components/openssl/library/ssl_lib.c | 61 ++++- components/openssl/library/ssl_methods.c | 38 ++- components/openssl/library/ssl_pkey.c | 76 +++++- components/openssl/library/ssl_stack.c | 23 ++ components/openssl/library/ssl_x509.c | 59 ++--- components/openssl/platform/ssl_pm.c | 88 ++++++- components/openssl/test/CMakeLists.txt | 2 + components/openssl/test/component.mk | 4 + components/openssl/test/test_openssl.c | 138 +++++++++++ 21 files changed, 1427 insertions(+), 136 deletions(-) create mode 100644 components/openssl/include/openssl/bio.h create mode 100644 components/openssl/include/openssl/openssl_err.h create mode 100644 components/openssl/library/ssl_bio.c create mode 100644 components/openssl/library/ssl_err.c create mode 100644 components/openssl/test/CMakeLists.txt create mode 100644 components/openssl/test/component.mk create mode 100644 components/openssl/test/test_openssl.c diff --git a/components/openssl/CMakeLists.txt b/components/openssl/CMakeLists.txt index bba4006f1a..bf3d44057d 100644 --- a/components/openssl/CMakeLists.txt +++ b/components/openssl/CMakeLists.txt @@ -2,6 +2,8 @@ idf_component_register(SRCS "library/ssl_cert.c" "library/ssl_lib.c" "library/ssl_methods.c" "library/ssl_pkey.c" + "library/ssl_bio.c" + "library/ssl_err.c" "library/ssl_stack.c" "library/ssl_x509.c" "platform/ssl_pm.c" diff --git a/components/openssl/Kconfig b/components/openssl/Kconfig index 7bb271f281..aa60b15603 100644 --- a/components/openssl/Kconfig +++ b/components/openssl/Kconfig @@ -8,6 +8,12 @@ menu "OpenSSL" If the option is enabled, "SSL_DEBUG" works. + config OPENSSL_ERROR_STACK + bool "Enable OpenSSL error structure" + default y + help + Enable OpenSSL Error reporting + config OPENSSL_DEBUG_LEVEL int "OpenSSL debugging level" default 0 diff --git a/components/openssl/OpenSSL-APIs.rst b/components/openssl/OpenSSL-APIs.rst index 93e438dcf9..4feb3d3078 100644 --- a/components/openssl/OpenSSL-APIs.rst +++ b/components/openssl/OpenSSL-APIs.rst @@ -12,8 +12,8 @@ Chapter Introduction ==================== - Chapter 1. SSL Context Method Create -- Chapter 2. SSL Context Fucntion -- Chapter 3. SSL Fucntion +- Chapter 2. SSL Context Function +- Chapter 3. SSL Function - Chapter 4. SSL X509 Certification and Private Key Function diff --git a/components/openssl/include/internal/ssl_pkey.h b/components/openssl/include/internal/ssl_pkey.h index e790fcc995..0f361288f6 100644 --- a/components/openssl/include/internal/ssl_pkey.h +++ b/components/openssl/include/internal/ssl_pkey.h @@ -55,6 +55,52 @@ EVP_PKEY* d2i_PrivateKey(int type, const unsigned char **pp, long length); +/** + * @brief decodes and load a buffer BIO into a EVP key context. If '*a' is pointed to the + * private key, then load key into it. Or create a new private key object + * + * @param bp BIO object containing the key + * @param a Pointer to an existing EVP_KEY or NULL if a new key shall be created + * + * @return Created or updated EVP_PKEY + */ +EVP_PKEY *d2i_PrivateKey_bio(BIO *bp, EVP_PKEY **a); + +/** + * @brief Same as d2i_PrivateKey_bio + * + * @param bp BIO object containing the key + * @param a Pointer to an existing EVP_KEY or NULL if a new key shall be created + * + * @return Created or updated EVP_PKEY + */ +RSA *d2i_RSAPrivateKey_bio(BIO *bp,RSA **rsa); + +/** + * @brief loads a private key in PEM format from BIO object + * + * @param bp BIO object containing the key + * @param x Pointer to an existent PKEY or NULL if a new key shall be created + * @param cb Password callback (not used) + * @param u User context (not used) + * + * @return Created or updated EVP_PKEY + */ +EVP_PKEY *PEM_read_bio_PrivateKey(BIO *bp, EVP_PKEY **x, pem_password_cb *cb, void *u); + +/** + * @brief RSA key in PEM format from BIO object + * + * @param bp BIO object containing the key + * @param x Pointer to an existent PKEY or NULL if a new key shall be created + * @param cb Password callback (not used) + * @param u User context (not used) + * + * @return Created or updated EVP_PKEY + */ + +RSA *PEM_read_bio_RSAPrivateKey(BIO *bp, RSA **rsa, pem_password_cb *cb, void *u); + /** * @brief free a private key object * diff --git a/components/openssl/include/internal/ssl_stack.h b/components/openssl/include/internal/ssl_stack.h index 7a7051a026..f1efa5795f 100644 --- a/components/openssl/include/internal/ssl_stack.h +++ b/components/openssl/include/internal/ssl_stack.h @@ -17,6 +17,49 @@ } \ #define DEFINE_STACK_OF(t) SKM_DEFINE_STACK_OF(t, t, t) +typedef struct asn1_string_st ASN1_OCTET_STRING; + +struct stack_st_GENERAL_NAME; +typedef struct GENERAL_NAME_st { + int type; + union { + char *ptr; + struct asn1_string_st* dNSName; + ASN1_OCTET_STRING* iPAddress; + } d; +} GENERAL_NAME; + +typedef struct asn1_string_st ASN1_OCTET_STRING; +typedef struct X509_name_st X509_NAME; +typedef struct asn1_string_st ASN1_STRING; +typedef struct X509_name_entry_st X509_NAME_ENTRY; + +typedef struct asn1_string_st { + int type; + int length; + void *data; +} ASN1_IA5STRING; + +typedef STACK_OF(GENERAL_NAME) GENERAL_NAMES; + +/** + * @brief get nr of stack items + * + * @param sk Stack structure pointer + * + * @return number of items in the stack + */ +size_t sk_GENERAL_NAME_num(const struct stack_st_GENERAL_NAME *sk); + +/** + * @brief get GENERAL_NAME value from the stack + * + * @param sk Stack structure pointer + * @param i Index to stack item + * + * @return GENERAL_NAME object pointer + */ +GENERAL_NAME *sk_GENERAL_NAME_value(const struct stack_st_GENERAL_NAME *sk, size_t i); /** * @brief create a openssl stack object diff --git a/components/openssl/include/internal/ssl_types.h b/components/openssl/include/internal/ssl_types.h index 21ba69f4c7..38c9673548 100644 --- a/components/openssl/include/internal/ssl_types.h +++ b/components/openssl/include/internal/ssl_types.h @@ -20,6 +20,7 @@ #endif #include "ssl_code.h" +#include typedef void SSL_CIPHER; @@ -30,6 +31,8 @@ typedef void RSA; typedef void STACK; +typedef void DH; + #define ossl_inline inline #define SSL_METHOD_CALL(f, s, ...) s->method->func->ssl_##f(s, ##__VA_ARGS__) @@ -37,7 +40,7 @@ typedef void STACK; #define EVP_PKEY_METHOD_CALL(f, k, ...) k->method->pkey_##f(k, ##__VA_ARGS__) typedef int (*OPENSSL_sk_compfunc)(const void *, const void *); - +typedef int (*openssl_verify_callback)(int, X509_STORE_CTX *); struct stack_st; typedef struct stack_st OPENSSL_STACK; @@ -100,6 +103,8 @@ struct evp_pkey_st { void *pkey_pm; const PKEY_METHOD *method; + + int ref_counter; }; struct x509_st { @@ -152,8 +157,16 @@ struct X509_VERIFY_PARAM_st { }; struct bio_st { - const unsigned char * data; + + unsigned char * data; int dlen; + BIO* peer; + size_t offset; + size_t roffset; + size_t size; + size_t flags; + size_t type; + }; typedef enum { ALPN_INIT, ALPN_ENABLE, ALPN_DISABLE, ALPN_ERROR } ALPN_STATUS; @@ -166,6 +179,9 @@ struct ssl_alpn_st { const char *alpn_list[ALPN_LIST_MAX]; }; +typedef int pem_password_cb(char *buf, int size, int rwflag, void *userdata); + + struct ssl_ctx_st { int version; @@ -193,6 +209,16 @@ struct ssl_ctx_st int read_buffer_len; X509_VERIFY_PARAM param; + + void *default_passwd_callback_userdata; + + pem_password_cb *default_passwd_callback; + + struct stack_st_X509 *extra_certs; + + int max_version; + int min_version; + }; struct ssl_st @@ -236,6 +262,7 @@ struct ssl_st /* SSL low-level system arch point */ void *ssl_pm; + void *bio; }; struct ssl_method_st { @@ -299,6 +326,13 @@ struct pkey_method_st { int (*pkey_load)(EVP_PKEY *pkey, const unsigned char *buf, int len); }; +struct bio_method_st { + + unsigned type; + + unsigned size; +}; + typedef int (*next_proto_cb)(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, diff --git a/components/openssl/include/internal/ssl_x509.h b/components/openssl/include/internal/ssl_x509.h index e58439724c..88e46e2e2c 100644 --- a/components/openssl/include/internal/ssl_x509.h +++ b/components/openssl/include/internal/ssl_x509.h @@ -114,23 +114,6 @@ int SSL_use_certificate_ASN1(SSL *ssl, int len, const unsigned char *d); */ int X509_STORE_add_cert(X509_STORE *store, X509 *x); -/** - * @brief load data in BIO - * - * Normally BIO_write should append data but that doesn't happen here, and - * 'data' cannot be freed after the function is called, it should remain valid - * until BIO object is in use. - * - * @param b - pointer to BIO - * @param data - pointer to data - * @param dlen - data bytes - * - * @return result - * 0 : failed - * 1 : OK - */ -int BIO_write(BIO *b, const void *data, int dlen); - /** * @brief load a character certification context into system context. * @@ -145,28 +128,22 @@ int BIO_write(BIO *b, const void *data, int dlen); * * @return X509 certification object point */ -X509 * PEM_read_bio_X509(BIO *bp, X509 **x, void *cb, void *u); +X509 * PEM_read_bio_X509(BIO *bp, X509 **x, pem_password_cb cb, void *u); /** - * @brief create a BIO object - * - * @param method - pointer to BIO_METHOD + * @brief load a character certification context into system context. * - * @return pointer to BIO object - */ -BIO *BIO_new(void * method); - -/** - * @brief get the memory BIO method function - */ -void *BIO_s_mem(void); - -/** - * @brief free a BIO object + * Current implementation directly calls PEM_read_bio_X509 * - * @param x - pointer to BIO object + * @param bp - pointer to BIO + * @param buffer - pointer to the certification context memory + * @param cb - pointer to the callback (not implemented) + * @param u - pointer to arbitrary data (not implemented) + * + * @return X509 certification object point */ -void BIO_free(BIO *b); +X509 *PEM_read_bio_X509_AUX(BIO *bp, X509 **cert, pem_password_cb *cb, void *u); + #ifdef __cplusplus } diff --git a/components/openssl/include/openssl/bio.h b/components/openssl/include/openssl/bio.h new file mode 100644 index 0000000000..1fc049b650 --- /dev/null +++ b/components/openssl/include/openssl/bio.h @@ -0,0 +1,179 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _OPENSSL_BIO_H +#define _OPENSSL_BIO_H + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/* These are the 'types' of BIOs */ +#define BIO_TYPE_NONE 0 +#define BIO_TYPE_MEM (1 | 0x0400) +#define BIO_TYPE_BIO (19 | 0x0400) /* (half a) BIO pair */ + +/* Bio object flags */ +#define BIO_FLAGS_READ 0x01 +#define BIO_FLAGS_WRITE 0x02 + +#define BIO_should_read(a) BIO_test_flags(a, BIO_FLAGS_READ) +#define BIO_should_write(a) BIO_test_flags(a, BIO_FLAGS_WRITE) + +typedef struct bio_st BIO; +typedef struct bio_method_st BIO_METHOD; + +/** + * @brief Create a BIO object as a file type + * Current implementation return NULL as file types are discouraged on ESP platform + * + * @param filename Filename + * @param mode Mode + * + * @return BIO object + */ +BIO *BIO_new_file(const char *filename, const char *mode); + +/** + * @brief Create a BIO object as a membuf type + * Current implementation takes a shallow copy of the buffer + * + * @param buf Pointer to the buffer + * @param len Length of the buffer + * + * @return BIO object + */ +BIO *BIO_new_mem_buf(void *buf, int len); + +/** + * @brief create a BIO object + * + * @param method - pointer to BIO_METHOD + * + * @return pointer to BIO object + */ +BIO *BIO_new(BIO_METHOD * method); + +/** + * @brief get the memory BIO method function + */ +void *BIO_s_mem(void); + +/** + * @brief free a BIO object + * + * @param x - pointer to BIO object + */ +void BIO_free(BIO *b); + +/** + * @brief Create a connected pair of BIOs bio1, bio2 with write buffer sizes writebuf1 and writebuf2 + * + * @param out1 pointer to BIO1 + * @param writebuf1 write size of BIO1 (0 means default size will be used) + * @param out2 pointer to BIO2 + * @param writebuf2 write size of BIO2 (0 means default size will be used) + * + * @return result + * 0 : failed + * 1 : OK + */ +int BIO_new_bio_pair(BIO **out1, size_t writebuf1, BIO **out2, size_t writebuf2); + +/** + * @brief Write data to BIO + * + * BIO_TYPE_BIO behaves the same way as OpenSSL bio object, other BIO types mock + * this functionality to avoid excessive allocation/copy, so the 'data' cannot + * be freed after the function is called, it should remain valid until BIO object is in use. + * + * @param b - pointer to BIO + * @param data - pointer to data + * @param dlen - data bytes + * + * @return result + * -1, 0 : failed + * 1 : OK + */ +int BIO_write(BIO *b, const void *data, int dlen); + +/** + * @brief Read data from BIO + * + * BIO_TYPE_BIO behaves the same way as OpenSSL bio object. + * Other types just hold pointer + * + * @param b - pointer to BIO + * @param data - pointer to data + * @param dlen - data bytes + * + * @return result + * -1, 0 : failed + * 1 : OK + */ +int BIO_read(BIO *bio, void *data, int len); + +/** + * @brief Get number of pending characters in the BIOs write buffers. + * + * @param b Pointer to BIO + * + * @return Amount of pending data + */ +size_t BIO_wpending(const BIO *bio); + +/** + * @brief Get number of pending characters in the BIOs read buffers. + * + * @param b Pointer to BIO + * + * @return Amount of pending data + */ +size_t BIO_ctrl_pending(const BIO *bio); + +/** + * @brief Get the maximum length of data that can be currently written to the BIO + * + * @param b Pointer to BIO + * + * @return Max length of writable data + */ +size_t BIO_ctrl_get_write_guarantee(BIO *bio); + +/** + * @brief Returns the type of a BIO. + * + * @param b Pointer to BIO + * + * @return Type of the BIO object + */ +int BIO_method_type(const BIO *b); + +/** + * @brief Test flags of a BIO. + * + * @param b Pointer to BIO + * @param flags Flags + * + * @return BIO object flags masked with the supplied flags + */ +int BIO_test_flags(const BIO *b, int flags); + +#ifdef __cplusplus +} +#endif + +#endif //_OPENSSL_BIO_H diff --git a/components/openssl/include/openssl/openssl_err.h b/components/openssl/include/openssl/openssl_err.h new file mode 100644 index 0000000000..930807500e --- /dev/null +++ b/components/openssl/include/openssl/openssl_err.h @@ -0,0 +1,228 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _OPENSSL_ERR_H +#define _OPENSSL_ERR_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @note This file contains a very simple implementation of error stack provided + * OpenSSL library. It is OFF by default. + */ + +#define OPENSSL_PUT_SYSTEM_ERROR() \ + ERR_put_error(ERR_LIB_SYS, 0, 0, __FILE__, __LINE__); + +#define OPENSSL_PUT_LIB_ERROR(lib, code) \ + ERR_put_error(lib, 0, code, __FILE__, __LINE__); + +#define ERR_GET_LIB(packed_error) ((int)(((packed_error) >> 24) & 0xff)) +#define ERR_GET_REASON(packed_error) ((int)((packed_error) & 0xffff)) +#define ERR_R_PEM_LIB ERR_LIB_PEM +/* inherent openssl errors */ +# define ERR_R_FATAL 64 +# define ERR_R_MALLOC_FAILURE (1|ERR_R_FATAL) +# define ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED (2|ERR_R_FATAL) +# define ERR_R_PASSED_NULL_PARAMETER (3|ERR_R_FATAL) +# define ERR_R_INTERNAL_ERROR (4|ERR_R_FATAL) +# define ERR_R_DISABLED (5|ERR_R_FATAL) +# define ERR_R_INIT_FAIL (6|ERR_R_FATAL) +# define ERR_R_PASSED_INVALID_ARGUMENT (7) +# define ERR_R_OPERATION_FAIL (8|ERR_R_FATAL) +# define ERR_R_INVALID_PROVIDER_FUNCTIONS (9|ERR_R_FATAL) +# define ERR_R_INTERRUPTED_OR_CANCELLED (10) + +enum { + ERR_LIB_NONE = 1, + ERR_LIB_SYS, + ERR_LIB_BN, + ERR_LIB_RSA, + ERR_LIB_DH, + ERR_LIB_EVP, + ERR_LIB_BUF, + ERR_LIB_OBJ, + ERR_LIB_PEM, + ERR_LIB_DSA, + ERR_LIB_X509, + ERR_LIB_ASN1, + ERR_LIB_CONF, + ERR_LIB_CRYPTO, + ERR_LIB_EC, + ERR_LIB_SSL, + ERR_LIB_BIO, + ERR_LIB_PKCS7, + ERR_LIB_PKCS8, + ERR_LIB_X509V3, + ERR_LIB_RAND, + ERR_LIB_ENGINE, + ERR_LIB_OCSP, + ERR_LIB_UI, + ERR_LIB_COMP, + ERR_LIB_ECDSA, + ERR_LIB_ECDH, + ERR_LIB_HMAC, + ERR_LIB_DIGEST, + ERR_LIB_CIPHER, + ERR_LIB_HKDF, + ERR_LIB_USER, + ERR_NUM_LIBS +}; + +/** + * @brief clear the SSL error code + * + * @param none + * + * @return none + */ +void ERR_clear_error(void); + +/** + * @brief get the current SSL error code + * + * @param none + * + * @return current SSL error number + */ +uint32_t ERR_get_error(void); + +/** + * @brief peek the current SSL error code, not clearing it + * + * @param none + * + * @return current SSL error number + */ +uint32_t ERR_peek_error(void); + +/** + * @brief peek the last SSL error code, not clearing it + * + * @param none + * + * @return current SSL error number + */ +uint32_t ERR_peek_last_error(void); + +/** + * @brief register the SSL error strings + * + * @param none + * + * @return none + */ +void ERR_load_SSL_strings(void); + +/** + * @brief clear the SSL error code + * + * @param none + * + * @return none + */ +void ERR_clear_error(void); + +/** + * @brief peek the current SSL error code, not clearing it + * + * @param none + * + * @return current SSL error number + */ +uint32_t ERR_peek_error(void); + +/** + * @brief peek the last SSL error code, not clearing it + * + * @param none + * + * @return current SSL error number + */ +uint32_t ERR_peek_last_error(void); + +/** + * @brief capture the current error to the error structure + * + * @param library Related library + * @param unused Not used (used for compliant function prototype) + * @param reason The actual error code + * @param file File name of the error report + * @param line Line number of the error report + * + */ +void ERR_put_error(int library, int unused, int reason, const char *file, unsigned line); + +/** + * @brief Peek the current SSL error, not clearing it + * + * @param file file name of the reported error + * @param line line number of the reported error + * @param data Associated data to the reported error + * @param flags Flags associated to the error + * + * @return current SSL error number + */ +uint32_t ERR_peek_error_line_data(const char **file, int *line, + const char **data, int *flags); + +/** + * @brief Get the current SSL error + * + * @param file file name of the reported error + * @param line line number of the reported error + * @param data Associated data to the reported error + * @param flags Flags associated to the error + * + * @return current SSL error number + */ +uint32_t ERR_get_error_line_data(const char **file, int *line, + const char **data, int *flags); + +/** + * @brief API provided as a declaration only + * + */ +void SSL_load_error_strings(void); + +/** + * @brief API provided as a declaration only + * + */ +void ERR_free_strings(void); + +/** + * @brief API provided as a declaration only + * + */ +void ERR_remove_state(unsigned long pid); + +/** + * @brief Returns error string -- Not implemented + * + * @param packed_error Packed error code + * + * @return NULL + */ +const char *ERR_reason_error_string(uint32_t packed_error); + +#ifdef __cplusplus +} +#endif + +#endif // _OPENSSL_ERR_H diff --git a/components/openssl/include/openssl/ssl.h b/components/openssl/include/openssl/ssl.h index 88d7bca69d..6a39c7cd53 100644 --- a/components/openssl/include/openssl/ssl.h +++ b/components/openssl/include/openssl/ssl.h @@ -21,6 +21,8 @@ #include "internal/ssl_x509.h" #include "internal/ssl_pkey.h" +#include "openssl/bio.h" +#include "openssl/openssl_err.h" /* { @@ -297,6 +299,67 @@ const SSL_METHOD* SSLv3_server_method(void); */ const SSL_METHOD* TLS_server_method(void); +/** + * @brief create the target SSL context method + * + * @return the TLS any version SSL context method + */ +const SSL_METHOD* TLS_method(void); + +/** + * @brief create the target SSL context method + * + * @return the TLS1.2 version SSL context method + */ +const SSL_METHOD* TLSv1_2_method(void); + +/** + * @brief create the target SSL context method + * + * @return the TLS1.1 version SSL context method + */ +const SSL_METHOD* TLSv1_1_method(void); + +/** + * @brief create the target SSL context method + * + * @return the TLS1.0 version SSL context method + */ +const SSL_METHOD* TLSv1_method(void); + +/** + * @brief create the target SSL context method + * + * @return the SSLV3.0 version SSL context method + */ +const SSL_METHOD* SSLv3_method(void); + +/** + * @brief create the target SSL context method + * + * @param none + * + * @return the SSLV2.3 version SSL context method + */ +const SSL_METHOD* SSLv23_method(void); + +/** + * @brief Set minimum protocol version for defined context + * + * @param ctx SSL context + * + * @return 1 on success + */ +int SSL_CTX_set_min_proto_version(SSL_CTX *ctx, int version); + +/** + * @brief Set maximum protocol version for defined context + * + * @param ctx SSL context + * + * @return 1 on success + */ +int SSL_CTX_set_max_proto_version(SSL_CTX *ctx, int version); /** * @brief set the SSL context ALPN select callback function @@ -348,43 +411,6 @@ void SSL_CTX_set_next_proto_select_cb(SSL_CTX *ctx, void *arg), void *arg); -/** - * @brief get SSL error code - * - * @param ssl - SSL point - * @param ret_code - SSL return code - * - * @return SSL error number - */ -int SSL_get_error(const SSL *ssl, int ret_code); - -/** - * @brief clear the SSL error code - * - * @param none - * - * @return none - */ -void ERR_clear_error(void); - -/** - * @brief get the current SSL error code - * - * @param none - * - * @return current SSL error number - */ -int ERR_get_error(void); - -/** - * @brief register the SSL error strings - * - * @param none - * - * @return none - */ -void ERR_load_SSL_strings(void); - /** * @brief initialize the SSL library * @@ -1399,7 +1425,17 @@ SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl); * * @return application data */ -char *SSL_get_app_data(SSL *ssl); +void *SSL_get_app_data(SSL *ssl); + +/** + * @brief get SSL error code + * + * @param ssl - SSL point + * @param ret_code - SSL return code + * + * @return SSL error number + */ +int SSL_get_error(const SSL *ssl, int ret_code); /** * @brief get SSL cipher bits @@ -1667,7 +1703,7 @@ void SSL_set_accept_state(SSL *ssl); * * @return none */ -void SSL_set_app_data(SSL *ssl, char *arg); +void SSL_set_app_data(SSL *ssl, void *arg); /** * @brief set SSL BIO @@ -1756,7 +1792,7 @@ void SSL_set_timeout(SSL *ssl, long t); * * @return SSL statement string */ -char *SSL_state_string(const SSL *ssl); +const char *SSL_state_string(const SSL *ssl); /** * @brief get SSL statement long string @@ -1815,6 +1851,43 @@ const char *SSL_get_psk_identity_hint(SSL *ssl); */ const char *SSL_get_psk_identity(SSL *ssl); +/** + * @brief set the SSL verify depth of the SSL + * + * @param ssl - SSL context + * @param depth - Depth level to verify + * + */ +void SSL_set_verify_depth(SSL *ssl, int depth); + +/** + * @brief Get default verify callback + * + * @param ctx - SSL context + * @return verify_callback - verifying callback function + * + */ +openssl_verify_callback SSL_CTX_get_verify_callback(const SSL_CTX *ctx); + +/** + * @brief Get default verify callback + * + * @param ctx - SSL context + * @return verify_callback - verifying callback function + * + */ +openssl_verify_callback SSL_get_verify_callback(const SSL *s); + +/** + * @brief Frees RSA object + * + * Current implementation calls directly EVP_PKEY free + * + * @param r RSA object + * + */ +void RSA_free(RSA *r); + #ifdef __cplusplus } #endif diff --git a/components/openssl/library/ssl_bio.c b/components/openssl/library/ssl_bio.c new file mode 100644 index 0000000000..46d4616f10 --- /dev/null +++ b/components/openssl/library/ssl_bio.c @@ -0,0 +1,210 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ssl_lib.h" +#include "openssl/bio.h" +#include "ssl_dbg.h" +#include "openssl/openssl_err.h" + +#define DEFAULT_BIO_SIZE 1024 + +BIO *BIO_new_mem_buf(void *buf, int len) +{ + BIO_METHOD m = { .type = BIO_TYPE_MEM, .size = 0 }; + BIO *b = BIO_new(&m); + if (b) { + b->dlen = len; + b->data = buf; + } + return b; +} + +/** + * @brief create a BIO object + */ +BIO *BIO_new(BIO_METHOD * method) +{ + BIO *b = (BIO *)ssl_mem_zalloc(sizeof(BIO)); + if (!b) { + OPENSSL_PUT_LIB_ERROR(ERR_LIB_BIO, ERR_R_MALLOC_FAILURE); + goto err; + } + if (method) { + b->size = method->size; + b->type = method->type; + } else { + b->type = BIO_TYPE_NONE; + } + if ((b->type & BIO_TYPE_BIO) && b->size) { + b->data = ssl_mem_zalloc(b->size); + if (!b->data) { + OPENSSL_PUT_LIB_ERROR(ERR_LIB_BIO, ERR_R_MALLOC_FAILURE); + goto err; + } + } + return b; + +err: + if (b && (b->type&BIO_TYPE_BIO)) { + ssl_mem_free(b->data); + } + ssl_mem_free(b); + return NULL; +} + +/** + * @brief free a BIO object + */ +void BIO_free(BIO *b) +{ + if (b && (b->type&BIO_TYPE_BIO)) { + ssl_mem_free(b->data); + } + ssl_mem_free(b); +} + +int BIO_new_bio_pair(BIO **out1, size_t writebuf1, BIO **out2, size_t writebuf2) +{ + BIO *bio1 = NULL; + BIO *bio2 = NULL; + if (!writebuf1) { + writebuf1 = DEFAULT_BIO_SIZE; + } + if (!writebuf2) { + writebuf2 = DEFAULT_BIO_SIZE; + } + BIO_METHOD m1 = { + .size = writebuf1, + .type = BIO_TYPE_BIO, + }; + BIO_METHOD m2 = { + .size = writebuf1, + .type = BIO_TYPE_BIO, + }; + bio1 = BIO_new(&m1); + if (!bio1) { + goto err; + } + bio2 = BIO_new(&m2); + if (!bio2) { + goto err; + } + *out1 = bio1; + *out2 = bio2; + bio1->peer = bio2; + bio1->size = writebuf1; + bio2->peer = bio1; + bio2->size = writebuf2; + return 1; + +err: + if (bio1) + { + BIO_free(bio1); + *out1 = NULL; + } + if (bio2) + { + BIO_free(bio2); + *out2 = NULL; + } + return 0; + +} + +/** + * @brief get the memory BIO method function + */ +void *BIO_s_mem(void) +{ + return NULL; +} + +int BIO_method_type(const BIO *b) +{ + SSL_ASSERT1(b); + return b->type; +} + +/** + * @brief load data into BIO. + * + */ +int BIO_write(BIO *b, const void * data, int dlen) +{ + SSL_ASSERT1(b); + int remaining = b->size - b->offset; + if (remaining <= 0) { + b->flags |= BIO_FLAGS_WRITE; + return -1; + } + int len_to_write = dlen > remaining?remaining:dlen; + memcpy(b->data + b->offset, data, len_to_write); + b->offset += len_to_write; + b->dlen = b->offset; + if (len_to_write == dlen) { + b->flags &= ~BIO_FLAGS_WRITE; + } + return len_to_write; +} + +/** + * @brief Read from BIO. + * + */ +int BIO_read(BIO *bio, void *data, int len) +{ + SSL_ASSERT1(bio); + BIO *peer = bio->peer; + int remaining = peer->dlen - peer->roffset; + if (remaining <= 0) { + bio->flags |= BIO_FLAGS_READ; + return -1; + } + int len_to_read = remaining > len ? len : remaining; + memcpy(data, peer->data + peer->roffset, len_to_read); + peer->roffset += len_to_read; + if (len_to_read == len) { + bio->flags &= ~BIO_FLAGS_READ; + } + if (peer->offset) { + // shift data back to the beginning of the buffer + memmove(peer->data, peer->data+peer->roffset, peer->offset - peer->roffset); + peer->offset -= peer->roffset; + peer->roffset = 0; + peer->dlen = peer->offset; + } + return len_to_read; +} + +size_t BIO_wpending(const BIO *bio) +{ + return bio->dlen - bio->roffset; +} + +size_t BIO_ctrl_pending(const BIO *bio) +{ + return bio->peer->dlen - bio->peer->roffset; +} + +size_t BIO_ctrl_get_write_guarantee(BIO *b) +{ + return (long)b->size - b->dlen; +} + +int BIO_test_flags(const BIO *b, int flags) +{ + return (b->flags & flags); +} + diff --git a/components/openssl/library/ssl_err.c b/components/openssl/library/ssl_err.c new file mode 100644 index 0000000000..a4b2c04026 --- /dev/null +++ b/components/openssl/library/ssl_err.c @@ -0,0 +1,120 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ssl_dbg.h" + +struct err_error_st { + /* file contains the filename where the error occurred. */ + const char *file; + /* packed contains the error library and reason, as packed by ERR_PACK. */ + uint32_t packed; + /* line contains the line number where the error occurred. */ + uint32_t line; +}; + +#define ERR_NUM_ERRORS 4 + +typedef struct err_state_st { + /* errors contains the ERR_NUM_ERRORS most recent errors, organised as a ring + * buffer. */ + struct err_error_st errors[ERR_NUM_ERRORS]; + /* top contains the index one past the most recent error. If |top| equals + * |bottom| then the queue is empty. */ + unsigned top; + /* bottom contains the index of the last error in the queue. */ + unsigned bottom; +} ERR_STATE; + +#if CONFIG_OPENSSL_ERROR_STACK +static ERR_STATE s_err_state = { 0 }; +#endif + +void ERR_clear_error(void) +{ +#if CONFIG_OPENSSL_ERROR_STACK + memset(&s_err_state.errors[0], 0, sizeof(struct err_state_st)); + s_err_state.top = s_err_state.bottom = 0; +#endif +} + +static uint32_t ERR_get_peek_error_internal(const char **file, int *line, bool peak) +{ +#if CONFIG_OPENSSL_ERROR_STACK + if (s_err_state.top == s_err_state.bottom) { + return 0; + } + unsigned new_bottom = (s_err_state.bottom + 1) % ERR_NUM_ERRORS; + int err = s_err_state.errors[new_bottom].packed; + + if (file) { + *file = s_err_state.errors[new_bottom].file; + } + if (line) { + *line = s_err_state.errors[new_bottom].line; + } + + if (peak == false) { + memset(&s_err_state.errors[new_bottom], 0, sizeof(struct err_error_st)); + s_err_state.bottom = new_bottom; + } + + return err; +#else + return 0; +#endif +} + +uint32_t ERR_get_error(void) +{ + return ERR_get_peek_error_internal(NULL, NULL, false); +} + +uint32_t ERR_peek_error(void) +{ + return ERR_get_peek_error_internal(NULL, NULL, true); +} + +uint32_t ERR_peek_last_error(void) +{ + return ERR_get_peek_error_internal(NULL, NULL, true); +} + +uint32_t ERR_peek_error_line_data(const char **file, int *line, const char **data, int *flags) +{ + return ERR_get_peek_error_internal(file, line, true); +} + +uint32_t ERR_get_error_line_data(const char **file, int *line, const char **data, int *flags) +{ + return ERR_get_peek_error_internal(file, line, false); +} + +const char *ERR_reason_error_string(uint32_t packed_error) +{ + return NULL; +} + +void ERR_put_error(int library, int unused, int reason, const char *file, unsigned line) +{ +#if CONFIG_OPENSSL_ERROR_STACK + s_err_state.top = (s_err_state.top + 1) % ERR_NUM_ERRORS; + if (s_err_state.top == s_err_state.bottom) { + s_err_state.bottom = (s_err_state.bottom + 1) % ERR_NUM_ERRORS; + } + + s_err_state.errors[s_err_state.top].packed = (uint32_t)library<<24 | abs(reason); + s_err_state.errors[s_err_state.top].file = file; + s_err_state.errors[s_err_state.top].line = line; +#endif +} diff --git a/components/openssl/library/ssl_lib.c b/components/openssl/library/ssl_lib.c index 6fc863aa76..adfacb07ed 100644 --- a/components/openssl/library/ssl_lib.c +++ b/components/openssl/library/ssl_lib.c @@ -1476,6 +1476,46 @@ long SSL_CTX_get_default_read_ahead(SSL_CTX *ctx) return ctx->read_ahead; } +char *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx) +{ + SSL_ASSERT2(ctx); + + return NULL; +} + +int SSL_CTX_set_app_data(SSL_CTX *ctx, void *arg) +{ + SSL_ASSERT1(ctx); + + return 0; +} + +void *SSL_get_app_data(SSL *ssl) +{ + SSL_ASSERT2(ssl); + + return NULL; +} + +void SSL_set_app_data(SSL *ssl, void *arg) +{ + SSL_ASSERT3(ssl); +} + +void SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio) +{ + SSL_ASSERT3(ssl); + + ssl->bio = rbio; +} + +int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file) +{ + SSL_ASSERT1(1) + + return -1; +} + /** * @brief set SSL session time */ @@ -1572,6 +1612,26 @@ void SSL_set_verify(SSL *ssl, int mode, int (*verify_callback)(int, X509_STORE_C ssl->verify_callback = verify_callback; } +/** + * @brief get the SSL verify callback from the context + */ +openssl_verify_callback SSL_CTX_get_verify_callback(const SSL_CTX *ctx) +{ + SSL_ASSERT2(ctx); + + return ctx->default_verify_callback; +} + +/** + * @brief get the SSL verify callback from ssl pointer + */ +openssl_verify_callback SSL_get_verify_callback(const SSL *ssl) +{ + SSL_ASSERT2(ssl); + + return ssl->verify_callback; +} + /** * @brief set the ALPN protocols in the preferred order. SSL APIs require the * protocols in a format. mbedtls doesn't need @@ -1606,4 +1666,3 @@ int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const unsigned char *protos, unsigned ctx->ssl_alpn.alpn_list[i] = NULL; return 0; } - diff --git a/components/openssl/library/ssl_methods.c b/components/openssl/library/ssl_methods.c index 5c5f7f9d2a..2eaf90e4db 100644 --- a/components/openssl/library/ssl_methods.c +++ b/components/openssl/library/ssl_methods.c @@ -49,7 +49,7 @@ IMPLEMENT_TLS_METHOD(TLS1_1_VERSION, 1, TLS_method_func, TLSv1_1_server_method); IMPLEMENT_TLS_METHOD(TLS1_2_VERSION, 1, TLS_method_func, TLSv1_2_server_method); -IMPLEMENT_TLS_METHOD(TLS1_VERSION, 0, TLS_method_func, TLSv1_server_method); +IMPLEMENT_TLS_METHOD(TLS1_VERSION, 1, TLS_method_func, TLSv1_server_method); IMPLEMENT_SSL_METHOD(SSL3_VERSION, 1, TLS_method_func, SSLv3_server_method); @@ -58,11 +58,11 @@ IMPLEMENT_SSL_METHOD(SSL3_VERSION, 1, TLS_method_func, SSLv3_server_method); */ IMPLEMENT_TLS_METHOD(TLS_ANY_VERSION, -1, TLS_method_func, TLS_method); -IMPLEMENT_SSL_METHOD(TLS1_2_VERSION, -1, TLS_method_func, TLSv1_2_method); +IMPLEMENT_TLS_METHOD(TLS1_2_VERSION, -1, TLS_method_func, TLSv1_2_method); -IMPLEMENT_SSL_METHOD(TLS1_1_VERSION, -1, TLS_method_func, TLSv1_1_method); +IMPLEMENT_TLS_METHOD(TLS1_1_VERSION, -1, TLS_method_func, TLSv1_1_method); -IMPLEMENT_SSL_METHOD(TLS1_VERSION, -1, TLS_method_func, TLSv1_method); +IMPLEMENT_TLS_METHOD(TLS1_VERSION, -1, TLS_method_func, TLSv1_method); IMPLEMENT_SSL_METHOD(SSL3_VERSION, -1, TLS_method_func, SSLv3_method); @@ -79,3 +79,33 @@ IMPLEMENT_X509_METHOD(X509_method, IMPLEMENT_PKEY_METHOD(EVP_PKEY_method, pkey_pm_new, pkey_pm_free, pkey_pm_load); + +/** + * @brief Generic SSL/TLS methods + */ +const SSL_METHOD *SSLv23_method(void) +{ + return TLS_method(); +} + +const SSL_METHOD *SSLv23_server_method(void) +{ + return TLS_server_method(); +} + +const SSL_METHOD *SSLv23_client_method(void) +{ + return TLS_client_method(); +} + +int SSL_CTX_set_min_proto_version(SSL_CTX *ctx, int version) +{ + ctx->min_version = version; + return 1; +} + +int SSL_CTX_set_max_proto_version(SSL_CTX *ctx, int version) +{ + ctx->max_version = version; + return 1; +} diff --git a/components/openssl/library/ssl_pkey.c b/components/openssl/library/ssl_pkey.c index 567a33e2c2..0ede47864d 100644 --- a/components/openssl/library/ssl_pkey.c +++ b/components/openssl/library/ssl_pkey.c @@ -16,6 +16,7 @@ #include "ssl_methods.h" #include "ssl_dbg.h" #include "ssl_port.h" +#include "openssl/bio.h" /** * @brief create a private key object according to input private key @@ -31,6 +32,8 @@ EVP_PKEY* __EVP_PKEY_new(EVP_PKEY *ipk) goto no_mem; } + pkey->ref_counter = 1; + if (ipk) { pkey->method = ipk->method; } else { @@ -66,6 +69,10 @@ void EVP_PKEY_free(EVP_PKEY *pkey) { SSL_ASSERT3(pkey); + if (--pkey->ref_counter > 0) { + return; + } + EVP_PKEY_METHOD_CALL(free, pkey); ssl_mem_free(pkey); @@ -118,6 +125,60 @@ failed1: return NULL; } +EVP_PKEY *d2i_PrivateKey_bio(BIO *bp, EVP_PKEY **a) +{ + return d2i_PrivateKey(0, a, (const unsigned char **)&bp->data, bp->dlen); +} + +RSA *d2i_RSAPrivateKey_bio(BIO *bp,RSA **a) +{ + return d2i_PrivateKey_bio(bp, (EVP_PKEY**)a); +} + +RSA *PEM_read_bio_RSAPrivateKey(BIO *bp, RSA **x, pem_password_cb *cb, void *u) +{ + return PEM_read_bio_PrivateKey(bp, (EVP_PKEY**)x, cb, u); +} + +EVP_PKEY *PEM_read_bio_PrivateKey(BIO *bp, EVP_PKEY **pk, pem_password_cb *cb, void *u) +{ + + int m = 0; + int ret; + EVP_PKEY *x; + + SSL_ASSERT2(BIO_method_type(bp) & BIO_TYPE_MEM); + if (bp->data == NULL || bp->dlen == 0) { + return NULL; + } + if (pk && *pk) { + x = *pk; + } else { + x = EVP_PKEY_new(); + if (!x) { + SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "EVP_PKEY_new() return NULL"); + goto failed; + } + m = 1; + } + + ret = EVP_PKEY_METHOD_CALL(load, x, bp->data, bp->dlen); + if (ret) { + SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "EVP_PKEY_METHOD_CALL(load) return %d", ret); + goto failed; + } + + // If buffer successfully created a EVP_PKEY from the bio, mark the buffer as consumed + bp->data = NULL; + bp->dlen = 0; + return x; + + failed: + if (m) { + EVP_PKEY_free(x); + } + + return NULL;} /** * @brief set the SSL context private key */ @@ -132,6 +193,7 @@ int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) if (ctx->cert->pkey) EVP_PKEY_free(ctx->cert->pkey); + pkey->ref_counter++; ctx->cert->pkey = pkey; return 1; @@ -214,12 +276,15 @@ failed1: return 0; } +#define ESP_OPENSSL_FILES_NOT_SUPPORTED 1 /** * @brief load the private key file into SSL context */ int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) { - return 0; + // Using file name as private key is discouraged + SSL_ASSERT1(ESP_OPENSSL_FILES_NOT_SUPPORTED); + return -1; } /** @@ -227,7 +292,9 @@ int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) */ int SSL_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) { - return 0; + // Using file name as private key is discouraged + SSL_ASSERT1(ESP_OPENSSL_FILES_NOT_SUPPORTED); + return -1; } /** @@ -237,3 +304,8 @@ int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const unsigned char *d, long le { return SSL_CTX_use_PrivateKey_ASN1(0, ctx, d, len); } + +void RSA_free (RSA *r) +{ + EVP_PKEY_free(r); +} diff --git a/components/openssl/library/ssl_stack.c b/components/openssl/library/ssl_stack.c index da836daf9c..4f80a871c7 100644 --- a/components/openssl/library/ssl_stack.c +++ b/components/openssl/library/ssl_stack.c @@ -25,6 +25,29 @@ /** * @brief create a openssl stack object */ +typedef struct stack_st_tag { + size_t num; + void **data; +} _STACK; + + +GENERAL_NAME *sk_GENERAL_NAME_value(const struct stack_st_GENERAL_NAME *sk, size_t i) +{ + if (!sk || i >= ((_STACK*)sk)->num) { + return NULL; + } + return ((_STACK*)sk)->data[i]; +} + + +size_t sk_GENERAL_NAME_num(const struct stack_st_GENERAL_NAME *sk) +{ + if (sk == NULL) { + return 0; + } + return ((_STACK*)sk)->num; +} + OPENSSL_STACK* OPENSSL_sk_new(OPENSSL_sk_compfunc c) { OPENSSL_STACK *stack; diff --git a/components/openssl/library/ssl_x509.c b/components/openssl/library/ssl_x509.c index 91c2a64efa..64f4ad253b 100644 --- a/components/openssl/library/ssl_x509.c +++ b/components/openssl/library/ssl_x509.c @@ -42,7 +42,7 @@ X509* __X509_new(X509 *ix) x->ref_counter = 1; - if (ix) + if (ix && ix->method) x->method = ix->method; else x->method = X509_method(); @@ -205,6 +205,7 @@ int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) X509_free(ctx->cert->x509); ctx->cert->x509 = x; + x->ref_counter++; return 1; } @@ -227,6 +228,11 @@ int SSL_use_certificate(SSL *ssl, X509 *x) return 1; } +long SSL_CTX_add_extra_chain_cert(SSL_CTX *ctx, X509 *x) +{ + return SSL_CTX_use_certificate(ctx, x); +} + /** * @brief get the SSL certification point */ @@ -252,12 +258,13 @@ int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, goto failed1; } - ret = SSL_CTX_use_certificate(ctx, x); + ret = SSL_CTX_use_certificate(ctx, x); // This uses the "x" so increments ref_count if (!ret) { SSL_DEBUG(SSL_PKEY_ERROR_LEVEL, "SSL_CTX_use_certificate() return %d", ret); goto failed2; } + X509_free(x); // decrements ref_count, so in case of happy flow doesn't free the "x" return 1; failed2: @@ -344,41 +351,21 @@ int X509_STORE_add_cert(X509_STORE *store, X509 *x) { return 1; } -/** - * @brief create a BIO object - */ -BIO *BIO_new(void *method) { - BIO *b = (BIO *)malloc(sizeof(BIO)); - return b; -} - -/** - * @brief load data into BIO. - * - * Normally BIO_write should append data but doesn't happen here, and - * 'data' cannot be freed after the function is called, it should remain valid - * until BIO object is in use. - */ -int BIO_write(BIO *b, const void * data, int dlen) { - b->data = data; - b->dlen = dlen; - return 1; -} - /** * @brief load a character certification context into system context. * * If '*cert' is pointed to the certification, then load certification * into it, or create a new X509 certification object. */ -X509 * PEM_read_bio_X509(BIO *bp, X509 **cert, void *cb, void *u) { +X509 * PEM_read_bio_X509(BIO *bp, X509 **cert, pem_password_cb cb, void *u) { int m = 0; int ret; X509 *x; - SSL_ASSERT2(bp->data); - SSL_ASSERT2(bp->dlen); - + SSL_ASSERT2(BIO_method_type(bp) & BIO_TYPE_MEM); + if (bp->data == NULL || bp->dlen == 0) { + return NULL; + } if (cert && *cert) { x = *cert; } else { @@ -396,6 +383,9 @@ X509 * PEM_read_bio_X509(BIO *bp, X509 **cert, void *cb, void *u) { goto failed; } + // If buffer successfully created a X509 from the bio, mark the buffer as consumed + bp->data = NULL; + bp->dlen = 0; return x; failed: @@ -406,11 +396,9 @@ failed: return NULL; } -/** - * @brief get the memory BIO method function - */ -void *BIO_s_mem(void) { - return NULL; +X509 *PEM_read_bio_X509_AUX(BIO *bp, X509 **cert, pem_password_cb *cb, void *u) +{ + return PEM_read_bio_X509(bp, cert, cb, u); } /** @@ -419,10 +407,3 @@ void *BIO_s_mem(void) { X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *ctx) { return (X509_STORE *)ctx; } - -/** - * @brief free a BIO object - */ -void BIO_free(BIO *b) { - free(b); -} diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 1448faa4ec..f971b1747c 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -24,6 +24,8 @@ #include "mbedtls/ctr_drbg.h" #include "mbedtls/error.h" #include "mbedtls/certs.h" +#include "openssl/bio.h" +#include "openssl/openssl_err.h" #define X509_INFO_STRING_LENGTH 8192 @@ -87,6 +89,39 @@ static void ssl_platform_debug(void *ctx, int level, } #endif +int mbedtls_ssl_send(void *ctx, const unsigned char *buf, size_t len ) +{ + BIO *bio = ctx; + int written = BIO_write(bio, buf, len); + if (written <= 0 && BIO_should_write(bio)) { + return MBEDTLS_ERR_SSL_WANT_WRITE; + } + return written; +} + +static int mbedtls_ssl_recv(void *ctx, unsigned char *buf, size_t len ) +{ + BIO *bio = ctx; + int read = BIO_read(bio, buf, len); + if (read <= 0 && BIO_should_read(bio)) { + return MBEDTLS_ERR_SSL_WANT_READ; + } + return read; +} + +static int ssl_pm_reload_crt(SSL *ssl); + +static int get_mbedtls_minor_ssl_version(int openssl_version_nr) +{ + if (TLS1_2_VERSION == openssl_version_nr) + return MBEDTLS_SSL_MINOR_VERSION_3; + if (TLS1_1_VERSION ==openssl_version_nr) + return MBEDTLS_SSL_MINOR_VERSION_2; + if (TLS1_VERSION == openssl_version_nr) + return MBEDTLS_SSL_MINOR_VERSION_1; + // SSLv3.0 otherwise + return MBEDTLS_SSL_MINOR_VERSION_0; +} /** * @brief create SSL low-level object */ @@ -99,13 +134,13 @@ int ssl_pm_new(SSL *ssl) size_t pers_len = sizeof(pers); int endpoint; - int version; const SSL_METHOD *method = ssl->method; ssl_pm = ssl_mem_zalloc(sizeof(struct ssl_pm)); if (!ssl_pm) { SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (ssl_pm)"); + OPENSSL_PUT_LIB_ERROR(ERR_LIB_SYS, ERR_R_MALLOC_FAILURE); goto no_mem; } @@ -122,6 +157,7 @@ int ssl_pm_new(SSL *ssl) ret = mbedtls_ctr_drbg_seed(&ssl_pm->ctr_drbg, mbedtls_entropy_func, &ssl_pm->entropy, pers, pers_len); if (ret) { SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ctr_drbg_seed() return -0x%x", -ret); + OPENSSL_PUT_LIB_ERROR(ERR_LIB_RAND, ret); goto mbedtls_err1; } @@ -133,21 +169,16 @@ int ssl_pm_new(SSL *ssl) ret = mbedtls_ssl_config_defaults(&ssl_pm->conf, endpoint, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); if (ret) { SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_config_defaults() return -0x%x", -ret); + OPENSSL_PUT_LIB_ERROR(ERR_LIB_CONF, ret); goto mbedtls_err2; } if (TLS_ANY_VERSION != ssl->version) { - if (TLS1_2_VERSION == ssl->version) - version = MBEDTLS_SSL_MINOR_VERSION_3; - else if (TLS1_1_VERSION == ssl->version) - version = MBEDTLS_SSL_MINOR_VERSION_2; - else if (TLS1_VERSION == ssl->version) - version = MBEDTLS_SSL_MINOR_VERSION_1; - else - version = MBEDTLS_SSL_MINOR_VERSION_0; + int min_version = ssl->ctx->min_version ? ssl->ctx->min_version : ssl->version; + int max_version = ssl->ctx->max_version ? ssl->ctx->max_version : ssl->version; - mbedtls_ssl_conf_max_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, version); - mbedtls_ssl_conf_min_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, version); + mbedtls_ssl_conf_max_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, get_mbedtls_minor_ssl_version(max_version)); + mbedtls_ssl_conf_min_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, get_mbedtls_minor_ssl_version(min_version)); } else { mbedtls_ssl_conf_max_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); mbedtls_ssl_conf_min_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_0); @@ -158,6 +189,7 @@ int ssl_pm_new(SSL *ssl) mbedtls_ssl_conf_alpn_protocols( &ssl_pm->conf, ssl->ctx->ssl_alpn.alpn_list ); #else SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "CONFIG_MBEDTLS_SSL_ALPN must be enabled to use ALPN", -1); + OPENSSL_PUT_LIB_ERROR(ERR_LIB_SYS, ERR_R_FATAL); #endif // MBEDTLS_SSL_ALPN } mbedtls_ssl_conf_rng(&ssl_pm->conf, mbedtls_ctr_drbg_random, &ssl_pm->ctr_drbg); @@ -172,12 +204,16 @@ int ssl_pm_new(SSL *ssl) ret = mbedtls_ssl_setup(&ssl_pm->ssl, &ssl_pm->conf); if (ret) { SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_setup() return -0x%x", -ret); + OPENSSL_PUT_LIB_ERROR(ERR_LIB_CONF, ret); goto mbedtls_err2; } mbedtls_ssl_set_bio(&ssl_pm->ssl, &ssl_pm->fd, mbedtls_net_send, mbedtls_net_recv, NULL); ssl->ssl_pm = ssl_pm; + ret = ssl_pm_reload_crt(ssl); + if (ret) + return 0; return 0; @@ -247,6 +283,7 @@ static int ssl_pm_reload_crt(SSL *ssl) if (ret) { SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_conf_own_cert() return -0x%x", -ret); + OPENSSL_PUT_LIB_ERROR(ERR_LIB_X509, ret); ret = -1; } @@ -278,6 +315,10 @@ int ssl_pm_handshake(SSL *ssl) int ret; struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + if (ssl->bio) { + mbedtls_ssl_set_bio(&ssl_pm->ssl, ssl->bio, mbedtls_ssl_send, mbedtls_ssl_recv, NULL); + } + ret = ssl_pm_reload_crt(ssl); if (ret) return 0; @@ -286,14 +327,24 @@ int ssl_pm_handshake(SSL *ssl) while((ret = mbedtls_handshake(&ssl_pm->ssl)) != 0) { if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + // exit handshake in case of any other error break; + } else if (ssl->bio) { + // exit even if wanted read/write if BIO used + if (ret == MBEDTLS_ERR_SSL_WANT_READ) { + ssl->rwstate = SSL_READING; + } else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + ssl->rwstate = SSL_WRITING; + } + return ret; } } ssl_speed_up_exit(); - + ssl->rwstate = SSL_NOTHING; if (ret) { SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_handshake() return -0x%x", -ret); + OPENSSL_PUT_LIB_ERROR(ERR_LIB_SSL, ret); ret = 0; } else { struct x509_pm *x509_pm = (struct x509_pm *)ssl->session->peer->x509_pm; @@ -313,6 +364,7 @@ int ssl_pm_shutdown(SSL *ssl) ret = mbedtls_ssl_close_notify(&ssl_pm->ssl); if (ret) { SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_close_notify() return -0x%x", -ret); + OPENSSL_PUT_LIB_ERROR(ERR_LIB_SSL, ret); ret = -1; } else { struct x509_pm *x509_pm = (struct x509_pm *)ssl->session->peer->x509_pm; @@ -337,6 +389,7 @@ int ssl_pm_read(SSL *ssl, void *buffer, int len) ret = mbedtls_ssl_read(&ssl_pm->ssl, buffer, len); if (ret < 0) { SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_read() return -0x%x", -ret); + OPENSSL_PUT_LIB_ERROR(ERR_LIB_SSL, ret); ret = -1; } @@ -351,6 +404,7 @@ int ssl_pm_send(SSL *ssl, const void *buffer, int len) ret = mbedtls_ssl_write(&ssl_pm->ssl, buffer, len); if (ret < 0) { SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_write() return -0x%x", -ret); + OPENSSL_PUT_LIB_ERROR(ERR_LIB_SSL, ret); ret = -1; } @@ -463,12 +517,14 @@ int x509_pm_show_info(X509 *x) buf = ssl_mem_malloc(X509_INFO_STRING_LENGTH); if (!buf) { SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (buf)"); + OPENSSL_PUT_LIB_ERROR(ERR_LIB_SYS, ERR_R_MALLOC_FAILURE); goto no_mem; } ret = mbedtls_x509_crt_info(buf, X509_INFO_STRING_LENGTH - 1, "", x509_crt); if (ret <= 0) { SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_x509_crt_info() return -0x%x", -ret); + OPENSSL_PUT_LIB_ERROR(ERR_LIB_X509, ret); goto mbedtls_err1; } @@ -493,6 +549,7 @@ int x509_pm_new(X509 *x, X509 *m_x) x509_pm = ssl_mem_zalloc(sizeof(struct x509_pm)); if (!x509_pm) { SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (x509_pm)"); + OPENSSL_PUT_LIB_ERROR(ERR_LIB_SYS, ERR_R_MALLOC_FAILURE); goto failed1; } @@ -538,6 +595,7 @@ int x509_pm_load(X509 *x, const unsigned char *buffer, int len) x509_pm->x509_crt = ssl_mem_malloc(sizeof(mbedtls_x509_crt)); if (!x509_pm->x509_crt) { SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (x509_pm->x509_crt)"); + OPENSSL_PUT_LIB_ERROR(ERR_LIB_SYS, ERR_R_MALLOC_FAILURE); goto no_mem; } } @@ -545,6 +603,7 @@ int x509_pm_load(X509 *x, const unsigned char *buffer, int len) load_buf = ssl_mem_malloc(len + 1); if (!load_buf) { SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (load_buf)"); + OPENSSL_PUT_LIB_ERROR(ERR_LIB_SYS, ERR_R_MALLOC_FAILURE); goto failed; } @@ -558,6 +617,7 @@ int x509_pm_load(X509 *x, const unsigned char *buffer, int len) if (ret) { SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_x509_crt_parse return -0x%x", -ret); + OPENSSL_PUT_LIB_ERROR(ERR_LIB_X509, ret); goto failed; } @@ -618,6 +678,7 @@ int pkey_pm_load(EVP_PKEY *pk, const unsigned char *buffer, int len) pkey_pm->pkey = ssl_mem_malloc(sizeof(mbedtls_pk_context)); if (!pkey_pm->pkey) { SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (pkey_pm->pkey)"); + OPENSSL_PUT_LIB_ERROR(ERR_LIB_SYS, ERR_R_MALLOC_FAILURE); goto no_mem; } } @@ -625,6 +686,7 @@ int pkey_pm_load(EVP_PKEY *pk, const unsigned char *buffer, int len) load_buf = ssl_mem_malloc(len + 1); if (!load_buf) { SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "no enough memory > (load_buf)"); + OPENSSL_PUT_LIB_ERROR(ERR_LIB_SYS, ERR_R_MALLOC_FAILURE); goto failed; } @@ -638,6 +700,7 @@ int pkey_pm_load(EVP_PKEY *pk, const unsigned char *buffer, int len) if (ret) { SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_pk_parse_key return -0x%x", -ret); + OPENSSL_PUT_LIB_ERROR(ERR_LIB_PKCS8, ret); goto failed; } @@ -667,6 +730,7 @@ long ssl_pm_get_verify_result(const SSL *ssl) ret = mbedtls_ssl_get_verify_result(&ssl_pm->ssl); if (ret) { SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_get_verify_result() return 0x%x", ret); + OPENSSL_PUT_LIB_ERROR(ERR_LIB_SSL, ret); verify_result = X509_V_ERR_UNSPECIFIED; } else verify_result = X509_V_OK; diff --git a/components/openssl/test/CMakeLists.txt b/components/openssl/test/CMakeLists.txt new file mode 100644 index 0000000000..28167230df --- /dev/null +++ b/components/openssl/test/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRC_DIRS "." + PRIV_REQUIRES unity test_utils openssl) \ No newline at end of file diff --git a/components/openssl/test/component.mk b/components/openssl/test/component.mk new file mode 100644 index 0000000000..5be873488b --- /dev/null +++ b/components/openssl/test/component.mk @@ -0,0 +1,4 @@ +# +#Component Makefile +# +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive \ No newline at end of file diff --git a/components/openssl/test/test_openssl.c b/components/openssl/test/test_openssl.c new file mode 100644 index 0000000000..f0078847e2 --- /dev/null +++ b/components/openssl/test/test_openssl.c @@ -0,0 +1,138 @@ +#include "test_utils.h" +#include "openssl/ssl.h" +#include "unity.h" + +/** + * @brief This simple test suite is taken from OpenSSL err_test.cc and bio_test.cc, the relevant test + * cases were adopted to the supported fraction of OpenSSL port in esp-idf + */ + +// +// Basic error stack support and test +// +#define ERR_NUM_ERRORS 4 + +TEST_CASE("ErrTest, Overflow", "[openssl]") +{ + + for (unsigned i = 0; i < ERR_NUM_ERRORS*2; i++) { + ERR_put_error(1, 0 /* unused */, i+1, "test", 1); + } + + for (unsigned i = 0; i < ERR_NUM_ERRORS - 1; i++) { + uint32_t err = ERR_get_error(); + /* Errors are returned in order they were pushed, with the least recent ones + * removed, up to |ERR_NUM_ERRORS - 1| errors. So the errors returned are + * |ERR_NUM_ERRORS + 2| through |ERR_NUM_ERRORS * 2|, inclusive. */ + TEST_ASSERT_NOT_EQUAL(0u, err); + TEST_ASSERT_EQUAL(i + ERR_NUM_ERRORS + 2, ERR_GET_REASON(err)); + } + + TEST_ASSERT_EQUAL(0u, ERR_get_error()); +} + +TEST_CASE("ErrTest, PutError", "[openssl]") +{ + TEST_ASSERT_EQUAL(0u, ERR_get_error()); // ERR_get_error returned value before an error was added. + + ERR_put_error(1, 0 /* unused */, 2, "test", 4); + + int peeked_line, line, peeked_flags, flags; + const char *peeked_file, *file, *peeked_data, *data; + uint32_t peeked_packed_error = + ERR_peek_error_line_data(&peeked_file, &peeked_line, &peeked_data, + &peeked_flags); + uint32_t packed_error = ERR_get_error_line_data(&file, &line, &data, &flags); + + TEST_ASSERT_EQUAL(peeked_packed_error, packed_error); + TEST_ASSERT_EQUAL(peeked_file, file); + + TEST_ASSERT_EQUAL_STRING("test", file); + TEST_ASSERT_EQUAL(4, line); + TEST_ASSERT_EQUAL(1, ERR_GET_LIB(packed_error)); + TEST_ASSERT_EQUAL(2, ERR_GET_REASON(packed_error)); +} + +TEST_CASE("ErrTest, ClearError", "[openssl]") +{ + TEST_ASSERT_EQUAL(0u, ERR_get_error()); // ERR_get_error returned value before an error was added. + + ERR_put_error(1, 0 /* unused */, 2, "test", 4); + ERR_clear_error(); + + // The error queue should be cleared. + TEST_ASSERT_EQUAL(0u, ERR_get_error()); +} + +// +// Simplified BIO support and check +// +TEST_CASE("BioTest, TestPair", "[openssl]") +{ + BIO *bio1, *bio2; + TEST_ASSERT_NOT_EQUAL(0, BIO_new_bio_pair(&bio1, 10, &bio2, 10)); + TEST_ASSERT_EQUAL(BIO_ctrl_get_write_guarantee(bio1), 10); + + // Data written in one end may be read out the other. + char buf[20]; + TEST_ASSERT_EQUAL(5, BIO_write(bio1, "12345", 5)); + TEST_ASSERT_EQUAL(5, BIO_ctrl_get_write_guarantee(bio1)); + TEST_ASSERT_EQUAL(5, BIO_read(bio2, buf, sizeof(buf))); + TEST_ASSERT_EQUAL_UINT8_ARRAY("12345", buf, 5); + TEST_ASSERT_EQUAL(10, BIO_ctrl_get_write_guarantee(bio1)); + + // Attempting to write more than 10 bytes will write partially. + TEST_ASSERT_EQUAL(10, BIO_write(bio1, "1234567890___", 13)); + TEST_ASSERT_EQUAL(0, BIO_ctrl_get_write_guarantee(bio1)); + TEST_ASSERT_EQUAL(-1, BIO_write(bio1, "z", 1)); + TEST_ASSERT_TRUE(BIO_should_write(bio1)); + TEST_ASSERT_EQUAL(10, BIO_read(bio2, buf, sizeof(buf))); + TEST_ASSERT_EQUAL_UINT8_ARRAY("1234567890", buf, 10); + TEST_ASSERT_EQUAL(10, BIO_ctrl_get_write_guarantee(bio1)); + + // Unsuccessful reads update the read request. + TEST_ASSERT_EQUAL(-1, BIO_read(bio2, buf, 5)); + TEST_ASSERT_TRUE(BIO_should_read(bio2)); + + // The read request is clamped to the size of the buffer. + TEST_ASSERT_EQUAL(-1, BIO_read(bio2, buf, 20)); + TEST_ASSERT_TRUE(BIO_should_read(bio2)); + + // Data may be written and read in chunks. + TEST_ASSERT_EQUAL(BIO_write(bio1, "12345", 5), 5); + TEST_ASSERT_EQUAL(5, BIO_ctrl_get_write_guarantee(bio1)); + TEST_ASSERT_EQUAL(5, BIO_write(bio1, "67890___", 8)); + TEST_ASSERT_EQUAL(0, BIO_ctrl_get_write_guarantee(bio1)); + TEST_ASSERT_EQUAL(3, BIO_read(bio2, buf, 3)); + TEST_ASSERT_EQUAL_UINT8_ARRAY("123", buf, 3); + TEST_ASSERT_EQUAL(3, BIO_ctrl_get_write_guarantee(bio1)); + TEST_ASSERT_EQUAL(7, BIO_read(bio2, buf, sizeof(buf))); + TEST_ASSERT_EQUAL_UINT8_ARRAY("4567890", buf, 7); + TEST_ASSERT_EQUAL(10, BIO_ctrl_get_write_guarantee(bio1)); + + // Test writes and reads starting in the middle of the ring buffer and + // wrapping to front. + TEST_ASSERT_EQUAL(8, BIO_write(bio1, "abcdefgh", 8)); + TEST_ASSERT_EQUAL(2, BIO_ctrl_get_write_guarantee(bio1)); + TEST_ASSERT_EQUAL(3, BIO_read(bio2, buf, 3)); + TEST_ASSERT_EQUAL_UINT8_ARRAY("abc", buf, 3); + TEST_ASSERT_EQUAL(5, BIO_ctrl_get_write_guarantee(bio1)); + TEST_ASSERT_EQUAL(5, BIO_write(bio1, "ijklm___", 8)); + TEST_ASSERT_EQUAL(0, BIO_ctrl_get_write_guarantee(bio1)); + TEST_ASSERT_EQUAL(10, BIO_read(bio2, buf, sizeof(buf))); + TEST_ASSERT_EQUAL_UINT8_ARRAY("defghijklm", buf, 10); + TEST_ASSERT_EQUAL(10, BIO_ctrl_get_write_guarantee(bio1)); + + // Data may flow from both ends in parallel. + TEST_ASSERT_EQUAL(5, BIO_write(bio1, "12345", 5)); + TEST_ASSERT_EQUAL(5, BIO_write(bio2, "67890", 5)); + TEST_ASSERT_EQUAL(5, BIO_read(bio2, buf, sizeof(buf))); + TEST_ASSERT_EQUAL_UINT8_ARRAY("12345", buf, 5); + TEST_ASSERT_EQUAL(5, BIO_read(bio1, buf, sizeof(buf))); + TEST_ASSERT_EQUAL_UINT8_ARRAY("67890", buf, 5); + + // Other tests below not imported since BIO_shutdown_wr() not supported + // - Closing the write end causes an EOF on the read half, after draining. + // - A closed write end may not be written to. + // - The other end is still functional. +} From 085d2b8d25bee96df6f3f5410cd31d80a8f11675 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Fri, 5 Jun 2020 14:29:32 +0200 Subject: [PATCH 2/7] examples: common connect to also support no connection flow if no inteface chosen This is useful for testing if there's no need for external network and connection --- .../protocol_examples_common/Kconfig.projbuild | 1 + .../protocol_examples_common/connect.c | 13 ++++++++++++- .../include/protocol_examples_common.h | 5 +++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/examples/common_components/protocol_examples_common/Kconfig.projbuild b/examples/common_components/protocol_examples_common/Kconfig.projbuild index eccf778890..5875ccdfa0 100644 --- a/examples/common_components/protocol_examples_common/Kconfig.projbuild +++ b/examples/common_components/protocol_examples_common/Kconfig.projbuild @@ -177,6 +177,7 @@ menu "Example Connection Configuration" config EXAMPLE_CONNECT_IPV6 bool "Obtain IPv6 address" default y + depends on EXAMPLE_CONNECT_WIFI || EXAMPLE_CONNECT_ETHERNET help By default, examples will wait until IPv4 and IPv6 local link addresses are obtained. Disable this option if the network does not support IPv6. diff --git a/examples/common_components/protocol_examples_common/connect.c b/examples/common_components/protocol_examples_common/connect.c index ed502210c8..dfba995e81 100644 --- a/examples/common_components/protocol_examples_common/connect.c +++ b/examples/common_components/protocol_examples_common/connect.c @@ -43,9 +43,10 @@ #define NR_OF_IP_ADDRESSES_TO_WAIT_FOR (s_active_interfaces) #endif +#define EXAMPLE_DO_CONNECT CONFIG_EXAMPLE_CONNECT_WIFI || CONFIG_EXAMPLE_CONNECT_ETHERNET + static int s_active_interfaces = 0; static xSemaphoreHandle s_semph_get_ip_addrs; -static esp_ip4_addr_t s_ip_addr; static esp_netif_t *s_example_esp_netif = NULL; #ifdef CONFIG_EXAMPLE_CONNECT_IPV6 @@ -102,7 +103,11 @@ static void start(void) s_example_esp_netif = NULL; #endif +#if EXAMPLE_DO_CONNECT + /* create semaphore if at least one interface is active */ s_semph_get_ip_addrs = xSemaphoreCreateCounting(NR_OF_IP_ADDRESSES_TO_WAIT_FOR, 0); +#endif + } /* tear down connection, release resources */ @@ -119,6 +124,9 @@ static void stop(void) #endif } +#if EXAMPLE_DO_CONNECT +static esp_ip4_addr_t s_ip_addr; + static void on_got_ip(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { @@ -131,6 +139,7 @@ static void on_got_ip(void *arg, esp_event_base_t event_base, memcpy(&s_ip_addr, &event->ip_info.ip, sizeof(s_ip_addr)); xSemaphoreGive(s_semph_get_ip_addrs); } +#endif #ifdef CONFIG_EXAMPLE_CONNECT_IPV6 @@ -155,9 +164,11 @@ static void on_got_ipv6(void *arg, esp_event_base_t event_base, esp_err_t example_connect(void) { +#if EXAMPLE_DO_CONNECT if (s_semph_get_ip_addrs != NULL) { return ESP_ERR_INVALID_STATE; } +#endif start(); ESP_ERROR_CHECK(esp_register_shutdown_handler(&stop)); ESP_LOGI(TAG, "Waiting for IP(s)"); diff --git a/examples/common_components/protocol_examples_common/include/protocol_examples_common.h b/examples/common_components/protocol_examples_common/include/protocol_examples_common.h index 859264df06..8c0a0a30e0 100644 --- a/examples/common_components/protocol_examples_common/include/protocol_examples_common.h +++ b/examples/common_components/protocol_examples_common/include/protocol_examples_common.h @@ -24,6 +24,11 @@ extern "C" { #define EXAMPLE_INTERFACE get_example_netif() #endif +#if !defined (CONFIG_EXAMPLE_CONNECT_ETHERNET) && !defined (CONFIG_EXAMPLE_CONNECT_WIFI) +// This is useful for some tests which do not need a network connection +#define EXAMPLE_INTERFACE NULL +#endif + /** * @brief Configure Wi-Fi or Ethernet, connect, wait for IP * From 9459c0dd432fdd0fccb49ea65bb5c72d1849e1ba Mon Sep 17 00:00:00 2001 From: David Cermak Date: Fri, 5 Jun 2020 16:17:01 +0200 Subject: [PATCH 3/7] asio: Basic SSL/TLS support in asio port for ESP platform This port employs IDF port of OpenSSL for most common features, others are discouraged or not supported. The port also introduces several stubs for OpenSSL functions which ASIO needs to get compiled and linked. Upstream ASIO supports WolfSSL as SSL/TLS stack, as well, which is another option for SSL support in ASIO on ESP platform. --- components/asio/CMakeLists.txt | 4 +- components/asio/asio | 2 +- components/asio/component.mk | 3 +- components/asio/port/include/openssl/conf.h | 20 ++ .../include/openssl/esp_asio_openssl_stubs.h | 185 ++++++++++++++++++ .../asio/port/src/esp_asio_openssl_stubs.c | 60 ++++++ components/openssl/library/ssl_lib.c | 3 + components/openssl/library/ssl_pkey.c | 6 +- components/openssl/platform/ssl_pm.c | 6 +- docs/en/api-reference/protocols/asio.rst | 14 +- 10 files changed, 292 insertions(+), 11 deletions(-) create mode 100644 components/asio/port/include/openssl/conf.h create mode 100644 components/asio/port/include/openssl/esp_asio_openssl_stubs.h create mode 100644 components/asio/port/src/esp_asio_openssl_stubs.c diff --git a/components/asio/CMakeLists.txt b/components/asio/CMakeLists.txt index f2038278de..b55dc56ab3 100644 --- a/components/asio/CMakeLists.txt +++ b/components/asio/CMakeLists.txt @@ -1,3 +1,5 @@ idf_component_register(SRCS "asio/asio/src/asio.cpp" + "asio/asio/src/asio_ssl.cpp" + "port/src/esp_asio_openssl_stubs.c" INCLUDE_DIRS "asio/asio/include" "port/include" - REQUIRES lwip) + REQUIRES lwip openssl) diff --git a/components/asio/asio b/components/asio/asio index 3b66e5b051..61702cd13b 160000 --- a/components/asio/asio +++ b/components/asio/asio @@ -1 +1 @@ -Subproject commit 3b66e5b051381fb70de9c2791df70a06181c64e3 +Subproject commit 61702cd13be0b8c9800a9793daae72768ede26af diff --git a/components/asio/component.mk b/components/asio/component.mk index e024df3f31..0c2919e24e 100644 --- a/components/asio/component.mk +++ b/components/asio/component.mk @@ -1,6 +1,5 @@ COMPONENT_ADD_INCLUDEDIRS := asio/asio/include port/include COMPONENT_PRIV_INCLUDEDIRS := private_include -COMPONENT_SRCDIRS := asio/asio/src -COMPONENT_OBJEXCLUDE := asio/asio/src/asio_ssl.o +COMPONENT_SRCDIRS := asio/asio/src port/src COMPONENT_SUBMODULES += asio diff --git a/components/asio/port/include/openssl/conf.h b/components/asio/port/include/openssl/conf.h new file mode 100644 index 0000000000..9c46bd14a0 --- /dev/null +++ b/components/asio/port/include/openssl/conf.h @@ -0,0 +1,20 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _ESP_ASIO_OPENSSL_CONF_H +#define _ESP_ASIO_OPENSSL_CONF_H + +#include "openssl/esp_asio_openssl_stubs.h" + +#endif // _ESP_ASIO_OPENSSL_CONF_H diff --git a/components/asio/port/include/openssl/esp_asio_openssl_stubs.h b/components/asio/port/include/openssl/esp_asio_openssl_stubs.h new file mode 100644 index 0000000000..611b4e784e --- /dev/null +++ b/components/asio/port/include/openssl/esp_asio_openssl_stubs.h @@ -0,0 +1,185 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _ESP_ASIO_OPENSSL_STUBS_H +#define _ESP_ASIO_OPENSSL_STUBS_H + +#include "internal/ssl_x509.h" +#include "internal/ssl_pkey.h" +#include "mbedtls/pem.h" +#include + +/** + * @note This header contains openssl API which are NOT implemented, and are only provided + * as stubs or no-operations to get the ASIO library compiled and working with most + * practical use cases as an embedded application on ESP platform + */ + +#ifdef __cplusplus +extern "C" { +#endif + +// The most applicable OpenSSL version wrtt ASIO usage +#define OPENSSL_VERSION_NUMBER 0x10100001L +// SSLv2 methods not supported +// OpenSSL port supports: TLS_ANY, TLS_1, TLS_1_1, TLS_1_2, SSL_3 +#define OPENSSL_NO_SSL2 +#define SSL2_VERSION 0x0002 + +#define SSL_R_SHORT_READ 219 +#define SSL_OP_ALL 0 +#define SSL_OP_SINGLE_DH_USE 0 +//#define OPENSSL_VERSION_NUMBER 0x10001000L +#define SSL_OP_NO_COMPRESSION 0 +//#define LIBRESSL_VERSION_NUMBER 1 +//#define PEM_R_NO_START_LINE 110 +// Translates mbedTLS PEM parse error, used by ASIO +#define PEM_R_NO_START_LINE -MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT + +#define SSL_OP_NO_SSLv2 0x01000000L +#define SSL_OP_NO_SSLv3 0x02000000L +#define SSL_OP_NO_TLSv1 0x04000000L + +#define X509_FILETYPE_PEM 1 +#define X509_FILETYPE_ASN1 2 +#define SSL_FILETYPE_ASN1 X509_FILETYPE_ASN1 +#define SSL_FILETYPE_PEM X509_FILETYPE_PEM + +#define NID_subject_alt_name 85 + +#define SSL_MODE_RELEASE_BUFFERS 0x00000000L +#define SSL_MODE_ENABLE_PARTIAL_WRITE 0x00000001L +#define SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER 0x00000002L + +#define GEN_DNS 2 +#define GEN_IPADD 7 +#define V_ASN1_OCTET_STRING 4 +#define V_ASN1_IA5STRING 22 +#define NID_commonName 13 + +#define SSL_CTX_get_app_data(ctx) ((void*)SSL_CTX_get_ex_data(ctx, 0)) + +/** +* @brief Frees DH object -- not implemented +* +* Current implementation calls SSL_ASSERT +* +* @param r DH object +*/ +void DH_free(DH *r); + +/** + * @brief Frees GENERAL_NAMES -- not implemented + * + * Current implementation calls SSL_ASSERT + * + * @param r GENERAL_NAMES object + */ +void GENERAL_NAMES_free(GENERAL_NAMES * gens); + +/** + * @brief Returns subject name from X509 -- not implemented + * + * Current implementation calls SSL_ASSERT + * + * @param r X509 object + */ +X509_NAME *X509_get_subject_name(X509 *a); + +/** + * @brief API provaded as declaration only + * + */ +int X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx); + +/** + * @brief API provaded as declaration only + * + */ +int X509_NAME_get_index_by_NID(X509_NAME *name, int nid, int lastpos); + +/** + * @brief API provaded as declaration only + * + */ +X509_NAME_ENTRY *X509_NAME_get_entry(X509_NAME *name, int loc); + +/** + * @brief API provaded as declaration only + * + */ +ASN1_STRING *X509_NAME_ENTRY_get_data(X509_NAME_ENTRY *ne); + +/** + * @brief API provaded as declaration only + * + */ +void *X509_get_ext_d2i(X509 *x, int nid, int *crit, int *idx); + +/** + * @brief API provaded as declaration only + * + */ +X509 * X509_STORE_CTX_get_current_cert(X509_STORE_CTX *ctx); + +/** + * @brief Reads DH params from a bio object -- not implemented + * + * Current implementation calls SSL_ASSERT + */ +DH *PEM_read_bio_DHparams(BIO *bp, DH **x, pem_password_cb *cb, void *u); + +/** + * @brief API provaded as declaration only + * + */ +void * X509_STORE_CTX_get_ex_data(X509_STORE_CTX *ctx,int idx); + +/** + * @brief Sets DH params to ssl ctx -- not implemented + * + * Current implementation calls SSL_ASSERT + */ +int SSL_CTX_set_tmp_dh(SSL_CTX *ctx, const DH *dh); + +/** + * @brief Sets SSL mode -- not implemented + * + * Current implementation is no-op + */ +uint32_t SSL_set_mode(SSL *ssl, uint32_t mode); + +/** + * @brief API provaded as declaration only + * + */ +void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx, void *data); + +/** + * @brief API provaded as declaration only + * + */ +void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, pem_password_cb *cb); + +/** + * @brief Clears any existing chain associated with the current certificate of ctx. + * + */ +int SSL_CTX_clear_chain_certs(SSL_CTX *ctx); + +#if defined(__cplusplus) +} /* extern C */ +#endif + +#endif /* _ESP_ASIO_OPENSSL_STUBS_H */ diff --git a/components/asio/port/src/esp_asio_openssl_stubs.c b/components/asio/port/src/esp_asio_openssl_stubs.c new file mode 100644 index 0000000000..9fdf53d0d1 --- /dev/null +++ b/components/asio/port/src/esp_asio_openssl_stubs.c @@ -0,0 +1,60 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "esp_asio_config.h" +#include "internal/ssl_dbg.h" +#include "openssl/esp_asio_openssl_stubs.h" + +// Unsupported features as macros to make the assertions more readable +#define ESP_OPENSSL_DH_IS_SUPPORTED 0 +#define ESP_OPENSSL_GENERAL_NAMES_IS_SUPPORTED 0 + +void DH_free (DH *r) +{ + SSL_ASSERT3(ESP_OPENSSL_DH_IS_SUPPORTED); +} + +DH *PEM_read_bio_DHparams(BIO *bp, DH **x, pem_password_cb *cb, void *u) +{ + SSL_ASSERT2(ESP_OPENSSL_DH_IS_SUPPORTED); + return NULL; +} + +int SSL_CTX_set_tmp_dh(SSL_CTX *ctx, const DH *dh) +{ + SSL_ASSERT1(ESP_OPENSSL_DH_IS_SUPPORTED); + return -1; +} + +void GENERAL_NAMES_free(GENERAL_NAMES * gens) +{ + SSL_ASSERT3(ESP_OPENSSL_GENERAL_NAMES_IS_SUPPORTED); +} + +X509_NAME *X509_get_subject_name(X509 *a) +{ + SSL_ASSERT2(ESP_OPENSSL_GENERAL_NAMES_IS_SUPPORTED); + return NULL; +} + +uint32_t SSL_set_mode(SSL *ssl, uint32_t mode) +{ + return 0; +} + +int SSL_CTX_clear_chain_certs(SSL_CTX *ctx) +{ + return 1; +} diff --git a/components/openssl/library/ssl_lib.c b/components/openssl/library/ssl_lib.c index adfacb07ed..46b0cc0fd7 100644 --- a/components/openssl/library/ssl_lib.c +++ b/components/openssl/library/ssl_lib.c @@ -1590,12 +1590,14 @@ void SSL_set_verify_depth(SSL *ssl, int depth) ssl->param.depth = depth; } +#define ESP_OPENSSL_VERIFYCB_IS_SUPPORTED 0 /** * @brief set the SSL context verifying of the SSL context */ void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *)) { SSL_ASSERT3(ctx); + SSL_ASSERT3(ESP_OPENSSL_VERIFYCB_IS_SUPPORTED); ctx->verify_mode = mode; ctx->default_verify_callback = verify_callback; @@ -1607,6 +1609,7 @@ void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509 void SSL_set_verify(SSL *ssl, int mode, int (*verify_callback)(int, X509_STORE_CTX *)) { SSL_ASSERT3(ssl); + SSL_ASSERT3(ESP_OPENSSL_VERIFYCB_IS_SUPPORTED); ssl->verify_mode = mode; ssl->verify_callback = verify_callback; diff --git a/components/openssl/library/ssl_pkey.c b/components/openssl/library/ssl_pkey.c index 0ede47864d..3a65293786 100644 --- a/components/openssl/library/ssl_pkey.c +++ b/components/openssl/library/ssl_pkey.c @@ -276,14 +276,14 @@ failed1: return 0; } -#define ESP_OPENSSL_FILES_NOT_SUPPORTED 1 +#define ESP_OPENSSL_FILES_IS_SUPPORTED 0 /** * @brief load the private key file into SSL context */ int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) { // Using file name as private key is discouraged - SSL_ASSERT1(ESP_OPENSSL_FILES_NOT_SUPPORTED); + SSL_ASSERT1(ESP_OPENSSL_FILES_IS_SUPPORTED); return -1; } @@ -293,7 +293,7 @@ int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) int SSL_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) { // Using file name as private key is discouraged - SSL_ASSERT1(ESP_OPENSSL_FILES_NOT_SUPPORTED); + SSL_ASSERT1(ESP_OPENSSL_FILES_IS_SUPPORTED); return -1; } diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index f971b1747c..6f55da47e3 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -89,7 +89,7 @@ static void ssl_platform_debug(void *ctx, int level, } #endif -int mbedtls_ssl_send(void *ctx, const unsigned char *buf, size_t len ) +static int mbedtls_bio_send(void *ctx, const unsigned char *buf, size_t len ) { BIO *bio = ctx; int written = BIO_write(bio, buf, len); @@ -99,7 +99,7 @@ int mbedtls_ssl_send(void *ctx, const unsigned char *buf, size_t len ) return written; } -static int mbedtls_ssl_recv(void *ctx, unsigned char *buf, size_t len ) +static int mbedtls_bio_recv(void *ctx, unsigned char *buf, size_t len ) { BIO *bio = ctx; int read = BIO_read(bio, buf, len); @@ -316,7 +316,7 @@ int ssl_pm_handshake(SSL *ssl) struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; if (ssl->bio) { - mbedtls_ssl_set_bio(&ssl_pm->ssl, ssl->bio, mbedtls_ssl_send, mbedtls_ssl_recv, NULL); + mbedtls_ssl_set_bio(&ssl_pm->ssl, ssl->bio, mbedtls_bio_send, mbedtls_bio_recv, NULL); } ret = ssl_pm_reload_crt(ssl); diff --git a/docs/en/api-reference/protocols/asio.rst b/docs/en/api-reference/protocols/asio.rst index 14f39ae485..cd6a40ec30 100644 --- a/docs/en/api-reference/protocols/asio.rst +++ b/docs/en/api-reference/protocols/asio.rst @@ -13,7 +13,18 @@ Asio also comes with a number of examples which could be find under Documentatio Supported features ^^^^^^^^^^^^^^^^^^ -ESP platform port currently supports only network asynchronous socket operations; does not support serial port and ssl. +ESP platform port currently supports only network asynchronous socket operations; does not support serial port. +SSL/TLS support if disabled by default and could be enabled in component configuration menu and choosing TLS library from + +- mbedTLS with OpenSSL translation layer (default option) +- wolfSSL + +SSL support is very basic at this stage, not including + +- Verification callbacks +- DH property files +- Certificates/private keys file APIs + Internal asio settings for ESP include - EXCEPTIONS are enabled in ASIO if enabled in menuconfig @@ -27,5 +38,6 @@ ESP examples are based on standard asio :example:`protocols/asio`: - :example:`protocols/asio/tcp_echo_server` - :example:`protocols/asio/chat_client` - :example:`protocols/asio/chat_server` +- :example:`protocols/asio/ssl_client_server` Please refer to the specific example README.md for details From 213bbe51fcde53099b1a6e72381ad8c48afdfcc9 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Fri, 5 Jun 2020 16:19:10 +0200 Subject: [PATCH 4/7] examples: asio ssl example demonstrating both server and client By default it uses simple client connecting to https address. It is possible to configure both server and client. As for example test the configuration of both server and client connecting to each other on --- .../asio/ssl_client_server/CMakeLists.txt | 10 + .../protocols/asio/ssl_client_server/Makefile | 9 + .../asio/ssl_client_server/README.md | 85 ++++++ .../asio/ssl_client_server/example_test.py | 15 + .../ssl_client_server/main/CMakeLists.txt | 3 + .../ssl_client_server/main/Kconfig.projbuild | 28 ++ .../ssl_client_server/main/asio_ssl_main.cpp | 283 ++++++++++++++++++ .../asio/ssl_client_server/main/cacert.pem | 21 ++ .../asio/ssl_client_server/main/component.mk | 11 + .../asio/ssl_client_server/main/prvtkey.pem | 27 ++ .../asio/ssl_client_server/partitions.csv | 5 + .../asio/ssl_client_server/sdkconfig.ci | 6 + .../asio/ssl_client_server/sdkconfig.defaults | 3 + 13 files changed, 506 insertions(+) create mode 100644 examples/protocols/asio/ssl_client_server/CMakeLists.txt create mode 100644 examples/protocols/asio/ssl_client_server/Makefile create mode 100644 examples/protocols/asio/ssl_client_server/README.md create mode 100644 examples/protocols/asio/ssl_client_server/example_test.py create mode 100644 examples/protocols/asio/ssl_client_server/main/CMakeLists.txt create mode 100644 examples/protocols/asio/ssl_client_server/main/Kconfig.projbuild create mode 100644 examples/protocols/asio/ssl_client_server/main/asio_ssl_main.cpp create mode 100644 examples/protocols/asio/ssl_client_server/main/cacert.pem create mode 100644 examples/protocols/asio/ssl_client_server/main/component.mk create mode 100644 examples/protocols/asio/ssl_client_server/main/prvtkey.pem create mode 100644 examples/protocols/asio/ssl_client_server/partitions.csv create mode 100644 examples/protocols/asio/ssl_client_server/sdkconfig.ci create mode 100644 examples/protocols/asio/ssl_client_server/sdkconfig.defaults diff --git a/examples/protocols/asio/ssl_client_server/CMakeLists.txt b/examples/protocols/asio/ssl_client_server/CMakeLists.txt new file mode 100644 index 0000000000..fcdac14880 --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/CMakeLists.txt @@ -0,0 +1,10 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +# (Not part of the boilerplate) +# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection. +set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(asio_ssl_client_server) diff --git a/examples/protocols/asio/ssl_client_server/Makefile b/examples/protocols/asio/ssl_client_server/Makefile new file mode 100644 index 0000000000..af59f284f4 --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# +PROJECT_NAME := asio_ssl_client_server + +EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/common_components/protocol_examples_common + +include $(IDF_PATH)/make/project.mk diff --git a/examples/protocols/asio/ssl_client_server/README.md b/examples/protocols/asio/ssl_client_server/README.md new file mode 100644 index 0000000000..e99ccfe11d --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/README.md @@ -0,0 +1,85 @@ +# Asio SSL client/server example + +Simple Asio client and server with SSL/TLS transport + +## How to Use Example + +### Hardware Required + +This example can be executed on any ESP platform board. No external connection is required, it is recommended though +to connect to internet or a local network via WiFi or Ethernet to easily exercise features of this example. + +### Configure the project + +* Open the project configuration menu (`idf.py menuconfig`) +* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details. +* Enable the ASIO client and set server's host name to examine client's functionality. +The ASIO client connects to the configured server and sends default payload string "GET / HTTP/1.1" +* Enable the ASIO server to examine server's functionality. The ASIO server listens to connection and echos back what was received. + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +### Client connecting to public server + +The below output illustrates the client connecting to a public https server. + +``` +I (1267) example_connect: Waiting for IP(s) +I (2587) wifi:new:<11,0>, old:<1,0>, ap:<255,255>, sta:<11,0>, prof:1 +I (3367) wifi:state: init -> auth (b0) +I (3377) wifi:state: auth -> assoc (0) +I (3387) wifi:state: assoc -> run (10) +I (3397) wifi:security type: 3, phy: bgn, rssi: -49 +I (3397) wifi:pm start, type: 1 +I (3457) wifi:AP's beacon interval = 102400 us, DTIM period = 1 +I (4747) example_connect: Got IPv6 event: Interface "example_connect: sta" address: fe80:0000:0000:0000:260a:xxxx:xxxx:xxxx, type: ESP_IP6_ADDR_IS_LINK_LOCAL +I (5247) esp_netif_handlers: example_connect: sta ip: 192.168.32.69, mask: 255.255.252.0, gw: 192.168.32.3 +I (5247) example_connect: Got IPv4 event: Interface "example_connect: sta" address: 192.168.32.69 +I (5257) example_connect: Connected to example_connect: sta +I (5257) example_connect: - IPv4 address: 192.168.32.69 +I (5267) example_connect: - IPv6 address: fe80:0000:0000:0000:260a:xxxx:xxxx:xxxx, type: ESP_IP6_ADDR_IS_LINK_LOCAL +W (5277) esp32_asio_pthread: pthread_condattr_setclock: not yet supported! +W (5297) esp32_asio_pthread: pthread_condattr_setclock: not yet supported! +Reply: HTTP/1.1 200 OK +D +``` +### Both server and client enabled + +The below output demonstrates the client connecting to the ASIO server via loopback interface, so no WiFi, nor Ethernet connection +was established. +``` +I (0) cpu_start: App cpu up. +I (495) heap_init: Initializing. RAM available for dynamic allocation: +I (502) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM +I (508) heap_init: At 3FFB5400 len 0002AC00 (171 KiB): DRAM +I (515) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM +I (521) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM +I (527) heap_init: At 4008BB80 len 00014480 (81 KiB): IRAM +I (534) cpu_start: Pro cpu start user code +I (556) spi_flash: detected chip: gd +I (556) spi_flash: flash io: dio +W (556) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header. +I (566) cpu_start: Starting scheduler on PRO CPU. +I (0) cpu_start: Starting scheduler on APP CPU. +I (600) example_connect: Waiting for IP(s) +W (600) esp32_asio_pthread: pthread_condattr_setclock: not yet supported! +W (1610) esp32_asio_pthread: pthread_condattr_setclock: not yet supported! +W (1610) esp32_asio_pthread: pthread_condattr_setclock: not yet supported! +Server received: GET / HTTP/1.1 + + +Reply: GET / HTTP/1.1 +``` +See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/protocols/asio/ssl_client_server/example_test.py b/examples/protocols/asio/ssl_client_server/example_test.py new file mode 100644 index 0000000000..876eb01b65 --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/example_test.py @@ -0,0 +1,15 @@ +from __future__ import unicode_literals +import ttfw_idf + + +@ttfw_idf.idf_example_test(env_tag='Example_GENERIC') +def test_examples_asio_ssl(env, extra_data): + + dut = env.get_dut('asio_ssl_client_server', 'examples/protocols/asio/ssl_client_server') + dut.start_app() + + dut.expect('Reply: GET / HTTP/1.1') + + +if __name__ == '__main__': + test_examples_asio_ssl() diff --git a/examples/protocols/asio/ssl_client_server/main/CMakeLists.txt b/examples/protocols/asio/ssl_client_server/main/CMakeLists.txt new file mode 100644 index 0000000000..5fd4fe15e8 --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "asio_ssl_main.cpp" + INCLUDE_DIRS "." + EMBED_TXTFILES cacert.pem prvtkey.pem) diff --git a/examples/protocols/asio/ssl_client_server/main/Kconfig.projbuild b/examples/protocols/asio/ssl_client_server/main/Kconfig.projbuild new file mode 100644 index 0000000000..a5996f7087 --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/main/Kconfig.projbuild @@ -0,0 +1,28 @@ +menu "Example Configuration" + + config EXAMPLE_CLIENT + bool "Enable TLS client" + default y + help + Choose this option to use ASIO TLS/SSL client functionality + + config EXAMPLE_PORT + string "ASIO port number" + default "443" + help + Port number used by ASIO example. + + config EXAMPLE_SERVER + bool "Enable TLS server" + default n + help + Choose this option to use ASIO TLS/SSL server functionality + + config EXAMPLE_SERVER_NAME + string "ASIO server name or IP" + default "www.google.com" + depends on EXAMPLE_CLIENT + help + Asio example server ip for the ASIO client to connect to. + +endmenu diff --git a/examples/protocols/asio/ssl_client_server/main/asio_ssl_main.cpp b/examples/protocols/asio/ssl_client_server/main/asio_ssl_main.cpp new file mode 100644 index 0000000000..8209b680d6 --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/main/asio_ssl_main.cpp @@ -0,0 +1,283 @@ +#include +#include "protocol_examples_common.h" +#include "esp_event.h" +#include "nvs_flash.h" +#include +#include +#include +#include +#include "asio.hpp" +#include "asio/ssl.hpp" +#include "asio/buffer.hpp" +#include "esp_pthread.h" + +extern const unsigned char cacert_pem_start[] asm("_binary_cacert_pem_start"); +extern const unsigned char cacert_pem_end[] asm("_binary_cacert_pem_end"); + +extern const unsigned char prvtkey_pem_start[] asm("_binary_prvtkey_pem_start"); +extern const unsigned char prvtkey_pem_end[] asm("_binary_prvtkey_pem_end"); + +const asio::const_buffer cert_chain(cacert_pem_start, cacert_pem_end - cacert_pem_start); +const asio::const_buffer privkey(prvtkey_pem_start, prvtkey_pem_end - prvtkey_pem_start); + +using asio::ip::tcp; + +using asio::ip::tcp; + +enum { max_length = 1024 }; + +class client +{ +public: + client(asio::io_context& io_context, + asio::ssl::context& context, + const tcp::resolver::results_type& endpoints) + : socket_(io_context, context) + { + socket_.set_verify_mode(asio::ssl::verify_peer); + + connect(endpoints); + } + +private: + void connect(const tcp::resolver::results_type& endpoints) + { + asio::async_connect(socket_.lowest_layer(), endpoints, + [this](const std::error_code& error, + const tcp::endpoint& /*endpoint*/) + { + if (!error) + { + handshake(); + } + else + { + std::cout << "Connect failed: " << error.message() << "\n"; + } + }); + } + + void handshake() + { + socket_.async_handshake(asio::ssl::stream_base::client, + [this](const std::error_code& error) + { + if (!error) + { + send_request(); + } + else + { + std::cout << "Handshake failed: " << error.message() << "\n"; + } + }); + } + + void send_request() + { + size_t request_length = std::strlen(request_); + + asio::async_write(socket_, + asio::buffer(request_, request_length), + [this](const std::error_code& error, std::size_t length) + { + if (!error) + { + receive_response(length); + } + else + { + std::cout << "Write failed: " << error.message() << "\n"; + } + }); + } + + void receive_response(std::size_t length) + { + asio::async_read(socket_, + asio::buffer(reply_, length), + [this](const std::error_code& error, std::size_t length) + { + if (!error) + { + std::cout << "Reply: "; + std::cout.write(reply_, length); + std::cout << "\n"; + } + else + { + std::cout << "Read failed: " << error.message() << "\n"; + } + }); + + } + + asio::ssl::stream socket_; + char request_[max_length] = "GET / HTTP/1.1\r\n\r\n"; + char reply_[max_length]; +}; + +class session : public std::enable_shared_from_this +{ +public: + session(tcp::socket socket, asio::ssl::context& context) + : socket_(std::move(socket), context) + { + } + + void start() + { + do_handshake(); + } + +private: + void do_handshake() + { + auto self(shared_from_this()); + socket_.async_handshake(asio::ssl::stream_base::server, + [this, self](const std::error_code& error) + { + if (!error) + { + do_read(); + } + }); + } + + void do_read() + { + auto self(shared_from_this()); + socket_.async_read_some(asio::buffer(data_), + [this, self](const std::error_code& ec, std::size_t length) + { + if (!ec) + { + data_[length] = 0; + std::cout << "Server received: " << data_ << std::endl; + do_write(length); + } + }); + } + + void do_write(std::size_t length) + { + auto self(shared_from_this()); + asio::async_write(socket_, asio::buffer(data_, length), + [this, self](const std::error_code& ec, + std::size_t /*length*/) + { + if (!ec) + { + do_read(); + } + }); + } + + asio::ssl::stream socket_; + char data_[1024]; +}; + +class server +{ +public: + server(asio::io_context& io_context, unsigned short port) + : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)), + context_(asio::ssl::context::tls_server) + { + context_.set_options( + asio::ssl::context::default_workarounds + | asio::ssl::context::no_sslv2); + context_.use_certificate_chain(cert_chain); + context_.use_private_key(privkey, asio::ssl::context::pem); + + do_accept(); + } + +private: + void do_accept() + { + acceptor_.async_accept( + [this](const std::error_code& error, tcp::socket socket) + { + if (!error) + { + std::make_shared(std::move(socket), context_)->start(); + } + + do_accept(); + }); + } + + tcp::acceptor acceptor_; + asio::ssl::context context_; +}; + +void set_thread_config(const char *name, int stack, int prio) +{ + auto cfg = esp_pthread_get_default_config(); + cfg.thread_name = name; + cfg.stack_size = stack; + cfg.prio = prio; + esp_pthread_set_cfg(&cfg); +} + +void ssl_server_thread() +{ + asio::io_context io_context; + + server s(io_context, 443); + + io_context.run(); +} + +void ssl_client_thread() +{ + asio::io_context io_context; + + tcp::resolver resolver(io_context); + std::string server_ip = CONFIG_EXAMPLE_SERVER_NAME; + std::string server_port = CONFIG_EXAMPLE_PORT; + auto endpoints = resolver.resolve(server_ip, server_port); + + asio::ssl::context ctx(asio::ssl::context::tls_client); + ctx.use_certificate_chain(cert_chain); + + client c(io_context, ctx, endpoints); + + io_context.run(); + +} + + +extern "C" void app_main(void) +{ + ESP_ERROR_CHECK(nvs_flash_init()); + esp_netif_init(); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. + * Read "Establishing Wi-Fi or Ethernet Connection" section in + * examples/protocols/README.md for more information about this function. + */ + ESP_ERROR_CHECK(example_connect()); + + /* This helper function configures blocking UART I/O */ + ESP_ERROR_CHECK(example_configure_stdin_stdout()); + std::vector work_threads; + +#if CONFIG_EXAMPLE_SERVER + set_thread_config("Server", 16 * 1024, 5); + work_threads.emplace_back(std::thread(ssl_server_thread)); + std::this_thread::sleep_for(std::chrono::seconds(1)); +#endif // CONFIG_EXAMPLE_SERVER + +#if CONFIG_EXAMPLE_CLIENT + set_thread_config("Client", 16 * 1024, 5); + work_threads.emplace_back(ssl_client_thread); +#endif // CONFIG_EXAMPLE_CLIENT + + for (auto & t : work_threads) { + t.join(); + } + +} diff --git a/examples/protocols/asio/ssl_client_server/main/cacert.pem b/examples/protocols/asio/ssl_client_server/main/cacert.pem new file mode 100644 index 0000000000..e09c3989cd --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/main/cacert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIJAPMMNobNczaUMA0GCSqGSIb3DQEBBAUAMHQxEzARBgNV +BAMTCk15IFRlc3QgQ0ExCzAJBgNVBAgTAkhaMQswCQYDVQQGEwJDTjEcMBoGCSqG +SIb3DQEJARYNdGVzdEBjZXJ0LmNvbTElMCMGA1UEChMcUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTAeFw0xNjExMTUwNTA0MThaFw0xOTExMTUwNTA0MThaMHQx +EzARBgNVBAMTCk15IFRlc3QgQ0ExCzAJBgNVBAgTAkhaMQswCQYDVQQGEwJDTjEc +MBoGCSqGSIb3DQEJARYNdGVzdEBjZXJ0LmNvbTElMCMGA1UEChMcUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALDjSPDlomepHCzbw4MUrquQAU0xTV4/Npb27k9I5TRVTjIoOs/5hNI2LPFW +e4CREx09ZrT8K3NFOBoSy7bhPAsjGaFxCYYWc9tiX1m5gq3ToVRSmbZ65fE3kvnI +8E/d5VyzA0OMmWbfaolBSTMoWgqRynEaT+z1Eh2yDTzVFy9eov1DdQFUqGDqbH5b +QYvTY5Fyem7UcKWAe2yS0j3H4dVtVBKNY7qV3Px08yGAs5fQFgUwhyB5+qwhvkeL +JdgapGaSTwLgoQKWHbe/lA3NiBIB9hznFUGKo3hmniAvYZbrQcn3tc0l/J4I39v2 +Pm29FAyjWvQyBkGktz2q4elOZYkCAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkq +hkiG9w0BAQQFAAOCAQEAJCJ+97oae/FcOLbPpjCpUQnWqYydgSChgalkZNvr4fVp +TnuNg471l0Y2oTJLoWn2YcbPSFVOEeKkU47mpjMzucHHp0zGaW9SdzhZalWwmbgK +q2ijecIbuFHFNedYTk/03K7eaAcjVhD8e0oOJImeLOL6DAFivA1LUnSgXsdGPDtD +zhISsCPTu+cL1j0yP6HBvLeAyb8kaCWJ05RtiVLRANNHQn/keHajJYpMwnEEbJdG +cqN3whfJoGVbZ6isEf2RQJ0pYRnP7uGLW3wGkLWxfdto8uER8HVDx7fZpevLIqGd +1OoSEi3cIJXWBAjx0TLzzhtb6aeIxBJWQqHThtkKdg== +-----END CERTIFICATE----- diff --git a/examples/protocols/asio/ssl_client_server/main/component.mk b/examples/protocols/asio/ssl_client_server/main/component.mk new file mode 100644 index 0000000000..656f72d7b7 --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/main/component.mk @@ -0,0 +1,11 @@ +# +# Main component makefile. +# +# This Makefile can be left empty. By default, it will take the sources in the +# src/ directory, compile them and link them into lib(subdirectory_name).a +# in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# + +COMPONENT_EMBED_TXTFILES := cacert.pem +COMPONENT_EMBED_TXTFILES += prvtkey.pem diff --git a/examples/protocols/asio/ssl_client_server/main/prvtkey.pem b/examples/protocols/asio/ssl_client_server/main/prvtkey.pem new file mode 100644 index 0000000000..4ead61f6ff --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/main/prvtkey.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAsONI8OWiZ6kcLNvDgxSuq5ABTTFNXj82lvbuT0jlNFVOMig6 +z/mE0jYs8VZ7gJETHT1mtPwrc0U4GhLLtuE8CyMZoXEJhhZz22JfWbmCrdOhVFKZ +tnrl8TeS+cjwT93lXLMDQ4yZZt9qiUFJMyhaCpHKcRpP7PUSHbINPNUXL16i/UN1 +AVSoYOpsfltBi9NjkXJ6btRwpYB7bJLSPcfh1W1UEo1jupXc/HTzIYCzl9AWBTCH +IHn6rCG+R4sl2BqkZpJPAuChApYdt7+UDc2IEgH2HOcVQYqjeGaeIC9hlutByfe1 +zSX8ngjf2/Y+bb0UDKNa9DIGQaS3Parh6U5liQIDAQABAoIBAB9K9jp3xXVlO3DM +KBhmbkg3n6NSV4eW00d9w8cO9E1/0eeZql3knJS7tNO1IwApqiIAHM1j1yP7WONz +88oUqpSlzwD6iF7KVhC3pHqxEOdDi0Tpn/viXg+Ab2X1IF5guRTfLnKiyviiCazi +edqtBtDb3d6Icx9Oc7gBKcpbQFDGt++wSOb5L+xhRm9B5B4l/6byikiPeKqIK5tC +SoP9Zr1mvpNoGm1P4LvEunFJcRBqVI010VNwfO9P98oVyzJu9/FZZrQxXoY9JdXF +OM6nbl+hMDM3TkEOda9NvBhImozEAvuc97CaaXyR3XivxMqNqNIb4+syUPa2PCS3 +ZztI5qECgYEA1gbVG6ifpvpbBkDPi3Im8fM3F7FLLrQc48FdFjdMvDhHD9lVKucD +Uaa8PF9dbbvlu2cwMyfBOKSuWaXxRxRsiqiPmTunS1MvPzQcSrGwUrL2AogGucn6 ++NrLQf5P4H5IpkDQ9ih3zwjO6xKFK1WeYnYpHM8qUBtl6q0YFyVBPu0CgYEA05Pn +StWA4D7VSbNnVi6lvFyEOUsTrK3v419598TFiq4eXLq6aV8/CQYzKsSzoG+aOZhX +Li+0uyT5cNzUcXYhTsW1hA/pNhMfxMrYiB1x14zlLp2WRGg4vd/+SxX6d9Yd3acX +7QzPKgdDicXs9QN8ozJOICKvNbUI53AJdATVEY0CgYEAwvpGeoQLrdq1weSZLrg3 +soOX1QW3MDz1dKdbXjnStkWut0mOxR7fbysuoPFf8/ARQcCnsHKvHCMqkpESVWbN +2yPkbfxiU8Tcbf/TJljqAOz4ISY6ula/RKZONTixHBrvpEW4GAiV3Q5xMsYUe33s +ZFaw7YXtTj0ng7tdDvjpj6ECgYEApHdUU9ejVq2BHslWiqe4LbO9FMxHfvO2hgix +xugupp6y+2Irhb2EQn+PRq+g8hXOzPaezkhHNTKItDL08T3iplkJwJ6dqmszRsZn +i2dYFzZu8M2PAZ4CfZahFbz/9id7D9HTx3EtmH4NAgvZJpyPRkzUbiaIDDettDpj +Hsyi1AECgYAPLvjBzQj4kPF8Zo9pQEUcz4pmupRVfv3aRfjnahDK4qZHEePDRj+J +W7pzayrs1dyN9QLB8pTc424z7f8MB3llCICN+ohs8CR/eW0NEobE9ldDOeoCr1Vh +NhNSbrN1iZ8U4oLkRTMaDKkVngGffvjGi/q0tOU7hJdZOqNlk2Iahg== +-----END RSA PRIVATE KEY----- diff --git a/examples/protocols/asio/ssl_client_server/partitions.csv b/examples/protocols/asio/ssl_client_server/partitions.csv new file mode 100644 index 0000000000..d01414b8a9 --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/partitions.csv @@ -0,0 +1,5 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 1200000, diff --git a/examples/protocols/asio/ssl_client_server/sdkconfig.ci b/examples/protocols/asio/ssl_client_server/sdkconfig.ci new file mode 100644 index 0000000000..958aa45d42 --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/sdkconfig.ci @@ -0,0 +1,6 @@ +CONFIG_EXAMPLE_CLIENT=y +CONFIG_EXAMPLE_SERVER=y +CONFIG_EXAMPLE_SERVER_NAME="localhost" +CONFIG_EXAMPLE_CONNECT_WIFI=n +CONFIG_EXAMPLE_CONNECT_ETHERNET=n + diff --git a/examples/protocols/asio/ssl_client_server/sdkconfig.defaults b/examples/protocols/asio/ssl_client_server/sdkconfig.defaults new file mode 100644 index 0000000000..8ff365b198 --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/sdkconfig.defaults @@ -0,0 +1,3 @@ +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" From 1c8171c3e8d5a67e47dc8d6abac27ad2989470c3 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Fri, 5 Jun 2020 17:17:55 +0200 Subject: [PATCH 5/7] asio: option to use wolfSSL as TLS stack for ASIO Plus other minor update, make openssl aware of current modes (SSL_set_mode) Update coding style in examples and tests, including copyright notices --- components/asio/CMakeLists.txt | 36 +++++++++-- components/asio/Kconfig | 25 ++++++++ components/asio/asio | 2 +- components/asio/component.mk | 4 ++ .../asio/port/include/esp_asio_config.h | 7 +++ components/asio/port/include/openssl/conf.h | 8 ++- components/asio/port/include/openssl/dh.h | 23 +++++++ .../include/openssl/esp_asio_openssl_stubs.h | 60 +++++++++++++------ components/asio/port/include/openssl/rsa.h | 23 +++++++ components/asio/port/include/openssl/x509v3.h | 23 +++++++ .../asio/port/src/esp_asio_openssl_stubs.c | 5 -- .../openssl/include/internal/ssl_code.h | 4 ++ .../openssl/include/internal/ssl_types.h | 3 +- .../include/openssl/{openssl_err.h => err.h} | 4 +- components/openssl/include/openssl/ssl.h | 11 +++- components/openssl/library/ssl_bio.c | 2 +- components/openssl/library/ssl_lib.c | 17 +++++- components/openssl/platform/ssl_pm.c | 7 ++- components/openssl/test/test_openssl.c | 14 +++++ docs/en/api-reference/protocols/asio.rst | 4 +- .../ssl_client_server/main/CMakeLists.txt | 2 +- .../ssl_client_server/main/Kconfig.projbuild | 8 +++ .../ssl_client_server/main/asio_ssl_main.cpp | 55 ++++++++++------- .../asio/ssl_client_server/main/ca.crt | 22 +++++++ .../asio/ssl_client_server/main/cacert.pem | 21 ------- .../asio/ssl_client_server/main/component.mk | 5 +- .../asio/ssl_client_server/main/prvtkey.pem | 27 --------- .../asio/ssl_client_server/main/server.key | 27 +++++++++ .../asio/ssl_client_server/main/srv.crt | 18 ++++++ .../asio/ssl_client_server/partitions.csv | 2 +- .../asio/ssl_client_server/sdkconfig.ci | 2 +- .../asio/ssl_client_server/sdkconfig.defaults | 1 + 32 files changed, 359 insertions(+), 113 deletions(-) create mode 100644 components/asio/Kconfig create mode 100644 components/asio/port/include/openssl/dh.h create mode 100644 components/asio/port/include/openssl/rsa.h create mode 100644 components/asio/port/include/openssl/x509v3.h rename components/openssl/include/openssl/{openssl_err.h => err.h} (98%) create mode 100644 examples/protocols/asio/ssl_client_server/main/ca.crt delete mode 100644 examples/protocols/asio/ssl_client_server/main/cacert.pem delete mode 100644 examples/protocols/asio/ssl_client_server/main/prvtkey.pem create mode 100644 examples/protocols/asio/ssl_client_server/main/server.key create mode 100644 examples/protocols/asio/ssl_client_server/main/srv.crt diff --git a/components/asio/CMakeLists.txt b/components/asio/CMakeLists.txt index b55dc56ab3..e45f2c95f5 100644 --- a/components/asio/CMakeLists.txt +++ b/components/asio/CMakeLists.txt @@ -1,5 +1,33 @@ -idf_component_register(SRCS "asio/asio/src/asio.cpp" - "asio/asio/src/asio_ssl.cpp" - "port/src/esp_asio_openssl_stubs.c" +set(asio_sources "asio/asio/src/asio.cpp") + +if (CONFIG_ASIO_SSL_SUPPORT) + if(CONFIG_ASIO_USE_ESP_OPENSSL) + list(APPEND asio_sources + "asio/asio/src/asio_ssl.cpp" + "port/src/esp_asio_openssl_stubs.c") + endif() + + if(CONFIG_ASIO_USE_ESP_WOLFSSL) + list(APPEND asio_sources + "asio/asio/src/asio_ssl.cpp") + endif() +endif() + +idf_component_register(SRCS ${asio_sources} INCLUDE_DIRS "asio/asio/include" "port/include" - REQUIRES lwip openssl) + REQUIRES lwip) + +if (CONFIG_ASIO_SSL_SUPPORT) + if(CONFIG_ASIO_USE_ESP_WOLFSSL) + idf_component_get_property(wolflib esp-wolfssl COMPONENT_LIB) + idf_component_get_property(wolfdir esp-wolfssl COMPONENT_DIR) + + target_link_libraries(${COMPONENT_LIB} PUBLIC ${wolflib}) + target_include_directories(${COMPONENT_LIB} PUBLIC ${wolfdir}/wolfssl/wolfssl) + endif() + + if(CONFIG_ASIO_USE_ESP_OPENSSL) + idf_component_get_property(esp_openssl openssl COMPONENT_LIB) + target_link_libraries(${COMPONENT_LIB} PUBLIC ${esp_openssl}) + endif() +endif() diff --git a/components/asio/Kconfig b/components/asio/Kconfig new file mode 100644 index 0000000000..582410e7ce --- /dev/null +++ b/components/asio/Kconfig @@ -0,0 +1,25 @@ +menu "ESP-ASIO" + config ASIO_SSL_SUPPORT + bool "Enable SSL/TLS support of ASIO" + default n + help + Enable support for basic SSL/TLS features, available for mbedTLS/OpenSSL + as well as wolfSSL TLS library. + + choice ASIO_SSL_LIBRARY_CHOICE + prompt "Choose SSL/TLS library for ESP-TLS (See help for more Info)" + default ASIO_USE_ESP_OPENSSL + depends on ASIO_SSL_SUPPORT + help + The ASIO support multiple backend TLS libraries. Currently the mbedTLS with a thin ESP-OpenSSL + port layer (default choice) and WolfSSL are supported. + Different TLS libraries may support different features and have different resource + usage. Consult the ESP-TLS documentation in ESP-IDF Programming guide for more details. + config ASIO_USE_ESP_OPENSSL + bool "esp-openssl" + config ASIO_USE_ESP_WOLFSSL + depends on TLS_STACK_WOLFSSL + bool "wolfSSL (License info in wolfSSL directory README)" + endchoice + +endmenu diff --git a/components/asio/asio b/components/asio/asio index 61702cd13b..f31694c9f1 160000 --- a/components/asio/asio +++ b/components/asio/asio @@ -1 +1 @@ -Subproject commit 61702cd13be0b8c9800a9793daae72768ede26af +Subproject commit f31694c9f1746ba189a4bcae2e34db15135ddb22 diff --git a/components/asio/component.mk b/components/asio/component.mk index 0c2919e24e..30b2907bbb 100644 --- a/components/asio/component.mk +++ b/components/asio/component.mk @@ -2,4 +2,8 @@ COMPONENT_ADD_INCLUDEDIRS := asio/asio/include port/include COMPONENT_PRIV_INCLUDEDIRS := private_include COMPONENT_SRCDIRS := asio/asio/src port/src +ifeq ($(CONFIG_ASIO_SSL_SUPPORT), ) +COMPONENT_OBJEXCLUDE := asio/asio/src/asio_ssl.o port/src/esp_asio_openssl_stubs.o +endif + COMPONENT_SUBMODULES += asio diff --git a/components/asio/port/include/esp_asio_config.h b/components/asio/port/include/esp_asio_config.h index 750f4cbe3f..bcf8c38d40 100644 --- a/components/asio/port/include/esp_asio_config.h +++ b/components/asio/port/include/esp_asio_config.h @@ -40,4 +40,11 @@ # define ASIO_STANDALONE # define ASIO_HAS_PTHREADS +# ifdef CONFIG_ASIO_USE_ESP_OPENSSL +# define ASIO_USE_ESP_OPENSSL +# define OPENSSL_NO_ENGINE +# elif CONFIG_ASIO_USE_ESP_WOLFSSL +# define ASIO_USE_WOLFSSL +# endif // CONFIG_ASIO_USE_ESP_OPENSSL + #endif // _ESP_ASIO_CONFIG_H_ diff --git a/components/asio/port/include/openssl/conf.h b/components/asio/port/include/openssl/conf.h index 9c46bd14a0..f125c3e6cf 100644 --- a/components/asio/port/include/openssl/conf.h +++ b/components/asio/port/include/openssl/conf.h @@ -14,7 +14,13 @@ #ifndef _ESP_ASIO_OPENSSL_CONF_H #define _ESP_ASIO_OPENSSL_CONF_H - +#include "esp_asio_config.h" #include "openssl/esp_asio_openssl_stubs.h" +#if defined(ASIO_USE_WOLFSSL) +// SSLv3 Methods not present in current wolfSSL library +#define OPENSSL_NO_SSL3 +#include_next "openssl/conf.h" +#endif // ASIO_USE_WOLFSSL + #endif // _ESP_ASIO_OPENSSL_CONF_H diff --git a/components/asio/port/include/openssl/dh.h b/components/asio/port/include/openssl/dh.h new file mode 100644 index 0000000000..def713cfd3 --- /dev/null +++ b/components/asio/port/include/openssl/dh.h @@ -0,0 +1,23 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _ESP_ASIO_OPENSSL_DH_STUB_H +#define _ESP_ASIO_OPENSSL_DH_STUB_H +// Dummy header needed for ASIO compilation with esp-openssl + +#if defined(ASIO_USE_WOLFSSL) +#include_next "openssl/dh.h" +#endif // ASIO_USE_WOLFSSL + +#endif // _ESP_ASIO_OPENSSL_DH_STUB_H diff --git a/components/asio/port/include/openssl/esp_asio_openssl_stubs.h b/components/asio/port/include/openssl/esp_asio_openssl_stubs.h index 611b4e784e..fde5231722 100644 --- a/components/asio/port/include/openssl/esp_asio_openssl_stubs.h +++ b/components/asio/port/include/openssl/esp_asio_openssl_stubs.h @@ -15,21 +15,57 @@ #ifndef _ESP_ASIO_OPENSSL_STUBS_H #define _ESP_ASIO_OPENSSL_STUBS_H -#include "internal/ssl_x509.h" -#include "internal/ssl_pkey.h" -#include "mbedtls/pem.h" -#include - /** * @note This header contains openssl API which are NOT implemented, and are only provided * as stubs or no-operations to get the ASIO library compiled and working with most * practical use cases as an embedded application on ESP platform */ +#if defined(ASIO_USE_WOLFSSL) + +#include "wolfssl/ssl.h" +// esp-wolfssl disables filesystem by default, but the ssl filesystem functions are needed for the ASIO to compile +// - so we could either configure wolfSSL to use filesystem +// - or use the default wolfSSL and declare the filesystem functions -- preferred option, as whenever +// the filesystem functions are used from app code (potential security impact if private keys in a filesystem) +// compilation fails with linking errors. + +#if defined(NO_FILESYSTEM) +// WolfSSL methods that are not included in standard esp-wolfssl config, must be defined here +// as function stubs, so ASIO compiles, but would get link errors, if these functions were used. + #ifdef __cplusplus extern "C" { #endif +typedef struct WOLFSSL_CTX WOLFSSL_CTX; + +void wolfSSL_CTX_set_verify_depth(WOLFSSL_CTX *ctx,int depth); +int SSL_CTX_load_verify_locations(WOLFSSL_CTX*, const char*, const char*); +int SSL_CTX_use_certificate_file(WOLFSSL_CTX*, const char*, int); +int SSL_CTX_use_certificate_chain_file(WOLFSSL_CTX*, const char*); +int SSL_CTX_use_PrivateKey_file(WOLFSSL_CTX*, const char*, int); +int SSL_CTX_use_RSAPrivateKey_file(WOLFSSL_CTX*, const char*, int); + +#if defined(__cplusplus) +} /* extern C */ +#endif + +#endif // NO_FILESYSTEM + +#elif defined(ASIO_USE_ESP_OPENSSL) + +#include "internal/ssl_x509.h" +#include "internal/ssl_pkey.h" +#include "mbedtls/pem.h" +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + // The most applicable OpenSSL version wrtt ASIO usage #define OPENSSL_VERSION_NUMBER 0x10100001L // SSLv2 methods not supported @@ -40,10 +76,7 @@ extern "C" { #define SSL_R_SHORT_READ 219 #define SSL_OP_ALL 0 #define SSL_OP_SINGLE_DH_USE 0 -//#define OPENSSL_VERSION_NUMBER 0x10001000L #define SSL_OP_NO_COMPRESSION 0 -//#define LIBRESSL_VERSION_NUMBER 1 -//#define PEM_R_NO_START_LINE 110 // Translates mbedTLS PEM parse error, used by ASIO #define PEM_R_NO_START_LINE -MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT @@ -58,9 +91,6 @@ extern "C" { #define NID_subject_alt_name 85 -#define SSL_MODE_RELEASE_BUFFERS 0x00000000L -#define SSL_MODE_ENABLE_PARTIAL_WRITE 0x00000001L -#define SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER 0x00000002L #define GEN_DNS 2 #define GEN_IPADD 7 @@ -153,13 +183,6 @@ void * X509_STORE_CTX_get_ex_data(X509_STORE_CTX *ctx,int idx); */ int SSL_CTX_set_tmp_dh(SSL_CTX *ctx, const DH *dh); -/** - * @brief Sets SSL mode -- not implemented - * - * Current implementation is no-op - */ -uint32_t SSL_set_mode(SSL *ssl, uint32_t mode); - /** * @brief API provaded as declaration only * @@ -182,4 +205,5 @@ int SSL_CTX_clear_chain_certs(SSL_CTX *ctx); } /* extern C */ #endif +#endif /* ASIO_USE_ESP_OPENSSL, ASIO_USE_WOLFSSL */ #endif /* _ESP_ASIO_OPENSSL_STUBS_H */ diff --git a/components/asio/port/include/openssl/rsa.h b/components/asio/port/include/openssl/rsa.h new file mode 100644 index 0000000000..5d9d10e82b --- /dev/null +++ b/components/asio/port/include/openssl/rsa.h @@ -0,0 +1,23 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _ESP_ASIO_OPENSSL_RSA_STUB_H +#define _ESP_ASIO_OPENSSL_RSA_STUB_H +// Dummy header needed for ASIO compilation with esp-openssl + +#if defined(ASIO_USE_WOLFSSL) +#include_next "openssl/rsa.h" +#endif // ASIO_USE_WOLFSSL + +#endif // _ESP_ASIO_OPENSSL_RSA_STUB_H diff --git a/components/asio/port/include/openssl/x509v3.h b/components/asio/port/include/openssl/x509v3.h new file mode 100644 index 0000000000..5ae8e78435 --- /dev/null +++ b/components/asio/port/include/openssl/x509v3.h @@ -0,0 +1,23 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _ESP_ASIO_OPENSSL_X509V3_STUB_H +#define _ESP_ASIO_OPENSSL_X509V3_STUB_H +// Dummy header needed for ASIO compilation with esp-openssl + +#if defined(ASIO_USE_WOLFSSL) +#include_next "openssl/x509v3.h" +#endif // ASIO_USE_WOLFSSL + +#endif // _ESP_ASIO_OPENSSL_X509V3_STUB_H diff --git a/components/asio/port/src/esp_asio_openssl_stubs.c b/components/asio/port/src/esp_asio_openssl_stubs.c index 9fdf53d0d1..6deb099caa 100644 --- a/components/asio/port/src/esp_asio_openssl_stubs.c +++ b/components/asio/port/src/esp_asio_openssl_stubs.c @@ -49,11 +49,6 @@ X509_NAME *X509_get_subject_name(X509 *a) return NULL; } -uint32_t SSL_set_mode(SSL *ssl, uint32_t mode) -{ - return 0; -} - int SSL_CTX_clear_chain_certs(SSL_CTX *ctx) { return 1; diff --git a/components/openssl/include/internal/ssl_code.h b/components/openssl/include/internal/ssl_code.h index 80fdbb20f3..18e687e5f9 100644 --- a/components/openssl/include/internal/ssl_code.h +++ b/components/openssl/include/internal/ssl_code.h @@ -23,6 +23,10 @@ #include "tls1.h" #include "x509_vfy.h" +/* Used in SSL_set_mode() -- supported mode when using BIO */ +#define SSL_MODE_ENABLE_PARTIAL_WRITE 0x00000001L +#define SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER 0x00000002L + /* Used in SSL_set_shutdown()/SSL_get_shutdown(); */ # define SSL_SENT_SHUTDOWN 1 # define SSL_RECEIVED_SHUTDOWN 2 diff --git a/components/openssl/include/internal/ssl_types.h b/components/openssl/include/internal/ssl_types.h index 38c9673548..2871d3a83a 100644 --- a/components/openssl/include/internal/ssl_types.h +++ b/components/openssl/include/internal/ssl_types.h @@ -21,6 +21,7 @@ #include "ssl_code.h" #include +#include typedef void SSL_CIPHER; @@ -256,7 +257,7 @@ struct ssl_st X509_VERIFY_PARAM param; - int err; + uint32_t mode; void (*info_callback) (const SSL *ssl, int type, int val); diff --git a/components/openssl/include/openssl/openssl_err.h b/components/openssl/include/openssl/err.h similarity index 98% rename from components/openssl/include/openssl/openssl_err.h rename to components/openssl/include/openssl/err.h index 930807500e..f4247a4a7c 100644 --- a/components/openssl/include/openssl/openssl_err.h +++ b/components/openssl/include/openssl/err.h @@ -22,8 +22,8 @@ extern "C" { #endif /** - * @note This file contains a very simple implementation of error stack provided - * OpenSSL library. It is OFF by default. + * @note This file contains a very simple implementation of error stack + * for ESP APIs stubs to OpenSSL */ #define OPENSSL_PUT_SYSTEM_ERROR() \ diff --git a/components/openssl/include/openssl/ssl.h b/components/openssl/include/openssl/ssl.h index 6a39c7cd53..4a3376c0db 100644 --- a/components/openssl/include/openssl/ssl.h +++ b/components/openssl/include/openssl/ssl.h @@ -22,7 +22,7 @@ #include "internal/ssl_x509.h" #include "internal/ssl_pkey.h" #include "openssl/bio.h" -#include "openssl/openssl_err.h" +#include "openssl/err.h" /* { @@ -1888,6 +1888,15 @@ openssl_verify_callback SSL_get_verify_callback(const SSL *s); */ void RSA_free(RSA *r); +/** + * @brief Sets SSL mode, partially implemented + * + * @param ssl SSL context + * + * @return the new mode bitmask after adding mode + */ +uint32_t SSL_set_mode(SSL *ssl, uint32_t mode); + #ifdef __cplusplus } #endif diff --git a/components/openssl/library/ssl_bio.c b/components/openssl/library/ssl_bio.c index 46d4616f10..2a70b70aa7 100644 --- a/components/openssl/library/ssl_bio.c +++ b/components/openssl/library/ssl_bio.c @@ -15,7 +15,7 @@ #include "ssl_lib.h" #include "openssl/bio.h" #include "ssl_dbg.h" -#include "openssl/openssl_err.h" +#include "openssl/err.h" #define DEFAULT_BIO_SIZE 1024 diff --git a/components/openssl/library/ssl_lib.c b/components/openssl/library/ssl_lib.c index 46b0cc0fd7..01374a214c 100644 --- a/components/openssl/library/ssl_lib.c +++ b/components/openssl/library/ssl_lib.c @@ -1597,7 +1597,9 @@ void SSL_set_verify_depth(SSL *ssl, int depth) void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *)) { SSL_ASSERT3(ctx); - SSL_ASSERT3(ESP_OPENSSL_VERIFYCB_IS_SUPPORTED); + if (verify_callback) { + SSL_ASSERT3(ESP_OPENSSL_VERIFYCB_IS_SUPPORTED); + } ctx->verify_mode = mode; ctx->default_verify_callback = verify_callback; @@ -1609,7 +1611,9 @@ void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509 void SSL_set_verify(SSL *ssl, int mode, int (*verify_callback)(int, X509_STORE_CTX *)) { SSL_ASSERT3(ssl); - SSL_ASSERT3(ESP_OPENSSL_VERIFYCB_IS_SUPPORTED); + if (verify_callback) { + SSL_ASSERT3(ESP_OPENSSL_VERIFYCB_IS_SUPPORTED); + } ssl->verify_mode = mode; ssl->verify_callback = verify_callback; @@ -1669,3 +1673,12 @@ int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const unsigned char *protos, unsigned ctx->ssl_alpn.alpn_list[i] = NULL; return 0; } + +/** + * @brief Set the mode, but might assert if the related mode is not supported once session starts + */ +uint32_t SSL_set_mode(SSL *ssl, uint32_t mode) +{ + ssl->mode |= mode; + return ssl->mode; +} diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 6f55da47e3..f5597da587 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -25,7 +25,7 @@ #include "mbedtls/error.h" #include "mbedtls/certs.h" #include "openssl/bio.h" -#include "openssl/openssl_err.h" +#include "openssl/err.h" #define X509_INFO_STRING_LENGTH 8192 @@ -316,7 +316,12 @@ int ssl_pm_handshake(SSL *ssl) struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; if (ssl->bio) { + // if using BIO, make sure the mode is supported + SSL_ASSERT1(ssl->mode & (SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER)); mbedtls_ssl_set_bio(&ssl_pm->ssl, ssl->bio, mbedtls_bio_send, mbedtls_bio_recv, NULL); + } else { + // defaults to SSL_read/write using a file descriptor -- expects default mode + SSL_ASSERT1(ssl->mode == 0); } ret = ssl_pm_reload_crt(ssl); diff --git a/components/openssl/test/test_openssl.c b/components/openssl/test/test_openssl.c index f0078847e2..663e9a2c72 100644 --- a/components/openssl/test/test_openssl.c +++ b/components/openssl/test/test_openssl.c @@ -1,3 +1,17 @@ +/* Copyright (c) 2014, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + #include "test_utils.h" #include "openssl/ssl.h" #include "unity.h" diff --git a/docs/en/api-reference/protocols/asio.rst b/docs/en/api-reference/protocols/asio.rst index cd6a40ec30..559759e9e0 100644 --- a/docs/en/api-reference/protocols/asio.rst +++ b/docs/en/api-reference/protocols/asio.rst @@ -14,12 +14,12 @@ Asio also comes with a number of examples which could be find under Documentatio Supported features ^^^^^^^^^^^^^^^^^^ ESP platform port currently supports only network asynchronous socket operations; does not support serial port. -SSL/TLS support if disabled by default and could be enabled in component configuration menu and choosing TLS library from +SSL/TLS support is disabled by default and could be enabled in component configuration menu by choosing TLS library from - mbedTLS with OpenSSL translation layer (default option) - wolfSSL -SSL support is very basic at this stage, not including +SSL support is very basic at this stage and it does include following features: - Verification callbacks - DH property files diff --git a/examples/protocols/asio/ssl_client_server/main/CMakeLists.txt b/examples/protocols/asio/ssl_client_server/main/CMakeLists.txt index 5fd4fe15e8..f962b470c2 100644 --- a/examples/protocols/asio/ssl_client_server/main/CMakeLists.txt +++ b/examples/protocols/asio/ssl_client_server/main/CMakeLists.txt @@ -1,3 +1,3 @@ idf_component_register(SRCS "asio_ssl_main.cpp" INCLUDE_DIRS "." - EMBED_TXTFILES cacert.pem prvtkey.pem) + EMBED_TXTFILES ca.crt server.key srv.crt) diff --git a/examples/protocols/asio/ssl_client_server/main/Kconfig.projbuild b/examples/protocols/asio/ssl_client_server/main/Kconfig.projbuild index a5996f7087..1d03a2af4a 100644 --- a/examples/protocols/asio/ssl_client_server/main/Kconfig.projbuild +++ b/examples/protocols/asio/ssl_client_server/main/Kconfig.projbuild @@ -25,4 +25,12 @@ menu "Example Configuration" help Asio example server ip for the ASIO client to connect to. + config EXAMPLE_CLIENT_VERIFY_PEER + bool "Client to verify peer" + default n + depends on EXAMPLE_CLIENT + help + This option sets client's mode to verify peer, default is + verify-none + endmenu diff --git a/examples/protocols/asio/ssl_client_server/main/asio_ssl_main.cpp b/examples/protocols/asio/ssl_client_server/main/asio_ssl_main.cpp index 8209b680d6..1959f1870e 100644 --- a/examples/protocols/asio/ssl_client_server/main/asio_ssl_main.cpp +++ b/examples/protocols/asio/ssl_client_server/main/asio_ssl_main.cpp @@ -1,3 +1,10 @@ +// +// Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + #include #include "protocol_examples_common.h" #include "esp_event.h" @@ -11,30 +18,36 @@ #include "asio/buffer.hpp" #include "esp_pthread.h" -extern const unsigned char cacert_pem_start[] asm("_binary_cacert_pem_start"); -extern const unsigned char cacert_pem_end[] asm("_binary_cacert_pem_end"); +extern const unsigned char server_pem_start[] asm("_binary_srv_crt_start"); +extern const unsigned char server_pem_end[] asm("_binary_srv_crt_end"); -extern const unsigned char prvtkey_pem_start[] asm("_binary_prvtkey_pem_start"); -extern const unsigned char prvtkey_pem_end[] asm("_binary_prvtkey_pem_end"); +extern const unsigned char cacert_pem_start[] asm("_binary_ca_crt_start"); +extern const unsigned char cacert_pem_end[] asm("_binary_ca_crt_end"); + +extern const unsigned char prvtkey_pem_start[] asm("_binary_server_key_start"); +extern const unsigned char prvtkey_pem_end[] asm("_binary_server_key_end"); const asio::const_buffer cert_chain(cacert_pem_start, cacert_pem_end - cacert_pem_start); const asio::const_buffer privkey(prvtkey_pem_start, prvtkey_pem_end - prvtkey_pem_start); - -using asio::ip::tcp; +const asio::const_buffer server_cert(server_pem_start, server_pem_end - server_pem_start); using asio::ip::tcp; enum { max_length = 1024 }; -class client -{ +class Client { public: - client(asio::io_context& io_context, + Client(asio::io_context& io_context, asio::ssl::context& context, const tcp::resolver::results_type& endpoints) : socket_(io_context, context) { + +#if CONFIG_EXAMPLE_CLIENT_VERIFY_PEER socket_.set_verify_mode(asio::ssl::verify_peer); +#else + socket_.set_verify_mode(asio::ssl::verify_none); +#endif // CONFIG_EXAMPLE_CLIENT_VERIFY_PEER connect(endpoints); } @@ -117,10 +130,9 @@ private: char reply_[max_length]; }; -class session : public std::enable_shared_from_this -{ +class Session : public std::enable_shared_from_this { public: - session(tcp::socket socket, asio::ssl::context& context) + Session(tcp::socket socket, asio::ssl::context& context) : socket_(std::move(socket), context) { } @@ -174,20 +186,19 @@ private: } asio::ssl::stream socket_; - char data_[1024]; + char data_[max_length]; }; -class server -{ +class Server { public: - server(asio::io_context& io_context, unsigned short port) + Server(asio::io_context& io_context, unsigned short port) : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)), context_(asio::ssl::context::tls_server) { context_.set_options( asio::ssl::context::default_workarounds | asio::ssl::context::no_sslv2); - context_.use_certificate_chain(cert_chain); + context_.use_certificate_chain(server_cert); context_.use_private_key(privkey, asio::ssl::context::pem); do_accept(); @@ -201,7 +212,7 @@ private: { if (!error) { - std::make_shared(std::move(socket), context_)->start(); + std::make_shared(std::move(socket), context_)->start(); } do_accept(); @@ -225,7 +236,7 @@ void ssl_server_thread() { asio::io_context io_context; - server s(io_context, 443); + Server s(io_context, 443); io_context.run(); } @@ -240,9 +251,11 @@ void ssl_client_thread() auto endpoints = resolver.resolve(server_ip, server_port); asio::ssl::context ctx(asio::ssl::context::tls_client); - ctx.use_certificate_chain(cert_chain); +#if CONFIG_EXAMPLE_CLIENT_VERIFY_PEER + ctx.add_certificate_authority(cert_chain); +#endif // CONFIG_EXAMPLE_CLIENT_VERIFY_PEER - client c(io_context, ctx, endpoints); + Client c(io_context, ctx, endpoints); io_context.run(); diff --git a/examples/protocols/asio/ssl_client_server/main/ca.crt b/examples/protocols/asio/ssl_client_server/main/ca.crt new file mode 100644 index 0000000000..894f2959b0 --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/main/ca.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDkzCCAnugAwIBAgIUNI5wldYysh6rtCzYmda6H414aRswDQYJKoZIhvcNAQEL +BQAwWTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJRXNwcmVzc2lmMB4X +DTIwMDEyMTA5MDk0NloXDTI1MDEyMDA5MDk0NlowWTELMAkGA1UEBhMCQVUxEzAR +BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 +IEx0ZDESMBAGA1UEAwwJRXNwcmVzc2lmMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAyadSpRnIQBVbEAsbpkrKrOMlBOMIUmA8AfNyOYPLfv0Oa5lBiMAV +3OQDu5tYyFYKwkCUqq65iAm50fPbSH71w1tkja6nZ1yAIM+TvpMlM/WiFGrhY+Tc +kAcLcKUJyPxrv/glzoVslbqUgIhuhCSKA8uk1+ILcn3nWzPcbcowLx31+AHeZj8h +bIAdj6vjqxMCFStp4IcA+ikmCk75LCN4vkkifdkebb/ZDNYCZZhpCBnCHyFAjPc4 +7C+FDVGT3/UUeeTy+Mtn+MqUAhB+W0sPDm1n2h59D4Z/MFm0hl6GQCAKeMJPzssU +BBsRm6zoyPQ4VTqG0uwfNNbORyIfKONMUwIDAQABo1MwUTAdBgNVHQ4EFgQUGYLV +EkgWzxjpltE6texha7zZVxowHwYDVR0jBBgwFoAUGYLVEkgWzxjpltE6texha7zZ +VxowDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAb2EF4Zg2XWNb +eZHnzupCDd9jAhwPqkt7F1OXvxJa/RFUSB9+2izGvikGGhuKY4f0iLuqF+bhExD9 +sapDcdFO2Suh4J3onbwEvmKvsv56K3xhapYg8WwPofpkVirnkwFjpQXGzrYxPujg +BPmSy3psQrhvOr/WH7SefJv2qr4ikaugfE+3enY4PL+C1dSQAuNo1QGgWsZIu0c8 +TZybNZ13vNVMA+tgj2CM8FR3Etaabwtu3TTcAnO7aoBTix/bLBTuZoczhN8/MhG3 +GylmDzFI8a6aKxQL3Fi4PsM82hRKWu3gfs39sR1Ci4V22v8uO5EWBPK0QZvDSc1a +KwwxI4zA0w== +-----END CERTIFICATE----- diff --git a/examples/protocols/asio/ssl_client_server/main/cacert.pem b/examples/protocols/asio/ssl_client_server/main/cacert.pem deleted file mode 100644 index e09c3989cd..0000000000 --- a/examples/protocols/asio/ssl_client_server/main/cacert.pem +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDezCCAmOgAwIBAgIJAPMMNobNczaUMA0GCSqGSIb3DQEBBAUAMHQxEzARBgNV -BAMTCk15IFRlc3QgQ0ExCzAJBgNVBAgTAkhaMQswCQYDVQQGEwJDTjEcMBoGCSqG -SIb3DQEJARYNdGVzdEBjZXJ0LmNvbTElMCMGA1UEChMcUm9vdCBDZXJ0aWZpY2F0 -aW9uIEF1dGhvcml0eTAeFw0xNjExMTUwNTA0MThaFw0xOTExMTUwNTA0MThaMHQx -EzARBgNVBAMTCk15IFRlc3QgQ0ExCzAJBgNVBAgTAkhaMQswCQYDVQQGEwJDTjEc -MBoGCSqGSIb3DQEJARYNdGVzdEBjZXJ0LmNvbTElMCMGA1UEChMcUm9vdCBDZXJ0 -aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBALDjSPDlomepHCzbw4MUrquQAU0xTV4/Npb27k9I5TRVTjIoOs/5hNI2LPFW -e4CREx09ZrT8K3NFOBoSy7bhPAsjGaFxCYYWc9tiX1m5gq3ToVRSmbZ65fE3kvnI -8E/d5VyzA0OMmWbfaolBSTMoWgqRynEaT+z1Eh2yDTzVFy9eov1DdQFUqGDqbH5b -QYvTY5Fyem7UcKWAe2yS0j3H4dVtVBKNY7qV3Px08yGAs5fQFgUwhyB5+qwhvkeL -JdgapGaSTwLgoQKWHbe/lA3NiBIB9hznFUGKo3hmniAvYZbrQcn3tc0l/J4I39v2 -Pm29FAyjWvQyBkGktz2q4elOZYkCAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkq -hkiG9w0BAQQFAAOCAQEAJCJ+97oae/FcOLbPpjCpUQnWqYydgSChgalkZNvr4fVp -TnuNg471l0Y2oTJLoWn2YcbPSFVOEeKkU47mpjMzucHHp0zGaW9SdzhZalWwmbgK -q2ijecIbuFHFNedYTk/03K7eaAcjVhD8e0oOJImeLOL6DAFivA1LUnSgXsdGPDtD -zhISsCPTu+cL1j0yP6HBvLeAyb8kaCWJ05RtiVLRANNHQn/keHajJYpMwnEEbJdG -cqN3whfJoGVbZ6isEf2RQJ0pYRnP7uGLW3wGkLWxfdto8uER8HVDx7fZpevLIqGd -1OoSEi3cIJXWBAjx0TLzzhtb6aeIxBJWQqHThtkKdg== ------END CERTIFICATE----- diff --git a/examples/protocols/asio/ssl_client_server/main/component.mk b/examples/protocols/asio/ssl_client_server/main/component.mk index 656f72d7b7..71869b0a3f 100644 --- a/examples/protocols/asio/ssl_client_server/main/component.mk +++ b/examples/protocols/asio/ssl_client_server/main/component.mk @@ -7,5 +7,6 @@ # please read the ESP-IDF documents if you need to do this. # -COMPONENT_EMBED_TXTFILES := cacert.pem -COMPONENT_EMBED_TXTFILES += prvtkey.pem +COMPONENT_EMBED_TXTFILES := ca.crt +COMPONENT_EMBED_TXTFILES += server.key +COMPONENT_EMBED_TXTFILES += srv.crt diff --git a/examples/protocols/asio/ssl_client_server/main/prvtkey.pem b/examples/protocols/asio/ssl_client_server/main/prvtkey.pem deleted file mode 100644 index 4ead61f6ff..0000000000 --- a/examples/protocols/asio/ssl_client_server/main/prvtkey.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAsONI8OWiZ6kcLNvDgxSuq5ABTTFNXj82lvbuT0jlNFVOMig6 -z/mE0jYs8VZ7gJETHT1mtPwrc0U4GhLLtuE8CyMZoXEJhhZz22JfWbmCrdOhVFKZ -tnrl8TeS+cjwT93lXLMDQ4yZZt9qiUFJMyhaCpHKcRpP7PUSHbINPNUXL16i/UN1 -AVSoYOpsfltBi9NjkXJ6btRwpYB7bJLSPcfh1W1UEo1jupXc/HTzIYCzl9AWBTCH -IHn6rCG+R4sl2BqkZpJPAuChApYdt7+UDc2IEgH2HOcVQYqjeGaeIC9hlutByfe1 -zSX8ngjf2/Y+bb0UDKNa9DIGQaS3Parh6U5liQIDAQABAoIBAB9K9jp3xXVlO3DM -KBhmbkg3n6NSV4eW00d9w8cO9E1/0eeZql3knJS7tNO1IwApqiIAHM1j1yP7WONz -88oUqpSlzwD6iF7KVhC3pHqxEOdDi0Tpn/viXg+Ab2X1IF5guRTfLnKiyviiCazi -edqtBtDb3d6Icx9Oc7gBKcpbQFDGt++wSOb5L+xhRm9B5B4l/6byikiPeKqIK5tC -SoP9Zr1mvpNoGm1P4LvEunFJcRBqVI010VNwfO9P98oVyzJu9/FZZrQxXoY9JdXF -OM6nbl+hMDM3TkEOda9NvBhImozEAvuc97CaaXyR3XivxMqNqNIb4+syUPa2PCS3 -ZztI5qECgYEA1gbVG6ifpvpbBkDPi3Im8fM3F7FLLrQc48FdFjdMvDhHD9lVKucD -Uaa8PF9dbbvlu2cwMyfBOKSuWaXxRxRsiqiPmTunS1MvPzQcSrGwUrL2AogGucn6 -+NrLQf5P4H5IpkDQ9ih3zwjO6xKFK1WeYnYpHM8qUBtl6q0YFyVBPu0CgYEA05Pn -StWA4D7VSbNnVi6lvFyEOUsTrK3v419598TFiq4eXLq6aV8/CQYzKsSzoG+aOZhX -Li+0uyT5cNzUcXYhTsW1hA/pNhMfxMrYiB1x14zlLp2WRGg4vd/+SxX6d9Yd3acX -7QzPKgdDicXs9QN8ozJOICKvNbUI53AJdATVEY0CgYEAwvpGeoQLrdq1weSZLrg3 -soOX1QW3MDz1dKdbXjnStkWut0mOxR7fbysuoPFf8/ARQcCnsHKvHCMqkpESVWbN -2yPkbfxiU8Tcbf/TJljqAOz4ISY6ula/RKZONTixHBrvpEW4GAiV3Q5xMsYUe33s -ZFaw7YXtTj0ng7tdDvjpj6ECgYEApHdUU9ejVq2BHslWiqe4LbO9FMxHfvO2hgix -xugupp6y+2Irhb2EQn+PRq+g8hXOzPaezkhHNTKItDL08T3iplkJwJ6dqmszRsZn -i2dYFzZu8M2PAZ4CfZahFbz/9id7D9HTx3EtmH4NAgvZJpyPRkzUbiaIDDettDpj -Hsyi1AECgYAPLvjBzQj4kPF8Zo9pQEUcz4pmupRVfv3aRfjnahDK4qZHEePDRj+J -W7pzayrs1dyN9QLB8pTc424z7f8MB3llCICN+ohs8CR/eW0NEobE9ldDOeoCr1Vh -NhNSbrN1iZ8U4oLkRTMaDKkVngGffvjGi/q0tOU7hJdZOqNlk2Iahg== ------END RSA PRIVATE KEY----- diff --git a/examples/protocols/asio/ssl_client_server/main/server.key b/examples/protocols/asio/ssl_client_server/main/server.key new file mode 100644 index 0000000000..2a4d650eac --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/main/server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAlUCywNhVv4RO2y9h/XGKZ1azzk3jzHpSBzIGO9LoiA8trC/p +1ykGaUfYPJllYK4HMhC4fUyE3J7tVL2Eskzl26LNPLbEoaBWZM9NhV3iA1/1EtOu +p6umLx+y3sDfvK35YAOUbjdAlBfhnJ4r8h7oTsxl3J5jZ18zgjJnJi2NEFq/yTpO +MiwHLWPjy25fDFixfV9UzSvbgt1JaGPmC7c4QkhHzjyp0+ikuvRIw0p9BBNeqBV2 +da3qBMB5FtodUJTAz6o6OKWbTalLjQi6C1H6z9TnY7IrJBUOy/FWkQH/sEsLdscD +hHa1Dz2oT203QjhzyOSfnNF95D/1MdNcMt6l0wIDAQABAoIBAC1JJTOoMFRc48RT +myrYQYNbZlEphv3q+2qdfhC2zMFDwbrmCtCy7PQSzYSNkpoEE8DYG/JAvmtmeWJl +4pZrCK9ctWM/nWfhC3WpBL97nfEiM20T94F+bn0L5Cz8XqaULv839th+QUTt/hGU +WIctY5VNJXcMQ+MAmtNdUbjex1d3iuxiKHUo4nDoZ8digKFNdtdP5B5nlMq5chCL +mxNRcsGsx2dDAxbGUapdTVPWHPJKpLOBoSkluDsfd2KZADFU2R1SJpAX9+RYh3HM +5FTUdHTUaISxbKkgeDKlEM0lqk2TtGUwCyEj098ewi7Wzsu9w60IplPPUJx5FRG6 +jp3wzLkCgYEAxKp5T20rf/7ysX7x053I7VCjDXUxAaWOEj1uS3AhOkl0NaZg7Di+ +y53fWNkcHdkt2n2LqMt/43UgMYq3TVVcq2eunPNF11e1bJw8CjDafwDs4omwwyVn +lYhPuB4dK2OAib+vU5Zqpp0kZMoxk2MZVgon8z+s8DW/zmB6aFqAWeUCgYEAwkhC +OgmXKMdjOCVy5t2f5UbY8Y9rV3w8eUATuJ47MMwLr4pGYnKoEn9JB4ltWrHv/u5S +fOv3tIrrCEvnCoCbOILwCsY5LqTNXgqova8FB6RpMUQCzhDd8LHuvdHv0WMnMzX1 +3PKuqwh8JS55m4WqZRhzr5BFKG4fHPVs4IcaJVcCgYAzzCaJSdqUKqTnJOUydDNQ +ddWMHNqccWs62J0tF0pZHLGT089hSAzQejMyJnSmU+Ykzr4y5e44DUg+ZCelIZ93 +saYmxlgVwI8THQ8fLADQRIEfpV4996MRmkZM2vmZzOo03Zyi6lIKsga82Rg3lnk8 +1Q3ynknBNpbfF0AGLhfyFQKBgBYlxJ73HutAJ5hr9HhLBYJOnEaVUehMOlycKGNg +bmD2sdJWEgYBChXpurqIORYguLo4EuE4ySkkuPxeIr14wbkkfBbOWBBwKxUwY+IT +xKAFZxR9q1AwbgyVTCEJgKw/AGX/HcMNS0omEnjunmBTUYRq0C1QZgHg490aQUor +PJjLAoGAevzdTpFlVeuKeYh1oDubGO1LinyXpBv7fPFjl+zu4AVbjojcU6yC4OO6 +QvqopE6SyAECKy8kAOFcESPsGc9Lta2XUvI203z7pIVlNVEcJ0+90mQh3Mn1U46l +sZ49PdRvNwNb5wvkh1UqNsMlGFbRlzMbIk45ou4311kCobowZek= +-----END RSA PRIVATE KEY----- diff --git a/examples/protocols/asio/ssl_client_server/main/srv.crt b/examples/protocols/asio/ssl_client_server/main/srv.crt new file mode 100644 index 0000000000..29bfa16641 --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/main/srv.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC9DCCAdwCFA1lSIcHwYKdB2UqOrZxZnVgPObTMA0GCSqGSIb3DQEBCwUAMFkx +CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl +cm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCUVzcHJlc3NpZjAeFw0yMDA2 +MTIwNjA0MTNaFw0yMjA2MDIwNjA0MTNaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJVAssDYVb+ETtsvYf1ximdW +s85N48x6UgcyBjvS6IgPLawv6dcpBmlH2DyZZWCuBzIQuH1MhNye7VS9hLJM5dui +zTy2xKGgVmTPTYVd4gNf9RLTrqerpi8fst7A37yt+WADlG43QJQX4ZyeK/Ie6E7M +ZdyeY2dfM4IyZyYtjRBav8k6TjIsBy1j48tuXwxYsX1fVM0r24LdSWhj5gu3OEJI +R848qdPopLr0SMNKfQQTXqgVdnWt6gTAeRbaHVCUwM+qOjilm02pS40IugtR+s/U +52OyKyQVDsvxVpEB/7BLC3bHA4R2tQ89qE9tN0I4c8jkn5zRfeQ/9THTXDLepdMC +AwEAATANBgkqhkiG9w0BAQsFAAOCAQEAnMYGW+idt37bEE4WPgrRorKWuplR+zHD +wJFz53DQzyIZJHmJ2hR5U0jNcHy/nMq7tbdz9LZPrVF4lZJ3TJhnmkOKjMFPCQE8 +YcmsP3il6eXgtGqg53InOi/uJqEQ9TfM54cbpp6xKbnmpwk4uprISBRQt7u2ZLk2 +40ED6zgjFPDTYmSjSpb2AN6KUB6PflgVs+4p9ViHNq4U3AlYV/BM0+3G4aMX2wNl +ZIpQfOyuaYD5MU50mY+O+gDiiypkpYf6a6S4YJ1sMbavDsP7bW5UMnP0jKYR549q +5hF1fdkXq52DfJ9ya2kl3mANFkKssQV+1KCBMxGoeqfakmJfa03xXA== +-----END CERTIFICATE----- diff --git a/examples/protocols/asio/ssl_client_server/partitions.csv b/examples/protocols/asio/ssl_client_server/partitions.csv index d01414b8a9..f3aa8e2b48 100644 --- a/examples/protocols/asio/ssl_client_server/partitions.csv +++ b/examples/protocols/asio/ssl_client_server/partitions.csv @@ -2,4 +2,4 @@ # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 1200000, +factory, app, factory, 0x10000, 1400000, diff --git a/examples/protocols/asio/ssl_client_server/sdkconfig.ci b/examples/protocols/asio/ssl_client_server/sdkconfig.ci index 958aa45d42..f1c43e8ac7 100644 --- a/examples/protocols/asio/ssl_client_server/sdkconfig.ci +++ b/examples/protocols/asio/ssl_client_server/sdkconfig.ci @@ -3,4 +3,4 @@ CONFIG_EXAMPLE_SERVER=y CONFIG_EXAMPLE_SERVER_NAME="localhost" CONFIG_EXAMPLE_CONNECT_WIFI=n CONFIG_EXAMPLE_CONNECT_ETHERNET=n - +CONFIG_EXAMPLE_CLIENT_VERIFY_PEER=y diff --git a/examples/protocols/asio/ssl_client_server/sdkconfig.defaults b/examples/protocols/asio/ssl_client_server/sdkconfig.defaults index 8ff365b198..7340a4467f 100644 --- a/examples/protocols/asio/ssl_client_server/sdkconfig.defaults +++ b/examples/protocols/asio/ssl_client_server/sdkconfig.defaults @@ -1,3 +1,4 @@ +CONFIG_ASIO_SSL_SUPPORT=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" From 0f72c05d783eace0c5b6486a5c642b596f9f8918 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Tue, 16 Jun 2020 15:53:19 +0200 Subject: [PATCH 6/7] openssl: made verification mode conversion to mbetls modes more strict --- components/openssl/platform/ssl_pm.c | 33 ++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index f5597da587..7bb27b89f2 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -249,21 +249,36 @@ void ssl_pm_free(SSL *ssl) static int ssl_pm_reload_crt(SSL *ssl) { int ret; - int mode; + int mode = MBEDTLS_SSL_VERIFY_UNSET; struct ssl_pm *ssl_pm = ssl->ssl_pm; struct x509_pm *ca_pm = (struct x509_pm *)ssl->client_CA->x509_pm; struct pkey_pm *pkey_pm = (struct pkey_pm *)ssl->cert->pkey->pkey_pm; struct x509_pm *crt_pm = (struct x509_pm *)ssl->cert->x509->x509_pm; - if (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) - mode = MBEDTLS_SSL_VERIFY_REQUIRED; - else if (ssl->verify_mode & SSL_VERIFY_PEER) - mode = MBEDTLS_SSL_VERIFY_OPTIONAL; - else if (ssl->verify_mode & SSL_VERIFY_CLIENT_ONCE) - mode = MBEDTLS_SSL_VERIFY_UNSET; - else - mode = MBEDTLS_SSL_VERIFY_NONE; +/* OpenSSL verification modes outline (see `man SSL_set_verify` for more details) + * + * | openssl mode | Server | Client | + * | SSL_VERIFY_NONE | will not send a client certificate request | server certificate which will be checked | + * handshake will be continued regardless | + * | SSL_VERIFY_PEER | depends on SSL_VERIFY_FAIL_IF_NO_PEER_CERT | handshake is terminated if verify fails | + * (unless anonymous ciphers--not supported | + * | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | handshake is terminated if | ignored | + * client cert verify fails | | + */ + if (ssl->method->endpoint == MBEDTLS_SSL_IS_SERVER) { + if (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) + mode = MBEDTLS_SSL_VERIFY_REQUIRED; + else if (ssl->verify_mode & SSL_VERIFY_PEER) + mode = MBEDTLS_SSL_VERIFY_OPTIONAL; + else if (ssl->verify_mode == SSL_VERIFY_NONE) + mode = MBEDTLS_SSL_VERIFY_NONE; + } else if (ssl->method->endpoint == MBEDTLS_SSL_IS_CLIENT) { + if (ssl->verify_mode & SSL_VERIFY_PEER) + mode = MBEDTLS_SSL_VERIFY_REQUIRED; + else if (ssl->verify_mode == SSL_VERIFY_NONE) + mode = MBEDTLS_SSL_VERIFY_NONE; + } mbedtls_ssl_conf_authmode(&ssl_pm->conf, mode); From b2150f86a502f6df063ed1e09d146a487db9f6c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=C4=8Cerm=C3=A1k?= Date: Thu, 18 Jun 2020 16:16:41 +0800 Subject: [PATCH 7/7] asio: make the example code conform to Espressif C++ standards --- .../ssl_client_server/main/asio_ssl_main.cpp | 170 ++++++++---------- 1 file changed, 73 insertions(+), 97 deletions(-) diff --git a/examples/protocols/asio/ssl_client_server/main/asio_ssl_main.cpp b/examples/protocols/asio/ssl_client_server/main/asio_ssl_main.cpp index 1959f1870e..8356dc7160 100644 --- a/examples/protocols/asio/ssl_client_server/main/asio_ssl_main.cpp +++ b/examples/protocols/asio/ssl_client_server/main/asio_ssl_main.cpp @@ -27,20 +27,20 @@ extern const unsigned char cacert_pem_end[] asm("_binary_ca_crt_end"); extern const unsigned char prvtkey_pem_start[] asm("_binary_server_key_start"); extern const unsigned char prvtkey_pem_end[] asm("_binary_server_key_end"); -const asio::const_buffer cert_chain(cacert_pem_start, cacert_pem_end - cacert_pem_start); -const asio::const_buffer privkey(prvtkey_pem_start, prvtkey_pem_end - prvtkey_pem_start); -const asio::const_buffer server_cert(server_pem_start, server_pem_end - server_pem_start); +static const asio::const_buffer cert_chain(cacert_pem_start, cacert_pem_end - cacert_pem_start); +static const asio::const_buffer privkey(prvtkey_pem_start, prvtkey_pem_end - prvtkey_pem_start); +static const asio::const_buffer server_cert(server_pem_start, server_pem_end - server_pem_start); using asio::ip::tcp; -enum { max_length = 1024 }; +static const std::size_t max_length = 1024; class Client { public: - Client(asio::io_context& io_context, - asio::ssl::context& context, - const tcp::resolver::results_type& endpoints) - : socket_(io_context, context) + Client(asio::io_context &io_context, + asio::ssl::context &context, + const tcp::resolver::results_type &endpoints) + : socket_(io_context, context) { #if CONFIG_EXAMPLE_CLIENT_VERIFY_PEER @@ -53,37 +53,29 @@ public: } private: - void connect(const tcp::resolver::results_type& endpoints) + void connect(const tcp::resolver::results_type &endpoints) { asio::async_connect(socket_.lowest_layer(), endpoints, - [this](const std::error_code& error, - const tcp::endpoint& /*endpoint*/) - { - if (!error) - { - handshake(); - } - else - { - std::cout << "Connect failed: " << error.message() << "\n"; - } - }); + [this](const std::error_code & error, + const tcp::endpoint & /*endpoint*/) { + if (!error) { + handshake(); + } else { + std::cout << "Connect failed: " << error.message() << "\n"; + } + }); } void handshake() { socket_.async_handshake(asio::ssl::stream_base::client, - [this](const std::error_code& error) - { - if (!error) - { - send_request(); - } - else - { - std::cout << "Handshake failed: " << error.message() << "\n"; - } - }); + [this](const std::error_code & error) { + if (!error) { + send_request(); + } else { + std::cout << "Handshake failed: " << error.message() << "\n"; + } + }); } void send_request() @@ -92,37 +84,28 @@ private: asio::async_write(socket_, asio::buffer(request_, request_length), - [this](const std::error_code& error, std::size_t length) - { - if (!error) - { - receive_response(length); - } - else - { - std::cout << "Write failed: " << error.message() << "\n"; - } - }); + [this](const std::error_code & error, std::size_t length) { + if (!error) { + receive_response(length); + } else { + std::cout << "Write failed: " << error.message() << "\n"; + } + }); } void receive_response(std::size_t length) { asio::async_read(socket_, asio::buffer(reply_, length), - [this](const std::error_code& error, std::size_t length) - { - if (!error) - { - std::cout << "Reply: "; - std::cout.write(reply_, length); - std::cout << "\n"; - } - else - { - std::cout << "Read failed: " << error.message() << "\n"; - } - }); - + [this](const std::error_code & error, std::size_t length) { + if (!error) { + std::cout << "Reply: "; + std::cout.write(reply_, length); + std::cout << "\n"; + } else { + std::cout << "Read failed: " << error.message() << "\n"; + } + }); } asio::ssl::stream socket_; @@ -132,8 +115,8 @@ private: class Session : public std::enable_shared_from_this { public: - Session(tcp::socket socket, asio::ssl::context& context) - : socket_(std::move(socket), context) + Session(tcp::socket socket, asio::ssl::context &context) + : socket_(std::move(socket), context) { } @@ -147,42 +130,37 @@ private: { auto self(shared_from_this()); socket_.async_handshake(asio::ssl::stream_base::server, - [this, self](const std::error_code& error) - { - if (!error) - { - do_read(); - } - }); + [this, self](const std::error_code & error) { + if (!error) { + do_read(); + } + }); } void do_read() { auto self(shared_from_this()); socket_.async_read_some(asio::buffer(data_), - [this, self](const std::error_code& ec, std::size_t length) - { - if (!ec) - { - data_[length] = 0; - std::cout << "Server received: " << data_ << std::endl; - do_write(length); - } - }); + [this, self](const std::error_code & ec, std::size_t length) { + if (!ec) { + std::cout << "Server received: "; + std::cout.write(data_, length); + std::cout << std::endl; + do_write(length); + } + }); } void do_write(std::size_t length) { auto self(shared_from_this()); asio::async_write(socket_, asio::buffer(data_, length), - [this, self](const std::error_code& ec, - std::size_t /*length*/) - { - if (!ec) - { - do_read(); - } - }); + [this, self](const std::error_code & ec, + std::size_t /*length*/) { + if (!ec) { + do_read(); + } + }); } asio::ssl::stream socket_; @@ -191,13 +169,13 @@ private: class Server { public: - Server(asio::io_context& io_context, unsigned short port) - : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)), - context_(asio::ssl::context::tls_server) + Server(asio::io_context &io_context, unsigned short port) + : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)), + context_(asio::ssl::context::tls_server) { context_.set_options( - asio::ssl::context::default_workarounds - | asio::ssl::context::no_sslv2); + asio::ssl::context::default_workarounds + | asio::ssl::context::no_sslv2); context_.use_certificate_chain(server_cert); context_.use_private_key(privkey, asio::ssl::context::pem); @@ -208,15 +186,13 @@ private: void do_accept() { acceptor_.async_accept( - [this](const std::error_code& error, tcp::socket socket) - { - if (!error) - { - std::make_shared(std::move(socket), context_)->start(); - } + [this](const std::error_code & error, tcp::socket socket) { + if (!error) { + std::make_shared(std::move(socket), context_)->start(); + } - do_accept(); - }); + do_accept(); + }); } tcp::acceptor acceptor_; @@ -289,7 +265,7 @@ extern "C" void app_main(void) work_threads.emplace_back(ssl_client_thread); #endif // CONFIG_EXAMPLE_CLIENT - for (auto & t : work_threads) { + for (auto &t : work_threads) { t.join(); }