From 41d03e8747b37e93f3f7d9e8068e3788bc2ee3ee Mon Sep 17 00:00:00 2001 From: slaff Date: Fri, 3 Jan 2020 09:45:22 +0100 Subject: [PATCH] Feature: ssl abstraction (#1999) Sming now supports different SSL adapters. At the moment we support axTLS and BearSSL. Switching between the different adapters is very easy. For example the Basic_Ssl example can be compiled to use Bearssl using the following command: ``` make ENABLE_SSL=Bearssl ``` See Basic_Ssl and HttpServer_ConfigNetwork samples. NOTICE: This PR introduces breaking changes. Make sure to read the migration guide: https://sming.readthedocs.io/en/latest/upgrading/4.0-4.1.html --- .gitmodules | 6 +- README.md | 2 +- Sming/Arch/Host/Components/lwip/lwipopts.h | 1 + Sming/Components/axtls-8266/axtls-8266 | 2 +- Sming/Components/axtls-8266/axtls-8266.patch | 57 ++- Sming/Components/axtls-8266/component.mk | 24 +- .../.patches/bearssl/src/config.h | 260 +++++++++++ Sming/Components/bearssl-esp8266/README.rst | 4 + Sming/Components/bearssl-esp8266/bearssl | 1 + .../Components/bearssl-esp8266/bearssl.patch | 27 ++ Sming/Components/bearssl-esp8266/component.mk | 24 + Sming/Components/ssl/.cs | 0 Sming/Components/ssl/Axtls/AxCertificate.cpp | 56 +++ Sming/Components/ssl/Axtls/AxCertificate.h | 49 ++ Sming/Components/ssl/Axtls/AxConnection.cpp | 100 ++++ Sming/Components/ssl/Axtls/AxConnection.h | 100 ++++ Sming/Components/ssl/Axtls/AxContext.cpp | 119 +++++ Sming/Components/ssl/Axtls/AxContext.h | 35 ++ Sming/Components/ssl/Axtls/AxError.cpp | 98 ++++ Sming/Components/ssl/Axtls/AxError.h | 20 + Sming/Components/ssl/Axtls/AxFactory.cpp | 30 ++ Sming/Components/ssl/BearSsl/Asn1Parser.h | 144 ++++++ .../Components/ssl/BearSsl/BrCertificate.cpp | 37 ++ Sming/Components/ssl/BearSsl/BrCertificate.h | 42 ++ .../ssl/BearSsl/BrClientConnection.cpp | 46 ++ .../ssl/BearSsl/BrClientConnection.h | 61 +++ Sming/Components/ssl/BearSsl/BrConnection.cpp | 248 ++++++++++ Sming/Components/ssl/BearSsl/BrConnection.h | 99 ++++ Sming/Components/ssl/BearSsl/BrContext.cpp | 48 ++ Sming/Components/ssl/BearSsl/BrContext.h | 38 ++ Sming/Components/ssl/BearSsl/BrError.cpp | 122 +++++ Sming/Components/ssl/BearSsl/BrError.h | 19 + Sming/Components/ssl/BearSsl/BrFactory.cpp | 30 ++ .../ssl/BearSsl/BrServerConnection.cpp | 56 +++ .../ssl/BearSsl/BrServerConnection.h | 51 +++ Sming/Components/ssl/BearSsl/BrServerKey.cpp | 87 ++++ Sming/Components/ssl/BearSsl/BrServerKey.h | 61 +++ Sming/Components/ssl/BearSsl/X509Context.cpp | 45 ++ Sming/Components/ssl/BearSsl/X509Context.h | 128 ++++++ Sming/Components/ssl/BearSsl/X509Name.cpp | 57 +++ Sming/Components/ssl/BearSsl/X509Name.h | 59 +++ Sming/Components/ssl/NoSsl/FactoryImpl.cpp | 6 + Sming/Components/ssl/README.rst | 15 + Sming/Components/ssl/Tools/make_certs.sh | 182 ++++++++ Sming/Components/ssl/api.rst | 4 + Sming/Components/ssl/comparison.rst | 7 + Sming/Components/ssl/component.mk | 61 +++ .../ssl/include/Network/Ssl/Alert.h | 54 +++ .../ssl/include/Network/Ssl/Certificate.h | 110 +++++ .../ssl/include/Network/Ssl/CipherSuite.h | 164 +++++++ .../ssl/include/Network/Ssl/Connection.h | 115 +++++ .../ssl/include/Network/Ssl/Context.h | 69 +++ .../ssl/include/Network/Ssl/Crypto.h | 37 ++ .../ssl/include/Network/Ssl/Factory.h | 42 ++ .../ssl/include/Network/Ssl/Fingerprints.h} | 55 ++- .../ssl/include/Network/Ssl/InputBuffer.h | 39 ++ .../ssl/include/Network/Ssl/KeyCertPair.h} | 69 ++- .../ssl/include/Network/Ssl/Session.h | 187 ++++++++ .../ssl/include/Network/Ssl/SessionId.h} | 38 +- .../ssl/include/Network/Ssl/Validator.h | 87 ++++ Sming/Components/ssl/include/SslDebug.h | 21 + Sming/Components/ssl/index.rst | 25 + Sming/Components/ssl/src/Alert.cpp | 30 ++ Sming/Components/ssl/src/Certificate.cpp | 45 ++ Sming/Components/ssl/src/CipherSuite.cpp | 36 ++ Sming/Components/ssl/src/Connection.cpp | 77 ++++ Sming/Components/ssl/src/Fingerprints.cpp | 84 ++++ Sming/Components/ssl/src/InputBuffer.cpp | 32 ++ Sming/Components/ssl/src/KeyCertPair.cpp | 49 ++ Sming/Components/ssl/src/Session.cpp | 241 ++++++++++ .../ssl/src/Validator.cpp} | 40 +- .../Network/Http/HttpClientConnection.cpp | 17 +- .../Core/Network/Http/HttpClientConnection.h | 15 +- Sming/Core/Network/Http/HttpRequest.cpp | 27 -- Sming/Core/Network/Http/HttpRequest.h | 55 +-- Sming/Core/Network/HttpServer.cpp | 3 - Sming/Core/Network/HttpServer.h | 4 - Sming/Core/Network/MqttClient.cpp | 4 +- Sming/Core/Network/MqttClient.h | 15 +- Sming/Core/Network/SmtpClient.cpp | 21 +- Sming/Core/Network/SmtpClient.h | 11 +- Sming/Core/Network/Ssl/SslFingerprints.cpp | 83 ---- Sming/Core/Network/Ssl/SslStructs.h | 28 -- Sming/Core/Network/Ssl/SslValidator.h | 79 ---- Sming/Core/Network/TcpClient.cpp | 23 +- Sming/Core/Network/TcpClient.h | 84 +--- Sming/Core/Network/TcpConnection.cpp | 427 +++++++----------- Sming/Core/Network/TcpConnection.h | 188 +++----- Sming/Core/Network/TcpServer.cpp | 49 +- Sming/Core/Network/TcpServer.h | 22 - Sming/Core/Network/WebsocketClient.cpp | 12 +- Sming/Core/Network/WebsocketClient.h | 13 +- Sming/README.rst | 20 +- Sming/build.mk | 2 +- Sming/component.mk | 24 +- docs/source/experimental/httpserver-ssl.rst | 63 ++- docs/source/framework/core/network/index.rst | 1 + docs/source/framework/core/network/ssl.rst | 23 - docs/source/index.rst | 15 +- docs/source/upgrading/4.0-4.1.rst | 164 +++++++ docs/source/upgrading/index.rst | 1 + samples/Basic_Ssl/app/application.cpp | 121 ++--- samples/Basic_Ssl/include/CounterStream.h | 35 ++ samples/Echo_Ssl/app/application.cpp | 61 +-- samples/HttpClient/app/application.cpp | 80 +--- .../app/application.cpp | 15 +- .../HttpServer_ConfigNetwork/cert/key_1024 | Bin 0 -> 609 bytes .../cert/key_1024.pem | 15 + .../cert/x509_1024.cer | Bin 0 -> 475 bytes .../cert/x509_1024.pem | 12 + .../include/ssl/cert.h | 27 ++ .../include/ssl/private_key.h | 35 ++ samples/MqttClient_Hello/app/application.cpp | 50 +- samples/SmtpClient/.cproject | 58 +++ samples/SmtpClient/app/application.cpp | 4 +- samples/Websocket_Client/app/application.cpp | 9 +- 116 files changed, 5188 insertions(+), 1341 deletions(-) create mode 100644 Sming/Components/bearssl-esp8266/.patches/bearssl/src/config.h create mode 100644 Sming/Components/bearssl-esp8266/README.rst create mode 160000 Sming/Components/bearssl-esp8266/bearssl create mode 100644 Sming/Components/bearssl-esp8266/bearssl.patch create mode 100644 Sming/Components/bearssl-esp8266/component.mk create mode 100644 Sming/Components/ssl/.cs create mode 100644 Sming/Components/ssl/Axtls/AxCertificate.cpp create mode 100644 Sming/Components/ssl/Axtls/AxCertificate.h create mode 100644 Sming/Components/ssl/Axtls/AxConnection.cpp create mode 100644 Sming/Components/ssl/Axtls/AxConnection.h create mode 100644 Sming/Components/ssl/Axtls/AxContext.cpp create mode 100644 Sming/Components/ssl/Axtls/AxContext.h create mode 100644 Sming/Components/ssl/Axtls/AxError.cpp create mode 100644 Sming/Components/ssl/Axtls/AxError.h create mode 100644 Sming/Components/ssl/Axtls/AxFactory.cpp create mode 100644 Sming/Components/ssl/BearSsl/Asn1Parser.h create mode 100644 Sming/Components/ssl/BearSsl/BrCertificate.cpp create mode 100644 Sming/Components/ssl/BearSsl/BrCertificate.h create mode 100644 Sming/Components/ssl/BearSsl/BrClientConnection.cpp create mode 100644 Sming/Components/ssl/BearSsl/BrClientConnection.h create mode 100644 Sming/Components/ssl/BearSsl/BrConnection.cpp create mode 100644 Sming/Components/ssl/BearSsl/BrConnection.h create mode 100644 Sming/Components/ssl/BearSsl/BrContext.cpp create mode 100644 Sming/Components/ssl/BearSsl/BrContext.h create mode 100644 Sming/Components/ssl/BearSsl/BrError.cpp create mode 100644 Sming/Components/ssl/BearSsl/BrError.h create mode 100644 Sming/Components/ssl/BearSsl/BrFactory.cpp create mode 100644 Sming/Components/ssl/BearSsl/BrServerConnection.cpp create mode 100644 Sming/Components/ssl/BearSsl/BrServerConnection.h create mode 100644 Sming/Components/ssl/BearSsl/BrServerKey.cpp create mode 100644 Sming/Components/ssl/BearSsl/BrServerKey.h create mode 100644 Sming/Components/ssl/BearSsl/X509Context.cpp create mode 100644 Sming/Components/ssl/BearSsl/X509Context.h create mode 100644 Sming/Components/ssl/BearSsl/X509Name.cpp create mode 100644 Sming/Components/ssl/BearSsl/X509Name.h create mode 100644 Sming/Components/ssl/NoSsl/FactoryImpl.cpp create mode 100644 Sming/Components/ssl/README.rst create mode 100644 Sming/Components/ssl/Tools/make_certs.sh create mode 100644 Sming/Components/ssl/api.rst create mode 100644 Sming/Components/ssl/comparison.rst create mode 100644 Sming/Components/ssl/component.mk create mode 100644 Sming/Components/ssl/include/Network/Ssl/Alert.h create mode 100644 Sming/Components/ssl/include/Network/Ssl/Certificate.h create mode 100644 Sming/Components/ssl/include/Network/Ssl/CipherSuite.h create mode 100644 Sming/Components/ssl/include/Network/Ssl/Connection.h create mode 100644 Sming/Components/ssl/include/Network/Ssl/Context.h create mode 100644 Sming/Components/ssl/include/Network/Ssl/Crypto.h create mode 100644 Sming/Components/ssl/include/Network/Ssl/Factory.h rename Sming/{Core/Network/Ssl/SslFingerprints.h => Components/ssl/include/Network/Ssl/Fingerprints.h} (69%) create mode 100644 Sming/Components/ssl/include/Network/Ssl/InputBuffer.h rename Sming/{Core/Network/Ssl/SslKeyCertPair.h => Components/ssl/include/Network/Ssl/KeyCertPair.h} (63%) create mode 100644 Sming/Components/ssl/include/Network/Ssl/Session.h rename Sming/{Core/Network/Ssl/SslSessionId.h => Components/ssl/include/Network/Ssl/SessionId.h} (55%) create mode 100644 Sming/Components/ssl/include/Network/Ssl/Validator.h create mode 100644 Sming/Components/ssl/include/SslDebug.h create mode 100644 Sming/Components/ssl/index.rst create mode 100644 Sming/Components/ssl/src/Alert.cpp create mode 100644 Sming/Components/ssl/src/Certificate.cpp create mode 100644 Sming/Components/ssl/src/CipherSuite.cpp create mode 100644 Sming/Components/ssl/src/Connection.cpp create mode 100644 Sming/Components/ssl/src/Fingerprints.cpp create mode 100644 Sming/Components/ssl/src/InputBuffer.cpp create mode 100644 Sming/Components/ssl/src/KeyCertPair.cpp create mode 100644 Sming/Components/ssl/src/Session.cpp rename Sming/{Core/Network/Ssl/SslValidator.cpp => Components/ssl/src/Validator.cpp} (68%) delete mode 100644 Sming/Core/Network/Ssl/SslFingerprints.cpp delete mode 100644 Sming/Core/Network/Ssl/SslStructs.h delete mode 100644 Sming/Core/Network/Ssl/SslValidator.h delete mode 100644 docs/source/framework/core/network/ssl.rst create mode 100644 docs/source/upgrading/4.0-4.1.rst create mode 100644 samples/Basic_Ssl/include/CounterStream.h create mode 100644 samples/HttpServer_ConfigNetwork/cert/key_1024 create mode 100644 samples/HttpServer_ConfigNetwork/cert/key_1024.pem create mode 100644 samples/HttpServer_ConfigNetwork/cert/x509_1024.cer create mode 100644 samples/HttpServer_ConfigNetwork/cert/x509_1024.pem create mode 100644 samples/HttpServer_ConfigNetwork/include/ssl/cert.h create mode 100644 samples/HttpServer_ConfigNetwork/include/ssl/private_key.h create mode 100644 samples/SmtpClient/.cproject diff --git a/.gitmodules b/.gitmodules index d74fceca5d..4f9e355ca2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -48,10 +48,14 @@ path = Sming/Arch/Esp8266/Components/driver/new-pwm url = https://github.com/StefanBruens/ESP8266_new_pwm.git ignore = dirty -[submodule "ESP8266.axtls-8266"] +[submodule "ESP8266.axtls"] path = Sming/Components/axtls-8266/axtls-8266 url = https://github.com/igrr/axtls-8266.git ignore = dirty +[submodule "ESP8266.bearssl"] + path = Sming/Components/bearssl-esp8266/bearssl + url = https://github.com/earlephilhower/bearssl-esp8266 + ignore = dirty [submodule "ESP8266.umm_malloc"] path = Sming/Arch/Esp8266/Components/heap/umm_malloc url = https://github.com/rhempel/umm_malloc.git diff --git a/README.md b/README.md index 87fb051871..4e7b298409 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ Please note Version 4 documentation is at [sming.readthedocs.io](https://sming.r * Async TCP and UDP stack based on [LWIP](http://savannah.nongnu.org/projects/lwip/). * With clients supporting: HTTP, MQTT, WebSockets and SMTP. * And servers for: DNS, FTP, HTTP(+ WebSockets), Telnet. - * With SSL support for all network clients and servers based on [axTLS 2.1+](https://github.com/igrr/axtls-8266) with [Lwirax](https://github.com/attachix/lwirax/). + * With [SSL support](https://sming.readthedocs.io/en/latest/framework/core/network/ssl.html) for all network clients and servers. Based on [axTLS](http://axtls.sourceforge.net/) and [BearSSL](https://www.bearssl.org/). * Out of the box support for OTA over HTTPS. * ESP8266 specific features. * Integrated boot loader [rboot](https://sming.readthedocs.io/en/latest/_inc/Sming/Components/rboot/index.html) with support for 1MB ROMs, OTA firmware updating and ROM switching. diff --git a/Sming/Arch/Host/Components/lwip/lwipopts.h b/Sming/Arch/Host/Components/lwip/lwipopts.h index b6250d69de..760297287d 100644 --- a/Sming/Arch/Host/Components/lwip/lwipopts.h +++ b/Sming/Arch/Host/Components/lwip/lwipopts.h @@ -348,6 +348,7 @@ #define LWIP_LISTEN_BACKLOG 0 #define TCP_QUEUE_OOSEQ 0 #define LWIP_TCP_KEEPALIVE 1 +#define TCP_MSS 1390 /* ---------------------------------- diff --git a/Sming/Components/axtls-8266/axtls-8266 b/Sming/Components/axtls-8266/axtls-8266 index 6b2fd4c461..6c3e87b307 160000 --- a/Sming/Components/axtls-8266/axtls-8266 +++ b/Sming/Components/axtls-8266/axtls-8266 @@ -1 +1 @@ -Subproject commit 6b2fd4c4615d66cbd5e7e787b0b707230b451146 +Subproject commit 6c3e87b307ffb67e670909a3c5ac613032eed0ba diff --git a/Sming/Components/axtls-8266/axtls-8266.patch b/Sming/Components/axtls-8266/axtls-8266.patch index 36af4a787a..d9622a5945 100644 --- a/Sming/Components/axtls-8266/axtls-8266.patch +++ b/Sming/Components/axtls-8266/axtls-8266.patch @@ -154,7 +154,7 @@ index 4972119..da75839 100644 return 0; } diff --git a/ssl/os_port.h b/ssl/os_port.h -index e0b9e46..268512e 100644 +index e0b9e46..ea246ae 100644 --- a/ssl/os_port.h +++ b/ssl/os_port.h @@ -43,7 +43,12 @@ extern "C" { @@ -171,9 +171,12 @@ index e0b9e46..268512e 100644 #ifdef WIN32 #define STDCALL __stdcall -@@ -62,12 +67,11 @@ extern "C" { +@@ -60,14 +65,13 @@ extern "C" { - #include "util/time.h" + #if defined(ESP8266) + +-#include "util/time.h" ++#include "../util/time.h" #include +#ifndef alloca #define alloca(size) __builtin_alloca(size) @@ -295,7 +298,7 @@ index e0b9e46..268512e 100644 EXP_FUNC int STDCALL ax_open(const char *pathname, int flags); diff --git a/ssl/tls1.c b/ssl/tls1.c -index 10b592c..be0fc29 100644 +index 8f0fbfb..be0fc29 100644 --- a/ssl/tls1.c +++ b/ssl/tls1.c @@ -1368,6 +1368,10 @@ int basic_read(SSL *ssl, uint8_t **in_data) @@ -309,25 +312,6 @@ index 10b592c..be0fc29 100644 if (IS_SET_SSL_FLAG(SSL_SENT_CLOSE_NOTIFY)) return SSL_CLOSE_NOTIFY; -@@ -1426,6 +1430,9 @@ int basic_read(SSL *ssl, uint8_t **in_data) - goto error; - } - -+ memcpy(ssl->hmac_header, buf, 3); /* store for hmac */ -+ ssl->record_type = buf[0]; -+ - /* is the allocated buffer large enough to handle all the data? if not, increase its size*/ - if (ssl->need_bytes > ssl->max_plain_length+RT_EXTRA-BM_RECORD_OFFSET) - { -@@ -1439,8 +1446,6 @@ int basic_read(SSL *ssl, uint8_t **in_data) - } - - CLR_SSL_FLAG(SSL_NEED_RECORD); -- memcpy(ssl->hmac_header, buf, 3); /* store for hmac */ -- ssl->record_type = buf[0]; - goto error; /* no error, we're done */ - } - diff --git a/tools/make_certs.sh b/tools/make_certs.sh index fc6cc90..3113355 100644 --- a/tools/make_certs.sh @@ -676,4 +660,31 @@ index d90b093..f18fbd5 100644 + m_putc((num <= 9) ? (num + '0') : (num + 'A' - 10)); } } + +diff --git a/ssl/crypto_misc.h b/ssl/crypto_misc.h +index 02d9306..191d605 100644 +--- a/ssl/crypto_misc.h ++++ b/ssl/crypto_misc.h +@@ -39,8 +39,8 @@ + extern "C" { + #endif + +-#include "crypto.h" +-#include "bigint.h" ++#include "../crypto/crypto.h" ++#include "../crypto/bigint.h" + + /************************************************************************** + * X509 declarations +diff --git a/ssl/tls1.h b/ssl/tls1.h +index dac63b9..f35a942 100644 +--- a/ssl/tls1.h ++++ b/ssl/tls1.h +@@ -44,7 +44,6 @@ extern "C" { + #include "config.h" + #include "os_int.h" + #include "os_port.h" +-#include "crypto.h" + #include "crypto_misc.h" + #define SSL_PROTOCOL_MIN_VERSION 0x31 /* TLS v1.0 */ diff --git a/Sming/Components/axtls-8266/component.mk b/Sming/Components/axtls-8266/component.mk index c23722d916..613e5c754a 100644 --- a/Sming/Components/axtls-8266/component.mk +++ b/Sming/Components/axtls-8266/component.mk @@ -1,10 +1,6 @@ -COMPONENT_VARS := SSL_DEBUG -SSL_DEBUG ?= 0 - COMPONENT_SUBMODULES := axtls-8266 COMPONENT_SRCDIRS := \ - axtls-8266/compat \ axtls-8266/crypto \ axtls-8266/ssl @@ -13,26 +9,12 @@ COMPONENT_SRCDIRS += \ axtls-8266/replacements endif -COMPONENT_INCDIRS := \ - . \ +COMPONENT_INCDIRS := . + +EXTRA_INCDIR := \ axtls-8266 \ axtls-8266/ssl \ axtls-8266/crypto GLOBAL_CFLAGS += -DLWIP_RAW=1 COMPONENT_CFLAGS := -DWITH_PGM_READ_HELPER=1 -DAXTLS_BUILD -ifeq ($(SSL_DEBUG),1) - COMPONENT_CFLAGS += -DAXL_DEBUG=1 - GLOBAL_CFLAGS += -DSSL_DEBUG=1 -endif - -# Application -CUSTOM_TARGETS += include/ssl/private_key.h - -AXTLS_PATH := $(COMPONENT_PATH)/axtls-8266 - -include/ssl/private_key.h: - $(info Generating unique certificate and key. This may take some time...) - $(Q) mkdir -p $(PROJECT_DIR)/include/ssl/ - $(Q) chmod a+x $(AXTLS_PATH)/tools/make_certs.sh - AXDIR=$(PROJECT_DIR)/include/ssl/ $(AXTLS_PATH)/tools/make_certs.sh diff --git a/Sming/Components/bearssl-esp8266/.patches/bearssl/src/config.h b/Sming/Components/bearssl-esp8266/.patches/bearssl/src/config.h new file mode 100644 index 0000000000..5302dc00ef --- /dev/null +++ b/Sming/Components/bearssl-esp8266/.patches/bearssl/src/config.h @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CONFIG_H__ +#define CONFIG_H__ + +#define phy_get_rand os_random + +#define BR_i386 0 +#define BR_amd64 0 + + +/* + * This file contains compile-time flags that can override the + * autodetection performed in relevant files. Each flag is a macro; it + * deactivates the feature if defined to 0, activates it if defined to a + * non-zero integer (normally 1). If the macro is not defined, then + * autodetection applies. + */ + +/* + * When BR_64 is enabled, 64-bit integer types are assumed to be + * efficient (i.e. the architecture has 64-bit registers and can + * do 64-bit operations as fast as 32-bit operations). + * + */ +#define BR_64 0 + +/* + * When BR_LOMUL is enabled, then multiplications of 32-bit values whose + * result are truncated to the low 32 bits are assumed to be + * substantially more efficient than 32-bit multiplications that yield + * 64-bit results. This is typically the case on low-end ARM Cortex M + * systems (M0, M0+, M1, and arguably M3 and M4 as well). + * +#define BR_LOMUL 1 + */ + +/* + * When BR_SLOW_MUL is enabled, multiplications are assumed to be + * substantially slow with regards to other integer operations, thus + * making it worth to make more operations for a given task if it allows + * using less multiplications. + * +#define BR_SLOW_MUL 1 + */ + +/* + * When BR_SLOW_MUL15 is enabled, short multplications (on 15-bit words) + * are assumed to be substantially slow with regards to other integer + * operations, thus making it worth to make more integer operations if + * it allows using less multiplications. + * + */ +#define BR_SLOW_MUL15 1 + +/* + * When BR_CT_MUL31 is enabled, multiplications of 31-bit values (used + * in the "i31" big integer implementation) use an alternate implementation + * which is slower and larger than the normal multiplication, but should + * ensure constant-time multiplications even on architectures where the + * multiplication opcode takes a variable number of cycles to complete. + * +#define BR_CT_MUL31 1 + */ + +/* + * When BR_CT_MUL15 is enabled, multiplications of 15-bit values (held + * in 32-bit words) use an alternate implementation which is slower and + * larger than the normal multiplication, but should ensure + * constant-time multiplications on most/all architectures where the + * basic multiplication is not constant-time. +#define BR_CT_MUL15 1 + */ + +/* + * When BR_NO_ARITH_SHIFT is enabled, arithmetic right shifts (with sign + * extension) are performed with a sequence of operations which is bigger + * and slower than a simple right shift on a signed value. This avoids + * relying on an implementation-defined behaviour. However, most if not + * all C compilers use sign extension for right shifts on signed values, + * so this alternate macro is disabled by default. +#define BR_NO_ARITH_SHIFT 1 + */ + +/* + * When BR_RDRAND is enabled, the SSL engine will use the RDRAND opcode + * to automatically obtain quality randomness for seeding its internal + * PRNG. Since that opcode is present only in recent x86 CPU, its + * support is dynamically tested; if the current CPU does not support + * it, then another random source will be used, such as /dev/urandom or + * CryptGenRandom(). + * +#define BR_RDRAND 1 + */ + +/* + * When BR_USE_GETENTROPY is enabled, the SSL engine will use the + * getentropy() function to obtain quality randomness for seeding its + * internal PRNG. On Linux and FreeBSD, getentropy() is implemented by + * the standard library with the system call getrandom(); on OpenBSD, + * getentropy() is the system call, and there is no getrandom() wrapper, + * hence the use of the getentropy() function for maximum portability. + * + * If the getentropy() call fails, and BR_USE_URANDOM is not explicitly + * disabled, then /dev/urandom will be used as a fallback mechanism. On + * FreeBSD and OpenBSD, this does not change much, since /dev/urandom + * will block if not enough entropy has been obtained since last boot. + * On Linux, /dev/urandom might not block, which can be troublesome in + * early boot stages, which is why getentropy() is preferred. + * + */ +#define BR_USE_GETENTROPY 0 + +/* + * When BR_USE_URANDOM is enabled, the SSL engine will use /dev/urandom + * to automatically obtain quality randomness for seeding its internal + * PRNG. + * + */ +#define BR_USE_URANDOM 0 + +/* + * When BR_USE_ESP8266_RAND is enabled, use the phy_get_rand() SDK call + * on the ESP8266 as the entropy source, 32-bits at a time. + * + */ +#define BR_USE_ESP8266_RAND 1 + +/* + * When BR_USE_WIN32_RAND is enabled, the SSL engine will use the Win32 + * (CryptoAPI) functions (CryptAcquireContext(), CryptGenRandom()...) to + * automatically obtain quality randomness for seeding its internal PRNG. + * + * Note: if both BR_USE_URANDOM and BR_USE_WIN32_RAND are defined, the + * former takes precedence. + * + */ +#define BR_USE_WIN32_RAND 0 + +/* + * When BR_USE_UNIX_TIME is enabled, the X.509 validation engine obtains + * the current time from the OS by calling time(), and assuming that the + * returned value (a 'time_t') is an integer that counts time in seconds + * since the Unix Epoch (Jan 1st, 1970, 00:00 UTC). + * + */ +#define BR_USE_UNIX_TIME 1 + +/* + * When BR_USE_WIN32_TIME is enabled, the X.509 validation engine obtains + * the current time from the OS by calling the Win32 function + * GetSystemTimeAsFileTime(). + * + * Note: if both BR_USE_UNIX_TIME and BR_USE_WIN32_TIME are defined, the + * former takes precedence. + * + */ +#define BR_USE_WIN32_TIME 0 + +/* + * When BR_ARMEL_CORTEXM_GCC is enabled, some operations are replaced with + * inline assembly which is shorter and/or faster. This should be used + * only when all of the following are true: + * - target architecture is ARM in Thumb mode + * - target endianness is little-endian + * - compiler is GCC (or GCC-compatible for inline assembly syntax) + * + * This is meant for the low-end cores (Cortex M0, M0+, M1, M3). + * Note: if BR_LOMUL is not explicitly enabled or disabled, then + * enabling BR_ARMEL_CORTEXM_GCC also enables BR_LOMUL. + * +#define BR_ARMEL_CORTEXM_GCC 1 + */ + +/* + * When BR_AES_X86NI is enabled, the AES implementation using the x86 "NI" + * instructions (dedicated AES opcodes) will be compiled. If this is not + * enabled explicitly, then that AES implementation will be compiled only + * if a compatible compiler is detected. If set explicitly to 0, the + * implementation will not be compiled at all. + * +#define BR_AES_X86NI 1 + */ + +/* + * When BR_SSE2 is enabled, SSE2 intrinsics will be used for some + * algorithm implementations that use them (e.g. chacha20_sse2). If this + * is not enabled explicitly, then support for SSE2 intrinsics will be + * automatically detected. If set explicitly to 0, then SSE2 code will + * not be compiled at all. + * +#define BR_SSE2 1 + */ + +/* + * When BR_POWER8 is enabled, the AES implementation using the POWER ISA + * 2.07 opcodes (available on POWER8 processors and later) is compiled. + * If this is not enabled explicitly, then that implementation will be + * compiled only if a compatible compiler is detected, _and_ the target + * architecture is POWER8 or later. + * +#define BR_POWER8 1 + */ + +/* + * When BR_INT128 is enabled, then code using the 'unsigned __int64' + * and 'unsigned __int128' types will be used to leverage 64x64->128 + * unsigned multiplications. This should work with GCC and compatible + * compilers on 64-bit architectures. + * +#define BR_INT128 1 + */ + +/* + * When BR_UMUL128 is enabled, then code using the '_umul128()' and + * '_addcarry_u64()' intrinsics will be used to implement 64x64->128 + * unsigned multiplications. This should work on Visual C on x64 systems. + * +#define BR_UMUL128 1 + */ + +/* + * When BR_LE_UNALIGNED is enabled, then the current architecture is + * assumed to use little-endian encoding for integers, and to tolerate + * unaligned accesses with no or minimal time penalty. + * +#define BR_LE_UNALIGNED 1 + */ + +/* + * When BR_BE_UNALIGNED is enabled, then the current architecture is + * assumed to use big-endian encoding for integers, and to tolerate + * unaligned accesses with no or minimal time penalty. + * +#define BR_BE_UNALIGNED 1 + */ + +#endif diff --git a/Sming/Components/bearssl-esp8266/README.rst b/Sming/Components/bearssl-esp8266/README.rst new file mode 100644 index 0000000000..3cc0a14d1d --- /dev/null +++ b/Sming/Components/bearssl-esp8266/README.rst @@ -0,0 +1,4 @@ +Bear SSL +======== + +SSL support using Bear SSL for ESP8266. diff --git a/Sming/Components/bearssl-esp8266/bearssl b/Sming/Components/bearssl-esp8266/bearssl new file mode 160000 index 0000000000..89454af34e --- /dev/null +++ b/Sming/Components/bearssl-esp8266/bearssl @@ -0,0 +1 @@ +Subproject commit 89454af34e3e61ddfc9837f3da5a0bc8ed44c3aa diff --git a/Sming/Components/bearssl-esp8266/bearssl.patch b/Sming/Components/bearssl-esp8266/bearssl.patch new file mode 100644 index 0000000000..6b9ed1ebec --- /dev/null +++ b/Sming/Components/bearssl-esp8266/bearssl.patch @@ -0,0 +1,27 @@ +diff --git a/src/inner.h b/src/inner.h +index 0a62d9c..e94b7cd 100644 +--- a/src/inner.h ++++ b/src/inner.h +@@ -2588,21 +2588,7 @@ br_cpuid(uint32_t mask_eax, uint32_t mask_ebx, + + #endif + +-#ifdef ESP8266 +- +- #ifdef __cplusplus +- extern "C" { +- #endif +- +- #define _debugBearSSL (0) +- extern void optimistic_yield(uint32_t); +- #ifdef __cplusplus +- } +- #endif +- +-#else +- #define optimistic_yield(ignored) +-#endif ++#define optimistic_yield(ignored) + + + /* ==================================================================== */ diff --git a/Sming/Components/bearssl-esp8266/component.mk b/Sming/Components/bearssl-esp8266/component.mk new file mode 100644 index 0000000000..d9d7554c05 --- /dev/null +++ b/Sming/Components/bearssl-esp8266/component.mk @@ -0,0 +1,24 @@ +COMPONENT_SUBMODULES := bearssl + +COMPONENT_SRCDIRS := \ + . \ + aead \ + codec \ + ec \ + hash \ + int \ + kdf \ + mac \ + rand \ + rsa \ + ssl \ + symcipher \ + x509 + +COMPONENT_SRCDIRS := $(addprefix bearssl/src/,$(COMPONENT_SRCDIRS)) +COMPONENT_INCDIRS := bearssl/inc +EXTRA_INCDIR := bearssl/src + +COMPONENT_CFLAGS := \ + -Wno-undef \ + -O2 diff --git a/Sming/Components/ssl/.cs b/Sming/Components/ssl/.cs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Sming/Components/ssl/Axtls/AxCertificate.cpp b/Sming/Components/ssl/Axtls/AxCertificate.cpp new file mode 100644 index 0000000000..4f43ddb8ef --- /dev/null +++ b/Sming/Components/ssl/Axtls/AxCertificate.cpp @@ -0,0 +1,56 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * AxCertificate.cpp + * + ****/ + +#include "AxCertificate.h" + +namespace Ssl +{ +String AxCertificate::getName(DN dn, RDN rdn) const +{ + if(ssl == nullptr || ssl->x509_ctx == nullptr) { + return nullptr; + } + + int dnType; + switch(rdn) { + case RDN::COMMON_NAME: + dnType = X509_COMMON_NAME; + break; + case RDN::ORGANIZATION_NAME: + dnType = X509_ORGANIZATION; + break; + case RDN::ORGANIZATIONAL_UNIT_NAME: + dnType = X509_ORGANIZATIONAL_UNIT; + break; + case RDN::LOCALITY_NAME: + dnType = X509_LOCATION; + break; + case RDN::COUNTRY_NAME: + dnType = X509_COUNTRY; + break; + case RDN::STATE_OR_PROVINCE_NAME: + dnType = X509_STATE; + break; + default: + return nullptr; + } + + switch(dn) { + case DN::ISSUER: + return ssl->x509_ctx->ca_cert_dn[dnType]; + + case DN::SUBJECT: + return ssl->x509_ctx->cert_dn[dnType]; + default: + return nullptr; + } +} + +} // namespace Ssl diff --git a/Sming/Components/ssl/Axtls/AxCertificate.h b/Sming/Components/ssl/Axtls/AxCertificate.h new file mode 100644 index 0000000000..adb77d04b0 --- /dev/null +++ b/Sming/Components/ssl/Axtls/AxCertificate.h @@ -0,0 +1,49 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * AxCertificate.h + * + * @author: 2019 - Slavey Karadzhov + * + ****/ + +#pragma once + +#include +#include + +namespace Ssl +{ +class AxCertificate : public Certificate +{ +public: + AxCertificate(SSL* ssl) : ssl(ssl) + { + ssl->can_free_certificates = false; + } + + ~AxCertificate() + { + ssl->can_free_certificates = true; + } + + bool matchFingerprint(const uint8_t* hash) const override + { + return (ssl_match_fingerprint(ssl, hash) == 0); + } + + bool matchPki(const uint8_t* hash) const override + { + return (ssl_match_spki_sha256(ssl, hash) == 0); + } + + String getName(DN dn, RDN rdn) const override; + +private: + SSL* ssl; +}; + +} // namespace Ssl diff --git a/Sming/Components/ssl/Axtls/AxConnection.cpp b/Sming/Components/ssl/Axtls/AxConnection.cpp new file mode 100644 index 0000000000..ecbac7ac2e --- /dev/null +++ b/Sming/Components/ssl/Axtls/AxConnection.cpp @@ -0,0 +1,100 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * AxConnection.cpp + * + * @author: 2019 - mikee47 + * + * Contains code from compatibility layer https://github.com/attachix/lwirax.git + * so we can work directly with axTLS. + * + * Compatibility for AxTLS with LWIP raw tcp mode (http://lwip.wikia.com/wiki/Raw/TCP) + * + * Created on: Jan 15, 2016 + * Author: Slavey Karadzhov + * + ****/ + +#include +#include "AxConnection.h" +#include "AxContext.h" +#include + +namespace Ssl +{ +int AxConnection::write(const uint8_t* data, size_t length) +{ + int required = ssl_calculate_write_length(ssl, length); + if(required < 0) { + return required; + } + + int available = tcp_sndbuf(tcp); + if(available < required) { + debug_i("SSL: Required: %d, Available: %d", required, available); + return SSL_NOT_OK; + } + + int written = ssl_write(ssl, data, length); + debug_d("SSL: Write len: %d, Written: %d", length, written); + if(written < 0) { + debug_e("SSL: Write Error: %d", written); + } + + return written; +} + +int AxConnection::read(InputBuffer& input, uint8_t*& output) +{ + bool connected = isHandshakeDone(); + this->input = &input; + int readBytes = ssl_read(ssl, &output); + this->input = nullptr; + if(!connected && isHandshakeDone()) { + auto& session = context.getSession(); + if(session.getConnection() != nullptr) { + if(!session.validateCertificate()) { + session.handshakeComplete(false); + return SSL_ERROR_BAD_CERTIFICATE; + } + session.handshakeComplete(true); + } + } + + if(readBytes == SSL_CLOSE_NOTIFY) { + readBytes = 0; + } + + return readBytes; +} + +/* + * Lower Level LWIP RAW functions + */ + +/* + * The LWIP tcp raw version of the SOCKET_WRITE(A, B, C) + */ +extern "C" int ax_port_write(int clientfd, uint8_t* buf, uint16_t bytes_needed) +{ + assert(clientfd != 0); + + auto connection = reinterpret_cast(clientfd); + return connection->writeTcpData(buf, bytes_needed); +} + +/* + * The LWIP tcp raw version of the SOCKET_READ(A, B, C) + */ +extern "C" int ax_port_read(int clientfd, uint8_t* buf, int bytes_needed) +{ + assert(clientfd != 0); + + auto connection = reinterpret_cast(clientfd); + return connection->readTcpData(buf, bytes_needed); +} + +} // namespace Ssl diff --git a/Sming/Components/ssl/Axtls/AxConnection.h b/Sming/Components/ssl/Axtls/AxConnection.h new file mode 100644 index 0000000000..8ce0362ff5 --- /dev/null +++ b/Sming/Components/ssl/Axtls/AxConnection.h @@ -0,0 +1,100 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * AxConnection.h + * + * @author: 2019 - Slavey Karadzhov + * + ****/ + +#pragma once + +#include +#include +#include "AxCertificate.h" +#include "AxError.h" + +namespace Ssl +{ +class AxConnection : public Connection +{ +public: + using Connection::Connection; + + ~AxConnection() + { + delete certificate; + // Typically sends out closing message + ssl_free(ssl); + } + + void init(SSL* ssl) + { + this->ssl = ssl; + } + + bool isHandshakeDone() const override + { + return (ssl_handshake_status(ssl) == SSL_OK); + } + + int write(const uint8_t* data, size_t length) override; + + CipherSuite getCipherSuite() const override + { + return CipherSuite(ssl_get_cipher_id(ssl)); + } + + SessionId getSessionId() const override + { + SessionId id; + if(isHandshakeDone()) { + id.assign(ssl->session_id, ssl->sess_id_size); + } + + return id; + } + + const Certificate* getCertificate() const override + { + if(certificate == nullptr && ssl->x509_ctx != nullptr) { + certificate = new AxCertificate(ssl); + } + + return certificate; + } + + void freeCertificate() override + { + delete certificate; + certificate = nullptr; + } + + int read(InputBuffer& input, uint8_t*& output) override; + + size_t readTcpData(uint8_t* buf, size_t count) + { + assert(input != nullptr); + return input->read(buf, count); + } + + String getErrorString(int error) const override + { + return Ssl::getErrorString(error); + } + + Alert getAlert(int error) const override + { + return Ssl::getAlert(error); + } + +private: + SSL* ssl; + mutable AxCertificate* certificate = nullptr; + InputBuffer* input = nullptr; +}; + +} // namespace Ssl diff --git a/Sming/Components/ssl/Axtls/AxContext.cpp b/Sming/Components/ssl/Axtls/AxContext.cpp new file mode 100644 index 0000000000..c3492f7919 --- /dev/null +++ b/Sming/Components/ssl/Axtls/AxContext.cpp @@ -0,0 +1,119 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * AxContext.cpp + * + * @author: 2019 - Slavey Karadzhov + * + ****/ + +#include "AxContext.h" +#include "AxConnection.h" +#include + +namespace Ssl +{ +AxContext::~AxContext() +{ + ssl_ctx_free(context); +} + +bool AxContext::init() +{ + assert(context == nullptr); + + uint32_t options = SSL_CONNECT_IN_PARTS; + if(session.options.clientAuthentication) { + options |= SSL_CLIENT_AUTHENTICATION; + } + if(session.options.verifyLater) { + options |= SSL_SERVER_VERIFY_LATER; + } + +#ifdef SSL_DEBUG + options |= SSL_DISPLAY_STATES | SSL_DISPLAY_CERTS; +#if DEBUG_VERBOSE_LEVEL == DBG + options |= SSL_DISPLAY_BYTES; +#endif + debug_d("SSL: Show debug data ..."); +#endif + + context = ssl_ctx_new(options, session.cacheSize); + if(context == nullptr) { + debug_e("SSL: Unable to allocate context"); + return false; + } + + auto keyCert = session.keyCert; + + if(!keyCert.isValid()) { + debug_w("Ignoring invalid keyCert"); + return true; + } + + auto load = [&](int objtype, const uint8_t* data, unsigned length, const char* password) -> bool { + lastError = ssl_obj_memory_load(context, objtype, data, length, password); + return lastError == SSL_OK; + }; + + if(!load(SSL_OBJ_RSA_KEY, keyCert.getKey(), keyCert.getKeyLength(), keyCert.getKeyPassword())) { + debug_e("SSL: Error loading key"); + return false; + } + + if(!load(SSL_OBJ_X509_CERT, keyCert.getCertificate(), keyCert.getCertificateLength(), nullptr)) { + debug_e("SSL: Error loading certificate"); + return false; + } + + return true; +} + +Connection* AxContext::createClient(tcp_pcb* tcp) +{ + assert(context != nullptr); + + auto ssl_ext = ssl_ext_new(); + ssl_ext_set_host_name(ssl_ext, session.hostName.c_str()); + ssl_ext_set_max_fragment_size(ssl_ext, unsigned(session.maxBufferSize)); + + auto id = session.getSessionId(); + auto connection = new AxConnection(*this, tcp); + auto client = + ssl_client_new(context, int(connection), id ? id->getValue() : nullptr, id ? id->getLength() : 0, ssl_ext); + if(client == nullptr) { + ssl_ext_free(ssl_ext); + delete connection; + return nullptr; + } + + connection->init(client); + return connection; +} + +Connection* AxContext::createServer(tcp_pcb* tcp) +{ + assert(context != nullptr); + + auto connection = new AxConnection(*this, tcp); + auto server = ssl_server_new(context, int(connection)); + if(server == nullptr) { + delete connection; + return nullptr; + } + + connection->init(server); + return connection; +} + +// Required by axtls-8266 +extern "C" int ax_get_file(const char* filename, uint8_t** buf) +{ + *buf = 0; + return 0; +} + +} // namespace Ssl diff --git a/Sming/Components/ssl/Axtls/AxContext.h b/Sming/Components/ssl/Axtls/AxContext.h new file mode 100644 index 0000000000..e454e6cb11 --- /dev/null +++ b/Sming/Components/ssl/Axtls/AxContext.h @@ -0,0 +1,35 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * AxContext.h + * + * @author: 2019 - Slavey Karadzhov + * + ****/ + +#pragma once + +#include +#include + +namespace Ssl +{ +class AxContext : public Context +{ +public: + using Context::Context; + ~AxContext(); + + bool init() override; + Connection* createClient(tcp_pcb* pcb) override; + Connection* createServer(tcp_pcb* pcb) override; + +private: + SSL_CTX* context = nullptr; + int lastError = SSL_OK; +}; + +} // namespace Ssl diff --git a/Sming/Components/ssl/Axtls/AxError.cpp b/Sming/Components/ssl/Axtls/AxError.cpp new file mode 100644 index 0000000000..5c5a5d2aa2 --- /dev/null +++ b/Sming/Components/ssl/Axtls/AxError.cpp @@ -0,0 +1,98 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * AxError.cpp + * + ****/ + +#include "AxError.h" +#include +#include + +#define AX_ERROR_MAP(XX) \ + XX(OK) \ + XX(NOT_OK) \ + XX(ERROR_DEAD) \ + XX(CLOSE_NOTIFY) \ + XX(ERROR_CONN_LOST) \ + XX(ERROR_RECORD_OVERFLOW) \ + XX(ERROR_SOCK_SETUP_FAILURE) \ + XX(ERROR_INVALID_HANDSHAKE) \ + XX(ERROR_INVALID_PROT_MSG) \ + XX(ERROR_INVALID_HMAC) \ + XX(ERROR_INVALID_VERSION) \ + XX(ERROR_UNSUPPORTED_EXTENSION) \ + XX(ERROR_INVALID_SESSION) \ + XX(ERROR_NO_CIPHER) \ + XX(ERROR_INVALID_CERT_HASH_ALG) \ + XX(ERROR_BAD_CERTIFICATE) \ + XX(ERROR_INVALID_KEY) \ + XX(ERROR_FINISHED_INVALID) \ + XX(ERROR_NO_CERT_DEFINED) \ + XX(ERROR_NO_CLIENT_RENOG) \ + XX(ERROR_NOT_SUPPORTED) + +#define AX_X509_ERROR_MAP(XX) \ + XX(X509_NOT_OK) \ + XX(X509_VFY_ERROR_NO_TRUSTED_CERT) \ + XX(X509_VFY_ERROR_BAD_SIGNATURE) \ + XX(X509_VFY_ERROR_NOT_YET_VALID) \ + XX(X509_VFY_ERROR_EXPIRED) \ + XX(X509_VFY_ERROR_SELF_SIGNED) \ + XX(X509_VFY_ERROR_INVALID_CHAIN) \ + XX(X509_VFY_ERROR_UNSUPPORTED_DIGEST) \ + XX(X509_INVALID_PRIV_KEY) \ + XX(X509_MAX_CERTS) \ + XX(X509_VFY_ERROR_BASIC_CONSTRAINT) + +namespace Ssl +{ +#define XX(tag) DEFINE_FSTR_LOCAL(errStr_##tag, #tag) +AX_ERROR_MAP(XX) +#undef XX + +#define XX(tag) {SSL_##tag, &errStr_##tag}, +DEFINE_FSTR_MAP_LOCAL(errorMap, int, FSTR::String, AX_ERROR_MAP(XX)); +#undef XX + +#define XX(tag) DEFINE_FSTR_LOCAL(x509ErrStr_##tag, #tag) +AX_X509_ERROR_MAP(XX) +#undef XX + +#define XX(tag) {tag, &x509ErrStr_##tag}, +DEFINE_FSTR_MAP_LOCAL(x509ErrorMap, int, FSTR::String, AX_X509_ERROR_MAP(XX)); +#undef XX + +String getErrorString(int error) +{ + String s; + + // X509 error ? + if(error < SSL_X509_OFFSET) { + s = String(x509ErrorMap[error - SSL_X509_OFFSET]); + } else if(error < SSL_CLOSE_NOTIFY && error > SSL_ERROR_CONN_LOST) { + auto alert = Alert(-error); + s = getAlertString(alert); + } else { + s = String(errorMap[error]); + } + return s ?: F("Unknown_") + String(error); +} + +Alert getAlert(int error) +{ + if(error == SSL_CLOSE_NOTIFY) { + return Alert::CLOSE_NOTIFY; + } + + if(error > SSL_ERROR_CONN_LOST && error < SSL_CLOSE_NOTIFY) { + return Alert(-error); + } + + return Alert::Invalid; +} + +} // namespace Ssl diff --git a/Sming/Components/ssl/Axtls/AxError.h b/Sming/Components/ssl/Axtls/AxError.h new file mode 100644 index 0000000000..93ae60967a --- /dev/null +++ b/Sming/Components/ssl/Axtls/AxError.h @@ -0,0 +1,20 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * AxError.h + * + ****/ + +#pragma once + +#include +#include + +namespace Ssl +{ +String getErrorString(int error); +Alert getAlert(int error); +} // namespace Ssl diff --git a/Sming/Components/ssl/Axtls/AxFactory.cpp b/Sming/Components/ssl/Axtls/AxFactory.cpp new file mode 100644 index 0000000000..9f758622f3 --- /dev/null +++ b/Sming/Components/ssl/Axtls/AxFactory.cpp @@ -0,0 +1,30 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * AxFactory.cpp + * + * @author: 2019 - Slavey Karadzhov + * + ****/ + +#include +#include "AxContext.h" + +namespace Ssl +{ +class AxFactory : public Factory +{ +public: + Context* createContext(Session& session) override + { + return new AxContext(session); + } +}; + +static AxFactory axFactory; +Factory* factory = &axFactory; + +} // namespace Ssl diff --git a/Sming/Components/ssl/BearSsl/Asn1Parser.h b/Sming/Components/ssl/BearSsl/Asn1Parser.h new file mode 100644 index 0000000000..14f6f090f4 --- /dev/null +++ b/Sming/Components/ssl/BearSsl/Asn1Parser.h @@ -0,0 +1,144 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Asn1Parser.h + * + * A very simple helper class consisting of code based on asn1_name() in axTLS. + * + ****/ + +#pragma once + +#include + +enum ASN1 { + ASN1_BOOLEAN = 0x01, + ASN1_INTEGER = 0x02, + ASN1_BIT_STRING = 0x03, + ASN1_OCTET_STRING = 0x04, + ASN1_NULL = 0x05, + ASN1_OID = 0x06, + ASN1_PRINTABLE_STR2 = 0x0c, + ASN1_PRINTABLE_STR = 0x13, + ASN1_TELETEX_STR = 0x14, + ASN1_IA5_STR = 0x16, + ASN1_UTC_TIME = 0x17, + ASN1_GENERALIZED_TIME = 0x18, + ASN1_UNICODE_STR = 0x1e, + ASN1_SEQUENCE = 0x30, + ASN1_SET = 0x31, + ASN1_IMPLICIT_TAG = 0x80, + ASN1_CONTEXT_DNSNAME = 0x82, + ASN1_EXPLICIT_TAG = 0xa0, + ASN1_V3_DATA = 0xa3, +}; + +class Asn1Parser +{ +public: + Asn1Parser(const uint8_t* data, unsigned length) : buf(data), length(length) + { + } + + unsigned getOffset() const + { + return offset; + } + + void setOffset(unsigned offset) + { + this->offset = offset; + } + + unsigned getLength() + { + if((buf[offset] & 0x80) == 0) { + return buf[offset++]; + } + + unsigned lengthBytes = buf[offset++] & 0x7f; + if(lengthBytes > 4) { + return 0; + } + + uint32_t len = 0; + for(unsigned i = 0; i < lengthBytes; i++) { + len <<= 8; + len += buf[offset++]; + } + + return len; + } + + // Skip the ASN1.1 object type and its length. Get ready to read the object's data. + unsigned getNextObject(uint8_t objType) + { + if(offset >= length || buf[offset] != objType) { + return 0; + } + + ++offset; + return getLength(); + } + + // Get the components of a distinguished name + uint8_t getObjectId() + { + auto len = getNextObject(ASN1_OID); + if(len == 0) { + return 0; + } + + uint8_t dnType = 0; + + /* expect a sequence of 2.5.4.[x] where x is a one of distinguished name + components we are interested in. */ + if(len == 3 && buf[offset] == 0x55 && buf[offset + 1] == 0x04) { + dnType = buf[offset + 2]; + } + + // Skip it + offset += len; + return dnType; + } + + // Extract a readable string + String getString() + { + auto asn1_type = buf[offset]; + + ++offset; + auto len = getLength(); + + String s; + switch(asn1_type) { + case ASN1_UNICODE_STR: + len /= 2; + s.setLength(len); + for(unsigned i = 0; i < len; ++i) { + // Unicode MSB first + s[i] = buf[offset + 1]; + offset += 2; + } + break; + + case ASN1_PRINTABLE_STR: + case ASN1_PRINTABLE_STR2: + case ASN1_TELETEX_STR: + case ASN1_IA5_STR: + s.setString(reinterpret_cast(&buf[offset]), len); + offset += len; + break; + } + + return s; + } + +private: + const uint8_t* buf; + unsigned length; + unsigned offset = 0; +}; diff --git a/Sming/Components/ssl/BearSsl/BrCertificate.cpp b/Sming/Components/ssl/BearSsl/BrCertificate.cpp new file mode 100644 index 0000000000..e6e1cfb3b8 --- /dev/null +++ b/Sming/Components/ssl/BearSsl/BrCertificate.cpp @@ -0,0 +1,37 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * BrCertificate.cpp + * + ****/ + +#include "BrCertificate.h" +#include + +namespace Ssl +{ +String BrCertificate::getName(DN dn, RDN rdn) const +{ +#define XX(tag, a, b, c, d) d, + DEFINE_FSTR_ARRAY_LOCAL(rdnTypes, uint8_t, SSL_X509_RDN_OID_MAP(XX)); +#undef XX + + uint8_t type = rdnTypes[unsigned(rdn)]; + if(type == 0) { + return nullptr; + } + + switch(dn) { + case DN::ISSUER: + return context->getIssuer().getRDN(type); + case DN::SUBJECT: + return context->getSubject().getRDN(type); + default: + return nullptr; + } +} + +} // namespace Ssl diff --git a/Sming/Components/ssl/BearSsl/BrCertificate.h b/Sming/Components/ssl/BearSsl/BrCertificate.h new file mode 100644 index 0000000000..b17fc17fc0 --- /dev/null +++ b/Sming/Components/ssl/BearSsl/BrCertificate.h @@ -0,0 +1,42 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * BrCertificate.h + * + ****/ + +#pragma once + +#include +#include +#include "X509Context.h" + +namespace Ssl +{ +class BrCertificate : public Ssl::Certificate +{ +public: + BrCertificate(X509Context* context) : context(context) + { + } + + bool matchFingerprint(const uint8_t* hash) const override + { + return context->matchFingerprint(hash); + } + + bool matchPki(const uint8_t* hash) const override + { + return context->matchPki(hash); + } + + String getName(DN dn, RDN rdn) const override; + +private: + X509Context* context; +}; + +} // namespace Ssl diff --git a/Sming/Components/ssl/BearSsl/BrClientConnection.cpp b/Sming/Components/ssl/BearSsl/BrClientConnection.cpp new file mode 100644 index 0000000000..7facdbddee --- /dev/null +++ b/Sming/Components/ssl/BearSsl/BrClientConnection.cpp @@ -0,0 +1,46 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * BrClientConnection.cpp + * + * @author: 2019 - mikee47 + * + ****/ + +/* + */ +#include +#include "BrClientConnection.h" +#include + +namespace Ssl +{ +int BrClientConnection::init() +{ + br_ssl_client_zero(&clientContext); + + // Use Mono-directional buffer size according to requested max. fragment size + size_t bufSize = maxBufferSizeToBytes(context.getSession().maxBufferSize); + if(bufSize == 0) { + bufSize = 4096; + } + int err = BrConnection::init(bufSize, false); + if(err < 0) { + return err; + } + + br_ssl_client_set_default_rsapub(&clientContext); + + // X509 verification + x509Context = new X509Context([this]() { return context.getSession().validateCertificate(); }); + br_ssl_engine_set_x509(getEngine(), *x509Context); + + br_ssl_client_reset(&clientContext, context.getSession().hostName.c_str(), 0); + + return startHandshake(); +} + +} // namespace Ssl diff --git a/Sming/Components/ssl/BearSsl/BrClientConnection.h b/Sming/Components/ssl/BearSsl/BrClientConnection.h new file mode 100644 index 0000000000..bc9f169b72 --- /dev/null +++ b/Sming/Components/ssl/BearSsl/BrClientConnection.h @@ -0,0 +1,61 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * BrConnection.h + * + * @author: 2019 - mikee47 + * + ****/ + +#pragma once + +#include "BrConnection.h" +#include "X509Context.h" + +namespace Ssl +{ +class BrClientConnection : public BrConnection +{ +public: + using BrConnection::BrConnection; + + ~BrClientConnection() + { + delete certificate; + delete x509Context; + } + + int init(); + + const Certificate* getCertificate() const override + { + if(certificate == nullptr) { + certificate = new BrCertificate(x509Context); + } + + return certificate; + } + + void freeCertificate() override + { + delete certificate; + certificate = nullptr; + } + + /* BrConnection */ + + br_ssl_engine_context* getEngine() override + { + return &clientContext.eng; + } + +private: + br_ssl_client_context clientContext; + X509Context* x509Context = nullptr; + mutable BrCertificate* certificate = nullptr; +}; + +} // namespace Ssl diff --git a/Sming/Components/ssl/BearSsl/BrConnection.cpp b/Sming/Components/ssl/BearSsl/BrConnection.cpp new file mode 100644 index 0000000000..d4d4ef8cfb --- /dev/null +++ b/Sming/Components/ssl/BearSsl/BrConnection.cpp @@ -0,0 +1,248 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * BrConnection.cpp + * + * @author: 2019 - mikee47 + * + ****/ + +/* + */ +#include +#include "BrConnection.h" +#include +#include + +// Defined in ssl_engine.c +#define MAX_OUT_OVERHEAD 85 +#define MAX_IN_OVERHEAD 325 + +namespace Ssl +{ +int BrConnection::init(size_t bufferSize, bool bidi) +{ + DEFINE_FSTR_ARRAY_LOCAL(FS_suitesBasic, CipherSuite, CipherSuite::RSA_WITH_AES_128_CBC_SHA256, + CipherSuite::RSA_WITH_AES_256_CBC_SHA256, CipherSuite::RSA_WITH_AES_128_CBC_SHA, + CipherSuite::RSA_WITH_AES_256_CBC_SHA); + + DEFINE_FSTR_ARRAY_LOCAL( + FS_suitesFull, CipherSuite, CipherSuite::ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite::ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, CipherSuite::ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite::ECDHE_RSA_WITH_AES_128_GCM_SHA256, CipherSuite::ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + CipherSuite::ECDHE_RSA_WITH_AES_256_GCM_SHA384, CipherSuite::ECDHE_ECDSA_WITH_AES_128_CCM, + CipherSuite::ECDHE_ECDSA_WITH_AES_256_CCM, CipherSuite::ECDHE_ECDSA_WITH_AES_128_CCM_8, + CipherSuite::ECDHE_ECDSA_WITH_AES_256_CCM_8, CipherSuite::ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + CipherSuite::ECDHE_RSA_WITH_AES_128_CBC_SHA256, CipherSuite::ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + CipherSuite::ECDHE_RSA_WITH_AES_256_CBC_SHA384, CipherSuite::ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + CipherSuite::ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite::ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + CipherSuite::ECDHE_RSA_WITH_AES_256_CBC_SHA, CipherSuite::ECDH_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite::ECDH_RSA_WITH_AES_128_GCM_SHA256, CipherSuite::ECDH_ECDSA_WITH_AES_256_GCM_SHA384, + CipherSuite::ECDH_RSA_WITH_AES_256_GCM_SHA384, CipherSuite::ECDH_ECDSA_WITH_AES_128_CBC_SHA256, + CipherSuite::ECDH_RSA_WITH_AES_128_CBC_SHA256, CipherSuite::ECDH_ECDSA_WITH_AES_256_CBC_SHA384, + CipherSuite::ECDH_RSA_WITH_AES_256_CBC_SHA384, CipherSuite::ECDH_ECDSA_WITH_AES_128_CBC_SHA, + CipherSuite::ECDH_RSA_WITH_AES_128_CBC_SHA, CipherSuite::ECDH_ECDSA_WITH_AES_256_CBC_SHA, + CipherSuite::ECDH_RSA_WITH_AES_256_CBC_SHA, CipherSuite::RSA_WITH_AES_128_GCM_SHA256, + CipherSuite::RSA_WITH_AES_256_GCM_SHA384, CipherSuite::RSA_WITH_AES_128_CCM, CipherSuite::RSA_WITH_AES_256_CCM, + CipherSuite::RSA_WITH_AES_128_CCM_8, CipherSuite::RSA_WITH_AES_256_CCM_8, + CipherSuite::RSA_WITH_AES_128_CBC_SHA256, CipherSuite::RSA_WITH_AES_256_CBC_SHA256, + CipherSuite::RSA_WITH_AES_128_CBC_SHA, CipherSuite::RSA_WITH_AES_256_CBC_SHA, + CipherSuite::ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, CipherSuite::ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + CipherSuite::ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, CipherSuite::ECDH_RSA_WITH_3DES_EDE_CBC_SHA, + CipherSuite::RSA_WITH_3DES_EDE_CBC_SHA); + + auto& FS_suites = FS_suitesFull; + + auto engine = getEngine(); + br_ssl_engine_set_versions(engine, BR_TLS10, BR_TLS12); + + LOAD_FSTR_ARRAY(suites, FS_suites); + br_ssl_engine_set_suites(engine, (uint16_t*)suites, FS_suites.length()); + br_ssl_engine_set_default_rsavrfy(engine); + br_ssl_engine_set_default_ecdsa(engine); + + // Set supported hash functions for the SSL engine +#define INSTALL_HASH(hash) br_ssl_engine_set_hash(engine, br_##hash##_ID, &br_##hash##_vtable); + INSTALL_HASH(md5) + INSTALL_HASH(sha1) + INSTALL_HASH(sha224) + INSTALL_HASH(sha256) + INSTALL_HASH(sha384) + INSTALL_HASH(sha512) +#undef INSTALL_HASH + + // Set the PRF implementations + br_ssl_engine_set_prf10(engine, &br_tls10_prf); + br_ssl_engine_set_prf_sha256(engine, &br_tls12_sha256_prf); + br_ssl_engine_set_prf_sha384(engine, &br_tls12_sha384_prf); + + // Symmetric encryption + br_ssl_engine_set_default_aes_cbc(engine); + br_ssl_engine_set_default_aes_ccm(engine); + br_ssl_engine_set_default_aes_gcm(engine); + br_ssl_engine_set_default_des_cbc(engine); + br_ssl_engine_set_default_chapol(engine); + + bufferSize += MAX_IN_OVERHEAD; + if(bidi) { + bufferSize += MAX_OUT_OVERHEAD; + } + debug_i("Using buffer size of %u bytes", bufferSize); + delete[] buffer; + buffer = new uint8_t[bufferSize]; + if(buffer == nullptr) { + debug_e("Buffer allocation failed"); + return -BR_ERR_BAD_PARAM; + } + br_ssl_engine_set_buffer(engine, buffer, bufferSize, bidi); + + return BR_ERR_OK; +} + +int BrConnection::read(InputBuffer& input, uint8_t*& output) +{ + int state = runUntil(input, BR_SSL_RECVAPP); + if(state <= 0) { + return state; + } + + if((state & BR_SSL_RECVAPP) == 0) { + return 0; + } + + auto engine = getEngine(); + + size_t len = 0; + output = br_ssl_engine_recvapp_buf(engine, &len); + debug_hex(DBG, "READ", output, len); + br_ssl_engine_recvapp_ack(engine, len); + return len; +} + +int BrConnection::write(const uint8_t* data, size_t length) +{ + InputBuffer input(nullptr); + int state = runUntil(input, BR_SSL_SENDAPP); + if(state < 0) { + return state; + } + + if((state & BR_SSL_SENDAPP) == 0) { + return -BR_ERR_BAD_STATE; + } + + auto engine = getEngine(); + + size_t available; + auto buf = br_ssl_engine_sendapp_buf(engine, &available); + if(available == 0) { + debug_w("SSL: Send buffer full"); + return 0; + } + + if(available < length) { + debug_i("SSL: Required: %d, Available: %u", length, available); + length = available; + } + + memcpy(buf, data, length); + br_ssl_engine_sendapp_ack(engine, length); + br_ssl_engine_flush(engine, 0); + + /* + * Our data has been accepted so just let the SSL engine run so it can try to + * send some data. + * This can fail if the engine expects some receive data, so don't respond to + * the return value as this will get resolved on the next read operation. + */ + runUntil(input, BR_SSL_SENDAPP | BR_SSL_RECVAPP); + return length; +} + +int BrConnection::runUntil(InputBuffer& input, unsigned target) +{ + auto engine = getEngine(); + + for(;;) { + unsigned state = br_ssl_engine_current_state(engine); + + if(state & BR_SSL_CLOSED) { + int err = br_ssl_engine_last_error(engine); + debug_w("SSL CLOSED, last error = %d (%s), heap free = %u", err, getErrorString(err).c_str(), + system_get_free_heap_size()); + return -err; + } + + if(!handshakeDone && (state & BR_SSL_SENDAPP)) { + handshakeDone = true; + context.getSession().handshakeComplete(true); + debug_i("Negotiated MFLN: %u", br_ssl_engine_get_mfln_negotiated(engine)); + continue; + } + + /* + * If there is some record data to send, do it. This takes + * precedence over everything else. + */ + if(state & BR_SSL_SENDREC) { + size_t len; + auto buf = br_ssl_engine_sendrec_buf(engine, &len); + int wlen = writeTcpData(buf, len); + if(wlen == 0) { + return 0; + } + if(wlen < 0) { + debug_w("SSL SHUTDOWN"); + /* + * If we received a close_notify and we + * still send something, then we have our + * own response close_notify to send, and + * the peer is allowed by RFC 5246 not to + * wait for it. + */ + if(!engine->shutdown_recv) { + // br_ssl_engine_fail(engine, BR_ERR_IO); + } + return BR_ERR_IO; + } + + br_ssl_engine_sendrec_ack(engine, wlen); + continue; + } + + /* + * If we reached our target, then we are finished. + */ + if(state & target) { + return state; + } + + // Conflict: Application data hasn't been read + if(state & BR_SSL_RECVAPP) { + debug_e("SSL: Protocol Error"); + return BR_ERR_BAD_STATE; + } + + if(state & BR_SSL_RECVREC) { + size_t avail = 0; + auto buf = br_ssl_engine_recvrec_buf(engine, &avail); + auto len = input.read(buf, avail); + if(len == 0) { + return state; + } + + debug_hex(DBG, "READ", buf, len); + br_ssl_engine_recvrec_ack(engine, len); + + continue; + } + + // Make room for new incoming records + br_ssl_engine_flush(engine, 0); + } +} + +} // namespace Ssl diff --git a/Sming/Components/ssl/BearSsl/BrConnection.h b/Sming/Components/ssl/BearSsl/BrConnection.h new file mode 100644 index 0000000000..88a547cda5 --- /dev/null +++ b/Sming/Components/ssl/BearSsl/BrConnection.h @@ -0,0 +1,99 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * BrConnection.h + * + * @author: 2019 - mikee47 + * + ****/ + +#pragma once + +#include +#include "BrError.h" +#include "BrCertificate.h" +#include + +namespace Ssl +{ +class BrConnection : public Connection +{ +public: + using Connection::Connection; + + ~BrConnection() + { + delete[] buffer; + } + + int read(InputBuffer& input, uint8_t*& output) override; + + int write(const uint8_t* data, size_t length) override; + + CipherSuite getCipherSuite() const override + { + if(handshakeDone) { + return CipherSuite(getEngine()->session.cipher_suite); + } else { + return CipherSuite::NULL_WITH_NULL_NULL; + } + } + + SessionId getSessionId() const override + { + SessionId id; + if(handshakeDone) { + auto& param = getEngine()->session; + id.assign(param.session_id, param.session_id_len); + } + + return id; + } + + bool isHandshakeDone() const override + { + return handshakeDone; + } + + String getErrorString(int error) const override + { + return Ssl::getErrorString(error); + } + + Alert getAlert(int error) const override + { + return Ssl::getAlert(error); + } + +protected: + /** + * Perform initialisation common to both client and server connections + * @param bufferSize Buffer to allocate excluding overheads + * @param bidi Whether to use bi-directional buffering (true) or mono + */ + int init(size_t bufferSize, bool bidi); + + int runUntil(InputBuffer& input, unsigned target); + + int startHandshake() + { + InputBuffer input(nullptr); + return runUntil(input, BR_SSL_SENDAPP | BR_SSL_RECVAPP); + } + + virtual br_ssl_engine_context* getEngine() = 0; + + br_ssl_engine_context* getEngine() const + { + return const_cast(this)->getEngine(); + } + +private: + uint8_t* buffer = nullptr; + bool handshakeDone = false; +}; + +} // namespace Ssl diff --git a/Sming/Components/ssl/BearSsl/BrContext.cpp b/Sming/Components/ssl/BearSsl/BrContext.cpp new file mode 100644 index 0000000000..a51ad21869 --- /dev/null +++ b/Sming/Components/ssl/BearSsl/BrContext.cpp @@ -0,0 +1,48 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * BrContext.cpp + * + * @author: 2019 - mikee47 + * + ****/ + +#include "BrContext.h" +#include "BrClientConnection.h" +#include "BrServerConnection.h" +#include "BrError.h" + +namespace Ssl +{ +Connection* BrContext::createClient(tcp_pcb* tcp) +{ + auto connection = new BrClientConnection(*this, tcp); + if(connection != nullptr) { + int res = connection->init(); + if(res < 0) { + debug_e("Connection init failed: %s", connection->getErrorString(res).c_str()); + delete connection; + connection = nullptr; + } + } + return connection; +} + +Connection* BrContext::createServer(tcp_pcb* tcp) +{ + auto connection = new BrServerConnection(*this, tcp); + if(connection != nullptr) { + int res = connection->init(); + if(res < 0) { + debug_e("Connection init failed: %s", connection->getErrorString(res).c_str()); + delete connection; + connection = nullptr; + } + } + return connection; +} + +} // namespace Ssl diff --git a/Sming/Components/ssl/BearSsl/BrContext.h b/Sming/Components/ssl/BearSsl/BrContext.h new file mode 100644 index 0000000000..b468af1288 --- /dev/null +++ b/Sming/Components/ssl/BearSsl/BrContext.h @@ -0,0 +1,38 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * BrContext.h + * + * @author: 2019 - mikee47 + * + ****/ + +#pragma once + +#include + +namespace Ssl +{ +/** + * @brief Bear SSL uses separate contexts for clients and servers which are managed + * by the Connection, not this class + * + */ +class BrContext : public Context +{ +public: + using Context::Context; + + bool init() override + { + return true; + } + + Connection* createClient(tcp_pcb* tcp) override; + Connection* createServer(tcp_pcb* tcp) override; +}; + +} // namespace Ssl diff --git a/Sming/Components/ssl/BearSsl/BrError.cpp b/Sming/Components/ssl/BearSsl/BrError.cpp new file mode 100644 index 0000000000..d26ba8bb90 --- /dev/null +++ b/Sming/Components/ssl/BearSsl/BrError.cpp @@ -0,0 +1,122 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * BrError.cpp + * + ****/ + +#include +#include +#include + +#define BR_ERROR_MAP(XX) \ + XX(BAD_PARAM, "Caller-provided parameter is incorrect.") \ + XX(BAD_STATE, "Operation requested by the caller cannot be applied with the current context state (e.g. " \ + "reading data while outgoing data is waiting to be sent).") \ + XX(UNSUPPORTED_VERSION, "Incoming protocol or record version is unsupported.") \ + XX(BAD_VERSION, "Incoming record version does not match the expected version.") \ + XX(BAD_LENGTH, "Incoming record length is invalid.") \ + XX(TOO_LARGE, \ + "Incoming record is too large to be processed, or buffer is too small for the handshake message to send.") \ + XX(BAD_MAC, "Decryption found an invalid padding, or the record MAC is not correct.") \ + XX(NO_RANDOM, "No initial entropy was provided, and none can be obtained from the OS.") \ + XX(UNKNOWN_TYPE, "Incoming record type is unknown.") \ + XX(UNEXPECTED, "Incoming record or message has wrong type with regards to the current engine state.") \ + XX(BAD_CCS, "ChangeCipherSpec message from the peer has invalid contents.") \ + XX(BAD_ALERT, "Alert message from the peer has invalid contents (odd length).") \ + XX(BAD_HANDSHAKE, "Incoming handshake message decoding failed.") \ + XX(OVERSIZED_ID, "ServerHello contains a session ID which is larger than 32 bytes.") \ + XX(BAD_CIPHER_SUITE, "Server wants to use a cipher suite that we did not claim to support. This is also " \ + "reported if we tried to advertise a cipher suite that we do not support.") \ + XX(BAD_COMPRESSION, "Server wants to use a compression that we did not claim to support.") \ + XX(BAD_FRAGLEN, "Server's max fragment length does not match client's.") \ + XX(BAD_SECRENEG, "Secure renegotiation failed.") \ + XX(EXTRA_EXTENSION, "Server sent an extension type that we did not announce, or used the same extension type " \ + "several times in a single ServerHello.") \ + XX(BAD_SNI, "Invalid Server Name Indication contents (when used by the server, this extension shall be empty).") \ + XX(BAD_HELLO_DONE, "Invalid ServerHelloDone from the server (length is not 0).") \ + XX(LIMIT_EXCEEDED, "Internal limit exceeded (e.g. server's public key is too large).") \ + XX(BAD_FINISHED, "Finished message from peer does not match the expected value.") \ + XX(RESUME_MISMATCH, "Session resumption attempt with distinct version or cipher suite.") \ + XX(INVALID_ALGORITHM, "Unsupported or invalid algorithm (ECDHE curve, signature algorithm, hash function).") \ + XX(BAD_SIGNATURE, "Invalid signature in ServerKeyExchange or CertificateVerify message.") \ + XX(WRONG_KEY_USAGE, \ + "Peer's public key does not have the proper type or is not allowed for the requested operation.") \ + XX(NO_CLIENT_AUTH, \ + "Client did not send a certificate upon request, or the client certificate could not be validated.") \ + XX(IO, "I/O error or premature close on transport stream.") \ + XX(X509_INVALID_VALUE, "Invalid value in an ASN.1 structure.") \ + XX(X509_TRUNCATED, "Truncated certificate or other ASN.1 object.") \ + XX(X509_EMPTY_CHAIN, "Empty certificate chain XX( \no certificate at all).") \ + XX(X509_INNER_TRUNC, "Decoding error: inner element extends beyond outer element size.") \ + XX(X509_BAD_TAG_CLASS, "Decoding error: unsupported tag class XX( \application or private).") \ + XX(X509_BAD_TAG_VALUE, "Decoding error: unsupported tag value.") \ + XX(X509_INDEFINITE_LENGTH, "Decoding error: indefinite length.") \ + XX(X509_EXTRA_ELEMENT, "Decoding error: extraneous element.") \ + XX(X509_UNEXPECTED, "Decoding error: unexpected element.") \ + XX(X509_NOT_CONSTRUCTED, "Decoding error: expected constructed element, but is primitive.") \ + XX(X509_NOT_PRIMITIVE, "Decoding error: expected primitive element, but is constructed.") \ + XX(X509_PARTIAL_BYTE, "Decoding error: BIT STRING length is not multiple of 8.") \ + XX(X509_BAD_BOOLEAN, "Decoding error: BOOLEAN value has invalid length.") \ + XX(X509_OVERFLOW, "Decoding error: value is off-limits.") \ + XX(X509_BAD_DN, "Invalid distinguished name.") \ + XX(X509_BAD_TIME, "Invalid date/time representation.") \ + XX(X509_UNSUPPORTED, "Certificate contains unsupported features that cannot be ignored.") \ + XX(X509_LIMIT_EXCEEDED, "Key or signature size exceeds internal limits.") \ + XX(X509_WRONG_KEY_TYPE, "Key type does not match that which was expected.") \ + XX(X509_BAD_SIGNATURE, "Signature is invalid.") \ + XX(X509_TIME_UNKNOWN, "Validation time is unknown.") \ + XX(X509_EXPIRED, "Certificate is expired or not yet valid.") \ + XX(X509_DN_MISMATCH, "Issuer/Subject DN mismatch in the chain.") \ + XX(X509_BAD_SERVER_NAME, "Expected server name was not found in the chain.") \ + XX(X509_CRITICAL_EXTENSION, "Unknown critical extension in certificate.") \ + XX(X509_NOT_CA, "Not a CA, or path length constraint violation.") \ + XX(X509_FORBIDDEN_KEY_USAGE, "Key Usage extension prohibits intended usage.") \ + XX(X509_WEAK_PUBLIC_KEY, "Public key found in certificate is too small.") \ + XX(X509_NOT_TRUSTED, "Chain could not be linked to a trust anchor.") + +namespace Ssl +{ +#define XX(tag, text) DEFINE_FSTR_LOCAL(errStr_##tag, #tag) +BR_ERROR_MAP(XX) +#undef XX + +#define XX(tag, text) {BR_ERR_##tag, &errStr_##tag}, +DEFINE_FSTR_MAP_LOCAL(errorMap, int, FSTR::String, BR_ERROR_MAP(XX)); +#undef XX + +String getErrorString(int error) +{ + if(error < 0) { + error = -error; + } + if(error >= BR_ERR_SEND_FATAL_ALERT) { + auto alert = Alert(error - BR_ERR_SEND_FATAL_ALERT); + return F("SEND_") + getAlertString(alert); + } else if(error >= BR_ERR_RECV_FATAL_ALERT) { + auto alert = Alert(error - BR_ERR_RECV_FATAL_ALERT); + return F("RECV_") + getAlertString(alert); + } else { + auto s = String(errorMap[error]); + return s ?: F("Unknown_") + String(error); + } +} + +Alert getAlert(int error) +{ + if(error < 0) { + error = -error; + } + if(error >= BR_ERR_SEND_FATAL_ALERT) { + return Alert(error - BR_ERR_SEND_FATAL_ALERT); + } else if(error >= BR_ERR_RECV_FATAL_ALERT) { + return Alert(error - BR_ERR_RECV_FATAL_ALERT); + } else { + return Alert::Invalid; + } +} + +} // namespace Ssl diff --git a/Sming/Components/ssl/BearSsl/BrError.h b/Sming/Components/ssl/BearSsl/BrError.h new file mode 100644 index 0000000000..c0887d8ba3 --- /dev/null +++ b/Sming/Components/ssl/BearSsl/BrError.h @@ -0,0 +1,19 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * BrError.h + * + ****/ + +#pragma once + +#include + +namespace Ssl +{ +String getErrorString(int error); +Alert getAlert(int error); +} // namespace Ssl diff --git a/Sming/Components/ssl/BearSsl/BrFactory.cpp b/Sming/Components/ssl/BearSsl/BrFactory.cpp new file mode 100644 index 0000000000..b64dce3113 --- /dev/null +++ b/Sming/Components/ssl/BearSsl/BrFactory.cpp @@ -0,0 +1,30 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * FactoryImpl.cpp + * + * @author: 2019 - mikee47 + * + ****/ + +#include +#include "BrContext.h" + +namespace Ssl +{ +class BrFactory : public Factory +{ +public: + Context* createContext(Session& session) override + { + return new BrContext(session); + } +}; + +static BrFactory brFactory; +Factory* factory = &brFactory; + +} // namespace Ssl diff --git a/Sming/Components/ssl/BearSsl/BrServerConnection.cpp b/Sming/Components/ssl/BearSsl/BrServerConnection.cpp new file mode 100644 index 0000000000..73009bd9ac --- /dev/null +++ b/Sming/Components/ssl/BearSsl/BrServerConnection.cpp @@ -0,0 +1,56 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * BrServerConnection.cpp + * + * @author: 2019 - mikee47 + * + ****/ + +/* + */ +#include +#include "BrServerConnection.h" +#include + +namespace Ssl +{ +int BrServerConnection::init() +{ + br_ssl_server_zero(&serverContext); + + // Server requires bi-directional buffer + size_t bufSize = maxBufferSizeToBytes(context.getSession().maxBufferSize); + if(bufSize == 0) { + bufSize = 4096; + } + // add on minimum size for output + bufSize += 512U; + + int err = BrConnection::init(bufSize, true); + if(err < 0) { + return err; + } + + auto engine = getEngine(); + + br_ssl_engine_add_flags(engine, BR_OPT_NO_RENEGOTIATION); + + auto& keyCert = context.getSession().keyCert; + cert.data = const_cast(keyCert.getCertificate()); + cert.data_len = keyCert.getCertificateLength(); + err = key.decode(keyCert.getKey(), keyCert.getKeyLength()); + if(err < 0) { + return err; + } + br_ssl_server_set_single_rsa(&serverContext, &cert, 1, key, BR_KEYTYPE_RSA | BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN, + br_rsa_private_get_default(), br_rsa_pkcs1_sign_get_default()); + br_ssl_server_reset(&serverContext); + + return startHandshake(); +} + +} // namespace Ssl diff --git a/Sming/Components/ssl/BearSsl/BrServerConnection.h b/Sming/Components/ssl/BearSsl/BrServerConnection.h new file mode 100644 index 0000000000..4b0d2e99d5 --- /dev/null +++ b/Sming/Components/ssl/BearSsl/BrServerConnection.h @@ -0,0 +1,51 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * BrServerConnection.h + * + * @author: 2019 - mikee47 + * + ****/ + +#pragma once + +#include "BrConnection.h" +#include "BrServerKey.h" + +namespace Ssl +{ +class BrServerConnection : public BrConnection +{ +public: + using BrConnection::BrConnection; + + ~BrServerConnection() + { + } + + int init(); + + const Certificate* getCertificate() const override + { + return nullptr; + } + + void freeCertificate() override + { + } + + br_ssl_engine_context* getEngine() override + { + return &serverContext.eng; + } + +private: + br_ssl_server_context serverContext; + br_x509_certificate cert; + BrServerKey key; +}; + +} // namespace Ssl diff --git a/Sming/Components/ssl/BearSsl/BrServerKey.cpp b/Sming/Components/ssl/BearSsl/BrServerKey.cpp new file mode 100644 index 0000000000..0d1c540ce2 --- /dev/null +++ b/Sming/Components/ssl/BearSsl/BrServerKey.cpp @@ -0,0 +1,87 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * BrServerKey.cpp + * + ****/ + +#include +#include "BrServerKey.h" + +namespace Ssl +{ +int BrServerKey::decode(const uint8_t* buf, size_t len) +{ + freeMem(); + + br_skey_decoder_context dc; + br_skey_decoder_init(&dc); + br_skey_decoder_push(&dc, buf, len); + int err = br_skey_decoder_last_error(&dc); + if(err != 0) { + return -err; + } + + auto copy = [](const uint8_t* buf, size_t len) { + auto p = new uint8_t[len]; + memcpy(p, buf, len); + return p; + }; + + keyType = Type(br_skey_decoder_key_type(&dc)); + switch(keyType) { + case Type::RSA: { + auto rk = br_skey_decoder_get_rsa(&dc); + key.rsa.n_bitlen = rk->n_bitlen; + key.rsa.p = copy(rk->p, rk->plen); + key.rsa.plen = rk->plen; + key.rsa.q = copy(rk->q, rk->qlen); + key.rsa.qlen = rk->qlen; + key.rsa.dp = copy(rk->dp, rk->dplen); + key.rsa.dplen = rk->dplen; + key.rsa.dq = copy(rk->dq, rk->dqlen); + key.rsa.dqlen = rk->dqlen; + key.rsa.iq = copy(rk->iq, rk->iqlen); + key.rsa.iqlen = rk->iqlen; + return BR_ERR_OK; + } + + case Type::EC: { + auto ek = br_skey_decoder_get_ec(&dc); + key.ec.curve = ek->curve; + key.ec.x = copy(ek->x, ek->xlen); + key.ec.xlen = ek->xlen; + return BR_ERR_OK; + } + + default: + debug_e("Unknown key type: %d", keyType); + return BR_ERR_UNKNOWN_TYPE; + } +} + +void BrServerKey::freeMem() +{ + switch(keyType) { + case Type::RSA: + delete[] key.rsa.iq; + delete[] key.rsa.dq; + delete[] key.rsa.dp; + delete[] key.rsa.q; + delete[] key.rsa.p; + break; + + case Type::EC: + delete[] key.ec.x; + break; + + case Type::None:; + } + + keyType = Type::None; +} + +} // namespace Ssl diff --git a/Sming/Components/ssl/BearSsl/BrServerKey.h b/Sming/Components/ssl/BearSsl/BrServerKey.h new file mode 100644 index 0000000000..23eb318a6e --- /dev/null +++ b/Sming/Components/ssl/BearSsl/BrServerKey.h @@ -0,0 +1,61 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * BrServerKey.h + * + ****/ + +#pragma once + +#include + +namespace Ssl +{ +/** + * @brief Decodes a private key blob into BR compatible format + */ +class BrServerKey +{ +public: + enum class Type { + None = 0, + RSA = BR_KEYTYPE_RSA, + EC = BR_KEYTYPE_EC, + }; + + ~BrServerKey() + { + freeMem(); + } + + int decode(const uint8_t* buf, size_t len); + + void freeMem(); + + Type getKeyType() const + { + return keyType; + } + + operator const br_rsa_private_key*() const + { + return (keyType == Type::RSA) ? &key.rsa : nullptr; + } + + operator const br_ec_private_key*() const + { + return (keyType == Type::EC) ? &key.ec : nullptr; + } + +private: + Type keyType = Type::None; + union { + br_rsa_private_key rsa; + br_ec_private_key ec; + } key = {}; +}; + +} // namespace Ssl diff --git a/Sming/Components/ssl/BearSsl/X509Context.cpp b/Sming/Components/ssl/BearSsl/X509Context.cpp new file mode 100644 index 0000000000..e88f29ffbc --- /dev/null +++ b/Sming/Components/ssl/BearSsl/X509Context.cpp @@ -0,0 +1,45 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * X509Context.cpp + * + ****/ + +#include "X509Context.h" +#include "BrConnection.h" + +namespace Ssl +{ +const br_x509_class X509Context::vt PROGMEM = { + sizeof(X509Context), start_chain, start_cert, append, end_cert, end_chain, get_pkey, +}; + +void X509Context::startChain(const char* serverName) +{ + br_x509_decoder_init(&x509Decoder, subject.append, &subject, issuer.append, &issuer); + certificateCount = 0; + br_sha1_init(&certificateSha1); + issuer.clear(); + subject.clear(); + debug_i("X509Context: serverName = \"%s\"", serverName); + (void)serverName; +} + +unsigned X509Context::endChain() +{ + if(certificateCount == 0) { + debug_w("No certificate processed"); + return BR_ERR_X509_EMPTY_CHAIN; + } + + if(!onValidate()) { + return BR_ERR_X509_NOT_TRUSTED; + } + + return BR_ERR_OK; +} + +} // namespace Ssl diff --git a/Sming/Components/ssl/BearSsl/X509Context.h b/Sming/Components/ssl/BearSsl/X509Context.h new file mode 100644 index 0000000000..106898b2e6 --- /dev/null +++ b/Sming/Components/ssl/BearSsl/X509Context.h @@ -0,0 +1,128 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * X509Context.h + * + ****/ + +#pragma once + +#include +#include "X509Name.h" + +namespace Ssl +{ +#define GET_SELF() auto self = reinterpret_cast(ctx) + +class X509Context +{ +public: + using OnValidate = Delegate; + + X509Context(OnValidate onValidate) : onValidate(onValidate) + { + } + + operator const br_x509_class**() + { + return &vtable; + } + + const X509Name& getIssuer() const + { + return issuer; + } + + const X509Name& getSubject() const + { + return subject; + } + + bool matchFingerprint(const uint8_t* sha1Hash) const + { + uint8_t certHash[br_sha1_SIZE]; + br_sha1_out(&certificateSha1, certHash); + return memcmp(certHash, sha1Hash, br_sha1_SIZE) == 0; + } + + bool matchPki(const uint8_t* hash) const + { + // @todo + return false; + } + +private: + // Callback on the first byte of any certificate + static void start_chain(const br_x509_class** ctx, const char* server_name) + { + debug_d("start_chain: %s", server_name); + GET_SELF(); + self->startChain(server_name); + } + + void startChain(const char* serverName); + + // Callback for each certificate present in the chain + static void start_cert(const br_x509_class** ctx, uint32_t length) + { + debug_d("start_cert: %u", length); + (void)ctx; + (void)length; + } + + // Callback for each byte stream in the chain + static void append(const br_x509_class** ctx, const unsigned char* buf, size_t len) + { + debug_d("append: %u", len); + GET_SELF(); + // Don't process anything but the first certificate in the chain + if(self->certificateCount == 0) { + br_sha1_update(&self->certificateSha1, buf, len); + br_x509_decoder_push(&self->x509Decoder, (const void*)buf, len); + debug_hex(DBG, "CERT", buf, len); + } + } + + static void end_cert(const br_x509_class** ctx) + { + debug_d("end_cert"); + GET_SELF(); + ++self->certificateCount; + } + + // Complete chain has been parsed, return 0 on validation success + static unsigned end_chain(const br_x509_class** ctx) + { + debug_d("end_chain"); + GET_SELF(); + return self->endChain(); + } + + unsigned endChain(); + + // Return the public key from the validator (set by x509_minimal) + static const br_x509_pkey* get_pkey(const br_x509_class* const* ctx, unsigned* usages) + { + auto self = reinterpret_cast(ctx); + if(usages != nullptr) { + *usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN; + } + return &self->x509Decoder.pkey; + } + +private: + const br_x509_class* vtable = &vt; + static const br_x509_class vt; + OnValidate onValidate; + br_sha1_context certificateSha1 = {}; + X509Name issuer; + X509Name subject; + br_x509_decoder_context x509Decoder = {}; + bool allowSelfSigned = false; + uint8_t certificateCount = 0; +}; + +} // namespace Ssl diff --git a/Sming/Components/ssl/BearSsl/X509Name.cpp b/Sming/Components/ssl/BearSsl/X509Name.cpp new file mode 100644 index 0000000000..cd247ba4e9 --- /dev/null +++ b/Sming/Components/ssl/BearSsl/X509Name.cpp @@ -0,0 +1,57 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * X509Name.cpp + * + ****/ + +#include +#include "X509Name.h" +#include "Asn1Parser.h" + +namespace Ssl +{ +uint8_t* X509Name::getHash(uint8_t hash[br_sha256_SIZE]) const +{ + br_sha256_context sha256; + br_sha256_init(&sha256); + br_sha256_update(&sha256, dn.c_str(), dn.length()); + br_sha256_out(&sha256, hash); + return hash; +} + +String X509Name::getRDN(uint8_t type) const +{ + Asn1Parser parser(reinterpret_cast(dn.c_str()), dn.length()); + + if(parser.getNextObject(ASN1_SEQUENCE) == 0) { + return nullptr; + } + + unsigned len; + while((len = parser.getNextObject(ASN1_SET)) != 0) { + auto nextOffset = parser.getOffset() + len; + + if(parser.getNextObject(ASN1_SEQUENCE) == 0) { + break; + } + + int dn_type = parser.getObjectId(); + if(dn_type < 0) { + break; + } + + if(dn_type == type) { + return parser.getString(); + } + + parser.setOffset(nextOffset); + } + + return nullptr; +} + +} // namespace Ssl diff --git a/Sming/Components/ssl/BearSsl/X509Name.h b/Sming/Components/ssl/BearSsl/X509Name.h new file mode 100644 index 0000000000..0e426d4dc4 --- /dev/null +++ b/Sming/Components/ssl/BearSsl/X509Name.h @@ -0,0 +1,59 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * X509Name.h + * + ****/ + +#pragma once + +#include +#include + +namespace Ssl +{ +class X509Name +{ +public: + X509Name() = default; + + X509Name(const X509Name& name) : dn(name.dn) + { + } + + X509Name(X509Name&& name) : dn(std::move(name.dn)) + { + } + + X509Name& operator=(X509Name&& name) + { + if(this != &name) { + dn = std::move(name.dn); + } + return *this; + } + + void clear() + { + dn.setLength(0); + } + + uint8_t* getHash(uint8_t hash[br_sha256_SIZE]) const; + + static void append(void* ctx, const void* buf, size_t len) + { + auto self = reinterpret_cast(ctx); + self->dn.concat(static_cast(buf), len); + } + + // Obtain Relative Distinguished Name by type + String getRDN(uint8_t type) const; + +private: + String dn; ///< The ASN1-encoded Distinguished Name +}; + +} // namespace Ssl diff --git a/Sming/Components/ssl/NoSsl/FactoryImpl.cpp b/Sming/Components/ssl/NoSsl/FactoryImpl.cpp new file mode 100644 index 0000000000..2e9bd79f2a --- /dev/null +++ b/Sming/Components/ssl/NoSsl/FactoryImpl.cpp @@ -0,0 +1,6 @@ +#include + +namespace Ssl +{ +Factory* factory = nullptr; +} diff --git a/Sming/Components/ssl/README.rst b/Sming/Components/ssl/README.rst new file mode 100644 index 0000000000..c9631b23ad --- /dev/null +++ b/Sming/Components/ssl/README.rst @@ -0,0 +1,15 @@ +SSL +=== + +Provides Secure Socket Layer (SSL) support for Sming with selectable implementation. + +.. envvar:: ENABLE_SSL + + 1: (default) Enable SSL using AXTLS implementation + 0: Disable SSL + + Other values specify a supported SSL implementation: + + - Axtls + - Bearssl + diff --git a/Sming/Components/ssl/Tools/make_certs.sh b/Sming/Components/ssl/Tools/make_certs.sh new file mode 100644 index 0000000000..b79373e175 --- /dev/null +++ b/Sming/Components/ssl/Tools/make_certs.sh @@ -0,0 +1,182 @@ +#!/bin/sh +# +# Copyright (c) 2007-2016, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# +# Generate the certificates and keys for testing. +# + +PROJECT_NAME="Sming Project" + +# Generate the openssl configuration files. +cat > ca_cert.conf << EOF +[ req ] +distinguished_name = req_distinguished_name +prompt = no + +[ req_distinguished_name ] + O = $PROJECT_NAME Dodgy Certificate Authority +EOF + +cat > certs.conf << EOF +[ req ] +distinguished_name = req_distinguished_name +prompt = no + +[ req_distinguished_name ] + O = $PROJECT_NAME + CN = 127.0.0.1 +EOF + +cat > device_cert.conf << EOF +[ req ] +distinguished_name = req_distinguished_name +prompt = no + +[ req_distinguished_name ] + O = $PROJECT_NAME Device Certificate +EOF + +# private key generation +openssl genrsa -out ca_key.pem 1024 +openssl genrsa -out key_512.pem 512 +openssl genrsa -out key_1024.pem 1024 +openssl genrsa -out key_1042.pem 1042 +openssl genrsa -out key_2048.pem 2048 +openssl genrsa -out key_4096.pem 4096 +openssl genrsa -out device_key.pem 1024 +openssl genrsa -aes128 -passout pass:abcd -out key_aes128.pem 512 +openssl genrsa -aes256 -passout pass:abcd -out key_aes256.pem 512 + + +# convert private keys into DER format +openssl rsa -in key_512.pem -out key_512 -outform DER +openssl rsa -in key_1024.pem -out key_1024 -outform DER +openssl rsa -in key_1042.pem -out key_1042 -outform DER +openssl rsa -in key_2048.pem -out key_2048 -outform DER +openssl rsa -in key_4096.pem -out key_4096 -outform DER +openssl rsa -in device_key.pem -out device_key -outform DER + +# cert requests +openssl req -out ca_x509.req -key ca_key.pem -new \ + -config ./ca_cert.conf +openssl req -out x509_512.req -key key_512.pem -new \ + -config ./certs.conf +openssl req -out x509_1024.req -key key_1024.pem -new \ + -config ./certs.conf +openssl req -out x509_1042.req -key key_1042.pem -new \ + -config ./certs.conf +openssl req -out x509_2048.req -key key_2048.pem -new \ + -config ./certs.conf +openssl req -out x509_4096.req -key key_4096.pem -new \ + -config ./certs.conf +openssl req -out x509_device.req -key device_key.pem -new \ + -config ./device_cert.conf +openssl req -out x509_aes128.req -key key_aes128.pem \ + -new -config ./certs.conf -passin pass:abcd +openssl req -out x509_aes256.req -key key_aes256.pem \ + -new -config ./certs.conf -passin pass:abcd + +# generate the actual certs. +openssl x509 -req -in ca_x509.req -out ca_x509.pem \ + -sha1 -days 5000 -signkey ca_key.pem +openssl x509 -req -in x509_512.req -out x509_512.pem \ + -sha1 -CAcreateserial -days 5000 \ + -CA ca_x509.pem -CAkey ca_key.pem +openssl x509 -req -in x509_1024.req -out x509_1024.pem \ + -sha1 -CAcreateserial -days 5000 \ + -CA ca_x509.pem -CAkey ca_key.pem +openssl x509 -req -in x509_1042.req -out x509_1042.pem \ + -sha1 -CAcreateserial -days 5000 \ + -CA ca_x509.pem -CAkey ca_key.pem +openssl x509 -req -in x509_2048.req -out x509_2048.pem \ + -md5 -CAcreateserial -days 5000 \ + -CA ca_x509.pem -CAkey ca_key.pem +openssl x509 -req -in x509_4096.req -out x509_4096.pem \ + -md5 -CAcreateserial -days 5000 \ + -CA ca_x509.pem -CAkey ca_key.pem +openssl x509 -req -in x509_device.req -out x509_device.pem \ + -sha1 -CAcreateserial -days 5000 \ + -CA x509_512.pem -CAkey key_512.pem +openssl x509 -req -in x509_aes128.req \ + -out x509_aes128.pem \ + -sha1 -CAcreateserial -days 5000 \ + -CA ca_x509.pem -CAkey ca_key.pem +openssl x509 -req -in x509_aes256.req \ + -out x509_aes256.pem \ + -sha1 -CAcreateserial -days 5000 \ + -CA ca_x509.pem -CAkey ca_key.pem + +# note: must be root to do this +DATE_NOW=$(date) +if date -s "Jan 1 2025"; then +openssl x509 -req -in x509_512.req -out x509_bad_before.pem \ + -sha1 -CAcreateserial -days 365 \ + -CA ca_x509.pem -CAkey ca_key.pem +date -s "$DATE_NOW" +touch x509_bad_before.pem +fi +openssl x509 -req -in x509_512.req -out x509_bad_after.pem \ + -sha1 -CAcreateserial -days -365 \ + -CA ca_x509.pem -CAkey ca_key.pem + +# some cleanup +rm *.req +rm srl +rm *.conf + +# need this for the client tests +openssl x509 -in ca_x509.pem -outform DER -out ca_x509.cer +openssl x509 -in x509_512.pem -outform DER -out x509_512.cer +openssl x509 -in x509_1024.pem -outform DER -out x509_1024.cer +openssl x509 -in x509_1042.pem -outform DER -out x509_1042.cer +openssl x509 -in x509_2048.pem -outform DER -out x509_2048.cer +openssl x509 -in x509_4096.pem -outform DER -out x509_4096.cer +openssl x509 -in x509_device.pem -outform DER -out x509_device.cer + +# generate pkcs8 files (use RC4-128 for encryption) +openssl pkcs8 -in key_512.pem -passout pass:abcd -topk8 -v1 PBE-SHA1-RC4-128 -out encrypted_pem.p8 +openssl pkcs8 -in key_512.pem -passout pass:abcd -topk8 -outform DER -v1 PBE-SHA1-RC4-128 -out encrypted.p8 +openssl pkcs8 -in key_512.pem -nocrypt -topk8 -out unencrypted_pem.p8 +openssl pkcs8 -in key_512.pem -nocrypt -topk8 -outform DER -out unencrypted.p8 + +# generate pkcs12 files (use RC4-128 for encryption) +openssl pkcs12 -export -in x509_1024.pem -inkey key_1024.pem -certfile ca_x509.pem -keypbe PBE-SHA1-RC4-128 -certpbe PBE-SHA1-RC4-128 -name "p12_with_CA" -out withCA.p12 -password pass:abcd +openssl pkcs12 -export -in x509_1024.pem -inkey key_1024.pem -keypbe PBE-SHA1-RC4-128 -certpbe PBE-SHA1-RC4-128 -name "p12_without_CA" -out withoutCA.p12 -password pass:abcd +openssl pkcs12 -export -in x509_1024.pem -inkey key_1024.pem -keypbe PBE-SHA1-RC4-128 -certpbe PBE-SHA1-RC4-128 -out noname.p12 -password pass:abcd + +# PEM certificate chain +cat ca_x509.pem >> x509_device.pem + +# set default key/cert for use in the server +xxd -i x509_1024.cer | sed -e \ + "s/axTLS_x509_1024_cer/default_certificate/" > $SSL_INCLUDE_DIR/cert.h +xxd -i key_1024 | sed -e \ + "s/axTLS_key_1024/default_private_key/" > $SSL_INCLUDE_DIR/private_key.h diff --git a/Sming/Components/ssl/api.rst b/Sming/Components/ssl/api.rst new file mode 100644 index 0000000000..2ee8de5f8e --- /dev/null +++ b/Sming/Components/ssl/api.rst @@ -0,0 +1,4 @@ +API Documentation +================= + +.. doxygennamespace:: Ssl diff --git a/Sming/Components/ssl/comparison.rst b/Sming/Components/ssl/comparison.rst new file mode 100644 index 0000000000..27643043b0 --- /dev/null +++ b/Sming/Components/ssl/comparison.rst @@ -0,0 +1,7 @@ +Comparison of SSL implementations +================================== + +.. highlight:: C++ + + +{ todo } diff --git a/Sming/Components/ssl/component.mk b/Sming/Components/ssl/component.mk new file mode 100644 index 0000000000..03d215d4d5 --- /dev/null +++ b/Sming/Components/ssl/component.mk @@ -0,0 +1,61 @@ +# => SSL +COMPONENT_SRCDIRS := src +COMPONENT_INCDIRS := include + +CONFIG_VARS += ENABLE_SSL +COMPONENT_RELINK_VARS := ENABLE_SSL +ENABLE_SSL ?= 0 +ifeq ($(ENABLE_SSL),$(filter $(ENABLE_SSL),1 Axtls)) + override ENABLE_SSL := Axtls + COMPONENT_DEPENDS := axtls-8266 + COMPONENT_SRCDIRS += Axtls + APP_CFLAGS += -DENABLE_SSL +else ifeq ($(ENABLE_SSL),Bearssl) + override ENABLE_SSL := Bearssl + COMPONENT_DEPENDS := bearssl-esp8266 + COMPONENT_SRCDIRS += BearSsl + APP_CFLAGS += -DENABLE_SSL +else ifeq ($(ENABLE_SSL),Mbedtls) +$(error MBED TLS not yet implemented) +else ifeq ($(ENABLE_SSL),0) + COMPONENT_SRCDIRS += NoSsl +else +$(error Unsupported ENABLE_SSL value "$(ENABLE_SSL)". Supported values are Axtls, Bearssl or 0 to disable SSL) +endif + +COMPONENT_RELINK_VARS += SSL_DEBUG +SSL_DEBUG ?= 0 +ifeq ($(SSL_DEBUG),1) + COMPONENT_CXXFLAGS += -DSSL_DEBUG=1 +endif + +# Prints SSL status when App gets built +CUSTOM_TARGETS += check-ssl +.PHONY:check-ssl +check-ssl: +ifeq ($(ENABLE_SSL),0) + $(info SSL support disabled) +else + $(info Using $(ENABLE_SSL) SSL implementation) + +# Application +CUSTOM_TARGETS += include/ssl/private_key.h + +SSL_TOOLS_PATH := $(COMPONENT_PATH)/Tools +SSL_INCLUDE_DIR := $(PROJECT_DIR)/include/ssl +SSL_CERT_DIR := $(PROJECT_DIR)/cert + +include/ssl/private_key.h: + $(info Generating unique certificate and key. This may take some time...) + $(Q) mkdir -p $(SSL_INCLUDE_DIR) $(SSL_CERT_DIR) + $(Q) chmod a+x $(SSL_TOOLS_PATH)/make_certs.sh + cd $(SSL_CERT_DIR); SSL_INCLUDE_DIR=$(SSL_INCLUDE_DIR) $(SSL_TOOLS_PATH)/make_certs.sh + +endif + +COMPONENT_DOXYGEN_INPUT := \ + include + +COMPONENT_DOXYGEN_PREDEFINED := \ + ENABLE_SSL=1 + diff --git a/Sming/Components/ssl/include/Network/Ssl/Alert.h b/Sming/Components/ssl/include/Network/Ssl/Alert.h new file mode 100644 index 0000000000..ed756ac30c --- /dev/null +++ b/Sming/Components/ssl/include/Network/Ssl/Alert.h @@ -0,0 +1,54 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Alert.h + * + ****/ + +#pragma once + +#include + +namespace Ssl +{ +#define SSL_ALERT_CODE_MAP(XX) \ + XX(CLOSE_NOTIFY, 0) \ + XX(UNEXPECTED_MESSAGE, 10) \ + XX(BAD_RECORD_MAC, 20) \ + XX(RECORD_OVERFLOW, 22) \ + XX(DECOMPRESSION_FAILURE, 30) \ + XX(HANDSHAKE_FAILURE, 40) \ + XX(BAD_CERTIFICATE, 42) \ + XX(UNSUPPORTED_CERTIFICATE, 43) \ + XX(CERTIFICATE_REVOKED, 44) \ + XX(CERTIFICATE_EXPIRED, 45) \ + XX(CERTIFICATE_UNKNOWN, 46) \ + XX(ILLEGAL_PARAMETER, 47) \ + XX(UNKNOWN_CA, 48) \ + XX(ACCESS_DENIED, 49) \ + XX(DECODE_ERROR, 50) \ + XX(DECRYPT_ERROR, 51) \ + XX(INVALID_VERSION, 70) \ + XX(INSUFFICIENT_SECURITY, 71) \ + XX(INTERNAL_ERROR, 80) \ + XX(USER_CANCELLED, 90) \ + XX(NO_RENEGOTIATION, 100) \ + XX(UNSUPPORTED_EXTENSION, 110) \ + XX(NO_APPLICATION_PROTOCOL, 120) + +/** + * @brief Alert codes defined by the standard + */ +enum class Alert { + Invalid = -1, +#define XX(tag, code) tag = code, + SSL_ALERT_CODE_MAP(XX) +#undef XX +}; + +String getAlertString(Alert alert); + +} // namespace Ssl diff --git a/Sming/Components/ssl/include/Network/Ssl/Certificate.h b/Sming/Components/ssl/include/Network/Ssl/Certificate.h new file mode 100644 index 0000000000..ff52707892 --- /dev/null +++ b/Sming/Components/ssl/include/Network/Ssl/Certificate.h @@ -0,0 +1,110 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Certificate.h + * + * @author: 2019 - Slavey Karadzhov + * + ****/ + +#pragma once + +#include + +namespace Ssl +{ +/** + * @brief X509 Relative Distinguished Name type + * + * From namespace 2.5.4.x + */ +#define SSL_X509_RDN_OID_MAP(XX) \ + XX(COMMON_NAME, 2, 5, 4, 3) \ + XX(SURNAME, 2, 5, 4, 4) \ + XX(SERIAL_NUMBER, 2, 5, 4, 5) \ + XX(COUNTRY_NAME, 2, 5, 4, 6) \ + XX(LOCALITY_NAME, 2, 5, 4, 7) \ + XX(STATE_OR_PROVINCE_NAME, 2, 5, 4, 8) \ + XX(STREET_ADDRESS, 2, 5, 4, 9) \ + XX(ORGANIZATION_NAME, 2, 5, 4, 10) \ + XX(ORGANIZATIONAL_UNIT_NAME, 2, 5, 4, 11) \ + XX(TITLE, 2, 5, 4, 12) \ + XX(BUSINESS_CATEGORY, 2, 5, 4, 15) \ + XX(POSTAL_ADDRESS, 2, 5, 4, 16) \ + XX(POSTAL_CODE, 2, 5, 4, 17) \ + XX(GIVEN_NAME, 2, 5, 4, 42) \ + XX(GENERATION_QUALIFIER, 2, 5, 4, 44) \ + XX(X500_UNIQUE_IDENTIFIER, 2, 5, 4, 45) \ + XX(DN_QUALIFIER, 2, 5, 4, 46) \ + XX(PSEUDONYM, 2, 5, 4, 65) + +/** + * @brief Implemented by SSL adapter to handle certificate operations + */ +class Certificate +{ +public: + /** + * @brief Distinguished Name type + */ + enum class DN { + ISSUER, + SUBJECT, + }; + + /** + * @brief Relative Distinguished Name type identifying a name component + */ + enum class RDN { +#define XX(tag, a, b, c, d) tag, + SSL_X509_RDN_OID_MAP(XX) +#undef XX + MAX + }; + + /** + * @brief Obtain a string describing the given name component + */ + static String getRdnTypeString(Certificate::RDN rdn); + + virtual ~Certificate() + { + } + + /** + * @brief Check if certificate fingerprint (SHA1) matches the one given. + * @param hash - SHA1 fingerprint to match against + * @retval bool true on match, false otherwise + */ + virtual bool matchFingerprint(const uint8_t* hash) const = 0; + + /** + * @brief Check if SHA256 hash of Subject Public Key Info matches the one given. + * @param hash SHA256 hash to match against + * @retval bool true on match, false otherwise + */ + virtual bool matchPki(const uint8_t* hash) const = 0; + + /** + * @brief Retrieve an X.509 distinguished name component + * @param dn The desired Distinguished Name + * @param rdn The component to return + * @retval String The requested Distinguished Name component + */ + virtual String getName(DN dn, RDN rdn) const = 0; + + /** + * @brief Debugging print support + */ + size_t printTo(Print& p) const; +}; + +} // namespace Ssl + +/** + * @deprecated Use `Ssl::Certificate` instead + */ +typedef Ssl::Certificate SslCertificate SMING_DEPRECATED; diff --git a/Sming/Components/ssl/include/Network/Ssl/CipherSuite.h b/Sming/Components/ssl/include/Network/Ssl/CipherSuite.h new file mode 100644 index 0000000000..acd9598948 --- /dev/null +++ b/Sming/Components/ssl/include/Network/Ssl/CipherSuite.h @@ -0,0 +1,164 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * CipherSuite.h + * + ****/ + +#pragma once + +#include + +namespace Ssl +{ +/* + * Cipher suites + * + * TLS v1.2 https://tools.ietf.org/html/rfc5246#appendix-A.5 + * TLS v1.3 https://tools.ietf.org/html/rfc8446#appendix-B.4 + * + * Courtesy of Bear SSL. Defined here so they're not tied into any specific implementation. + * + */ +#define SSL_CIPHER_SUITE_MAP(XX) \ + /* From RFC 5246 */ \ + XX(NULL_WITH_NULL_NULL, 0x0000) \ + XX(RSA_WITH_NULL_MD5, 0x0001) \ + XX(RSA_WITH_NULL_SHA, 0x0002) \ + XX(RSA_WITH_NULL_SHA256, 0x003B) \ + XX(RSA_WITH_RC4_128_MD5, 0x0004) \ + XX(RSA_WITH_RC4_128_SHA, 0x0005) \ + XX(RSA_WITH_3DES_EDE_CBC_SHA, 0x000A) \ + XX(RSA_WITH_AES_128_CBC_SHA, 0x002F) \ + XX(RSA_WITH_AES_256_CBC_SHA, 0x0035) \ + XX(RSA_WITH_AES_128_CBC_SHA256, 0x003C) \ + XX(RSA_WITH_AES_256_CBC_SHA256, 0x003D) \ + XX(DH_DSS_WITH_3DES_EDE_CBC_SHA, 0x000D) \ + XX(DH_RSA_WITH_3DES_EDE_CBC_SHA, 0x0010) \ + XX(DHE_DSS_WITH_3DES_EDE_CBC_SHA, 0x0013) \ + XX(DHE_RSA_WITH_3DES_EDE_CBC_SHA, 0x0016) \ + XX(DH_DSS_WITH_AES_128_CBC_SHA, 0x0030) \ + XX(DH_RSA_WITH_AES_128_CBC_SHA, 0x0031) \ + XX(DHE_DSS_WITH_AES_128_CBC_SHA, 0x0032) \ + XX(DHE_RSA_WITH_AES_128_CBC_SHA, 0x0033) \ + XX(DH_DSS_WITH_AES_256_CBC_SHA, 0x0036) \ + XX(DH_RSA_WITH_AES_256_CBC_SHA, 0x0037) \ + XX(DHE_DSS_WITH_AES_256_CBC_SHA, 0x0038) \ + XX(DHE_RSA_WITH_AES_256_CBC_SHA, 0x0039) \ + XX(DH_DSS_WITH_AES_128_CBC_SHA256, 0x003E) \ + XX(DH_RSA_WITH_AES_128_CBC_SHA256, 0x003F) \ + XX(DHE_DSS_WITH_AES_128_CBC_SHA256, 0x0040) \ + XX(DHE_RSA_WITH_AES_128_CBC_SHA256, 0x0067) \ + XX(DH_DSS_WITH_AES_256_CBC_SHA256, 0x0068) \ + XX(DH_RSA_WITH_AES_256_CBC_SHA256, 0x0069) \ + XX(DHE_DSS_WITH_AES_256_CBC_SHA256, 0x006A) \ + XX(DHE_RSA_WITH_AES_256_CBC_SHA256, 0x006B) \ + XX(DH_anon_WITH_RC4_128_MD5, 0x0018) \ + XX(DH_anon_WITH_3DES_EDE_CBC_SHA, 0x001B) \ + XX(DH_anon_WITH_AES_128_CBC_SHA, 0x0034) \ + XX(DH_anon_WITH_AES_256_CBC_SHA, 0x003A) \ + XX(DH_anon_WITH_AES_128_CBC_SHA256, 0x006C) \ + XX(DH_anon_WITH_AES_256_CBC_SHA256, 0x006D) \ + /* From, RFC, 4492, */ \ + XX(ECDH_ECDSA_WITH_NULL_SHA, 0xC001) \ + XX(ECDH_ECDSA_WITH_RC4_128_SHA, 0xC002) \ + XX(ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, 0xC003) \ + XX(ECDH_ECDSA_WITH_AES_128_CBC_SHA, 0xC004) \ + XX(ECDH_ECDSA_WITH_AES_256_CBC_SHA, 0xC005) \ + XX(ECDHE_ECDSA_WITH_NULL_SHA, 0xC006) \ + XX(ECDHE_ECDSA_WITH_RC4_128_SHA, 0xC007) \ + XX(ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, 0xC008) \ + XX(ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 0xC009) \ + XX(ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 0xC00A) \ + XX(ECDH_RSA_WITH_NULL_SHA, 0xC00B) \ + XX(ECDH_RSA_WITH_RC4_128_SHA, 0xC00C) \ + XX(ECDH_RSA_WITH_3DES_EDE_CBC_SHA, 0xC00D) \ + XX(ECDH_RSA_WITH_AES_128_CBC_SHA, 0xC00E) \ + XX(ECDH_RSA_WITH_AES_256_CBC_SHA, 0xC00F) \ + XX(ECDHE_RSA_WITH_NULL_SHA, 0xC010) \ + XX(ECDHE_RSA_WITH_RC4_128_SHA, 0xC011) \ + XX(ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 0xC012) \ + XX(ECDHE_RSA_WITH_AES_128_CBC_SHA, 0xC013) \ + XX(ECDHE_RSA_WITH_AES_256_CBC_SHA, 0xC014) \ + XX(ECDH_anon_WITH_NULL_SHA, 0xC015) \ + XX(ECDH_anon_WITH_RC4_128_SHA, 0xC016) \ + XX(ECDH_anon_WITH_3DES_EDE_CBC_SHA, 0xC017) \ + XX(ECDH_anon_WITH_AES_128_CBC_SHA, 0xC018) \ + XX(ECDH_anon_WITH_AES_256_CBC_SHA, 0xC019) \ + /* From, RFC, 5288, */ \ + XX(RSA_WITH_AES_128_GCM_SHA256, 0x009C) \ + XX(RSA_WITH_AES_256_GCM_SHA384, 0x009D) \ + XX(DHE_RSA_WITH_AES_128_GCM_SHA256, 0x009E) \ + XX(DHE_RSA_WITH_AES_256_GCM_SHA384, 0x009F) \ + XX(DH_RSA_WITH_AES_128_GCM_SHA256, 0x00A0) \ + XX(DH_RSA_WITH_AES_256_GCM_SHA384, 0x00A1) \ + XX(DHE_DSS_WITH_AES_128_GCM_SHA256, 0x00A2) \ + XX(DHE_DSS_WITH_AES_256_GCM_SHA384, 0x00A3) \ + XX(DH_DSS_WITH_AES_128_GCM_SHA256, 0x00A4) \ + XX(DH_DSS_WITH_AES_256_GCM_SHA384, 0x00A5) \ + XX(DH_anon_WITH_AES_128_GCM_SHA256, 0x00A6) \ + XX(DH_anon_WITH_AES_256_GCM_SHA384, 0x00A7) \ + /* From, RFC, 5289, */ \ + XX(ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 0xC023) \ + XX(ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, 0xC024) \ + XX(ECDH_ECDSA_WITH_AES_128_CBC_SHA256, 0xC025) \ + XX(ECDH_ECDSA_WITH_AES_256_CBC_SHA384, 0xC026) \ + XX(ECDHE_RSA_WITH_AES_128_CBC_SHA256, 0xC027) \ + XX(ECDHE_RSA_WITH_AES_256_CBC_SHA384, 0xC028) \ + XX(ECDH_RSA_WITH_AES_128_CBC_SHA256, 0xC029) \ + XX(ECDH_RSA_WITH_AES_256_CBC_SHA384, 0xC02A) \ + XX(ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0xC02B) \ + XX(ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 0xC02C) \ + XX(ECDH_ECDSA_WITH_AES_128_GCM_SHA256, 0xC02D) \ + XX(ECDH_ECDSA_WITH_AES_256_GCM_SHA384, 0xC02E) \ + XX(ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0xC02F) \ + XX(ECDHE_RSA_WITH_AES_256_GCM_SHA384, 0xC030) \ + XX(ECDH_RSA_WITH_AES_128_GCM_SHA256, 0xC031) \ + XX(ECDH_RSA_WITH_AES_256_GCM_SHA384, 0xC032) \ + /* From, RFC, 6655, and, 7251, */ \ + XX(RSA_WITH_AES_128_CCM, 0xC09C) \ + XX(RSA_WITH_AES_256_CCM, 0xC09D) \ + XX(RSA_WITH_AES_128_CCM_8, 0xC0A0) \ + XX(RSA_WITH_AES_256_CCM_8, 0xC0A1) \ + XX(ECDHE_ECDSA_WITH_AES_128_CCM, 0xC0AC) \ + XX(ECDHE_ECDSA_WITH_AES_256_CCM, 0xC0AD) \ + XX(ECDHE_ECDSA_WITH_AES_128_CCM_8, 0xC0AE) \ + XX(ECDHE_ECDSA_WITH_AES_256_CCM_8, 0xC0AF) \ + /* From, RFC, 7905, */ \ + XX(ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0xCCA8) \ + XX(ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0xCCA9) \ + XX(DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0xCCAA) \ + XX(PSK_WITH_CHACHA20_POLY1305_SHA256, 0xCCAB) \ + XX(ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, 0xCCAC) \ + XX(DHE_PSK_WITH_CHACHA20_POLY1305_SHA256, 0xCCAD) \ + XX(RSA_PSK_WITH_CHACHA20_POLY1305_SHA256, 0xCCAE) \ + /* From, RFC, 7507, */ \ + XX(FALLBACK_SCSV, 0x5600) + +/** + * @brief Cipher suite identifier + * + * The TLS standard specifies codes using two 8-bit values. + * We combine these into a single 16-bit value in MSB-LSB order. + * + * For example: + * + * TLS_RSA_WITH_AES_128_CBC_SHA = { 0x00, 0x2F } = 0x002F + */ +enum class CipherSuite : uint16_t { +#define XX(tag, code) tag = code, + SSL_CIPHER_SUITE_MAP(XX) +#undef XX +}; + +/** + * @brief Gets the name of the cipher suite + * @param id Suite identifier + * @retval String + */ +String getCipherSuiteName(CipherSuite id); + +} // namespace Ssl diff --git a/Sming/Components/ssl/include/Network/Ssl/Connection.h b/Sming/Components/ssl/include/Network/Ssl/Connection.h new file mode 100644 index 0000000000..281e1ecb25 --- /dev/null +++ b/Sming/Components/ssl/include/Network/Ssl/Connection.h @@ -0,0 +1,115 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Connection.h + * + * @author: 2019 - Slavey Karadzhov + * + ****/ + +#pragma once + +#include "SessionId.h" +#include "Certificate.h" +#include "InputBuffer.h" +#include "CipherSuite.h" +#include "Alert.h" + +namespace Ssl +{ +class Context; + +/** + * @brief Implemented by SSL adapter to handle a connection + */ +class Connection : public Printable +{ +public: + Connection(Context& context, tcp_pcb* tcp) : context(context), tcp(tcp) + { + assert(tcp != nullptr); + } + + virtual ~Connection() + { + } + + /** + * @brief Checks if the handshake has finished + * @retval bool true on success + */ + virtual bool isHandshakeDone() const = 0; + + /** + * @brief Reads encrypted information and decrypts it + * @param input Source encrypted data + * @param output Pointer to decrypted plaintext buffer + * + * @retval + * 0 : handshake is still in progress + * > 0 : there is decrypted data + * < 0 : error + */ + virtual int read(InputBuffer& input, uint8_t*& output) = 0; + + /** + * @brief Converts and sends plaintext data + * @param data + * @param length + * @retval int length of the data that was actually written + * < 0 on error + */ + virtual int write(const uint8_t* data, size_t length) = 0; + + /** + * @brief Gets the cipher suite that was used + * @retval CipherSuite IDs as defined by SSL/TLS standard + */ + virtual CipherSuite getCipherSuite() const = 0; + + /** + * @brief Gets the current session id object. + * Should be called after handshake. + * @retval SessionId + */ + virtual SessionId getSessionId() const = 0; + + /** + * @brief Gets the certificate object. + * That object MUST be owned by the Connection implementation + * and should not be freed outside of it + * + * @retval Certificate* Returns NULL if there is no certificate available + */ + virtual const Certificate* getCertificate() const = 0; + + virtual void freeCertificate() = 0; + + /** + * @brief For debugging + */ + size_t printTo(Print& p) const override; + + int writeTcpData(uint8_t* data, size_t length); + + /** + * @brief Get string for error code + */ + virtual String getErrorString(int error) const = 0; + + /** + * @brief Get alert code from error + * @param error + * @retval Alert Alert::INVALID if not an alert + */ + virtual Alert getAlert(int error) const = 0; + +protected: + Context& context; + tcp_pcb* tcp; +}; + +} // namespace Ssl diff --git a/Sming/Components/ssl/include/Network/Ssl/Context.h b/Sming/Components/ssl/include/Network/Ssl/Context.h new file mode 100644 index 0000000000..d4add78aed --- /dev/null +++ b/Sming/Components/ssl/include/Network/Ssl/Context.h @@ -0,0 +1,69 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Context.h + * + * @author: 2019 - Slavey Karadzhov + * + ****/ + +#pragma once + +#include "Connection.h" +#include "KeyCertPair.h" +#include "Validator.h" + +struct tcp_pcb; + +namespace Ssl +{ +class Session; + +/** + * @brief Implemented by SSL adapter to create and manage SSL connections + */ +class Context +{ +public: + Context(Session& session) : session(session) + { + } + + virtual ~Context() + { + } + + /** + * @brief Initializer method that must be called after object creation and before the creation + * of server or client connections + * @retval bool true on success + */ + virtual bool init() = 0; + + /** + * @brief Creates client SSL connection. + * Your SSL client use this call to create a client connection to remote server. + * @retval Connection* + */ + virtual Connection* createClient(tcp_pcb* tcp) = 0; + + /** + * @brief Creates server SSL connection. + * Your SSL servers use this call to allow remote clients to connect to them and use SSL. + * @retval Connection* + */ + virtual Connection* createServer(tcp_pcb* tcp) = 0; + + Session& getSession() + { + return session; + } + +protected: + Session& session; +}; + +} // namespace Ssl diff --git a/Sming/Components/ssl/include/Network/Ssl/Crypto.h b/Sming/Components/ssl/include/Network/Ssl/Crypto.h new file mode 100644 index 0000000000..d6425cbc01 --- /dev/null +++ b/Sming/Components/ssl/include/Network/Ssl/Crypto.h @@ -0,0 +1,37 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Crypto.h + * + * @author: 2019 - Slavey Karadzhov + * + ****/ + +#pragma once + +#include + +/** + * @ingroup ssl + * @{ + */ + +#define MD5_SIZE 16 +#define SHA1_SIZE 20 +#define SHA256_SIZE 32 + +// Common cryptographic support functions +#ifdef __cplusplus +extern "C" { +#endif + +void hmac_md5(const uint8_t* msg, int length, const uint8_t* key, int key_len, uint8_t* digest); + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/Sming/Components/ssl/include/Network/Ssl/Factory.h b/Sming/Components/ssl/include/Network/Ssl/Factory.h new file mode 100644 index 0000000000..238a981008 --- /dev/null +++ b/Sming/Components/ssl/include/Network/Ssl/Factory.h @@ -0,0 +1,42 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Factory.h + * + * @author: 2019 - Slavey Karadzhov + * + ****/ + +#pragma once + +#include "Context.h" + +namespace Ssl +{ +/** + * @brief Implemented by SSL adapter + * @see https://en.wikipedia.org/wiki/Factory_method_pattern + */ +class Factory +{ +public: + virtual ~Factory() + { + } + + /** + * @brief Create SSL context that can be used to create new client or server connections + * @retval Context* The constructed context, shouldn't fail (except on OOM) + */ + virtual Context* createContext(Session& session) = 0; +}; + +/** + * @brief Provided by ssl adapter, NULL if SSL is disabled + */ +extern Factory* factory; + +} // namespace Ssl diff --git a/Sming/Core/Network/Ssl/SslFingerprints.h b/Sming/Components/ssl/include/Network/Ssl/Fingerprints.h similarity index 69% rename from Sming/Core/Network/Ssl/SslFingerprints.h rename to Sming/Components/ssl/include/Network/Ssl/Fingerprints.h index 2535be6670..e93ebf1748 100644 --- a/Sming/Core/Network/Ssl/SslFingerprints.h +++ b/Sming/Components/ssl/include/Network/Ssl/Fingerprints.h @@ -4,19 +4,21 @@ * http://github.com/SmingHub/Sming * All files of the Sming Core are provided under the LGPL v3 license. * - * SslFingerprints.h + * Fingerprints.h * ****/ #pragma once -#include +#include "Crypto.h" +#include +namespace Ssl +{ /** * @brief SSL Certificate fingerprint type - * @ingroup ssl */ -enum SslFingerprintType { +enum FingerprintType { /** @brief Fingerprint based on the SHA1 value of the certificate * @note The SHA1 hash of the entire certificate. This changes on each certificate renewal so needs * to be updated every time the remote server updates its certificate. @@ -36,18 +38,27 @@ enum SslFingerprintType { /** @brief Contains SSL fingerprint data * @ingroup ssl - * @note Lifetime as follows: - * - Constructed by application, using appropriate setXXX method; - * - Passed into HttpRequest by application, using pinCertificate method - request is then queued; - * - Passed into HttpClientConnection (TcpClient descendant) by HttpClient, using pinCertificate method - * - When certificate validated, memory is released + * + * Use within application's SSL initialisation callback to simplify setting standard + * fingerprints for validation. For example: + * + * void mySslInitCallback(Ssl::Session& session, HttpRequest& request) + * { + * static const uint8_t sha1Fingerprint[] PROGMEM = { + * 0x15, 0x9A, 0x76, 0xC5, 0xAE, 0xF4, 0x90, 0x15, 0x79, 0xE6, + * 0xA4, 0x99, 0x96, 0xC1, 0xD6, 0xA1, 0xD9, 0x3B, 0x07, 0x43 + * }; + * Ssl::Fingerprints fingerprints; + * fingerprints.setSha1_P(sha1Fingerprint, sizeof(sha1Fingerprint)); + * session.validators.add(fingerprints); + * } * */ -struct SslFingerprints { +struct Fingerprints { const uint8_t* certSha1 = nullptr; ///< certificate SHA1 fingerprint const uint8_t* pkSha256 = nullptr; ///< public key SHA256 fingerprint - ~SslFingerprints() + ~Fingerprints() { free(); } @@ -87,10 +98,10 @@ struct SslFingerprints { } /** @brief Moves values out of source */ - SslFingerprints& operator=(SslFingerprints& source); + Fingerprints& operator=(Fingerprints& source); /** @brief Make copy of values from source */ - SslFingerprints& operator=(const SslFingerprints& source); + Fingerprints& operator=(const Fingerprints& source); private: /** @brief Internal method to set a fingerprint @@ -102,3 +113,21 @@ struct SslFingerprints { */ bool setValue(const uint8_t*& value, unsigned requiredLength, const uint8_t* newValue, unsigned newLength); }; + +} // namespace Ssl + +/** + * @deprecated Use Ssl::FingerprintType instead + * @{ + */ +typedef Ssl::FingerprintType SslFingerprintType SMING_DEPRECATED; +typedef Ssl::FingerprintType SSLFingerprintType SMING_DEPRECATED; +/** @} */ + +/** + * @deprecated Use Ssl::Fingerprints instead + * @{ + */ +typedef Ssl::Fingerprints SslFingerprints SMING_DEPRECATED; +typedef Ssl::Fingerprints SSLFingerprints SMING_DEPRECATED; +/** @} */ diff --git a/Sming/Components/ssl/include/Network/Ssl/InputBuffer.h b/Sming/Components/ssl/include/Network/Ssl/InputBuffer.h new file mode 100644 index 0000000000..beb25105d6 --- /dev/null +++ b/Sming/Components/ssl/include/Network/Ssl/InputBuffer.h @@ -0,0 +1,39 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * InputBuffer.h + * + ****/ + +#pragma once + +#include + +namespace Ssl +{ +/** + * @brief Wraps a pbuf for reading in chunks + */ +class InputBuffer +{ +public: + InputBuffer(pbuf* buf) : buf(buf) + { + } + + size_t available() const + { + return buf ? (buf->tot_len - offset) : 0; + } + + size_t read(uint8_t* buffer, size_t bufSize); + +private: + pbuf* buf; + uint16_t offset = 0; +}; + +} // namespace Ssl diff --git a/Sming/Core/Network/Ssl/SslKeyCertPair.h b/Sming/Components/ssl/include/Network/Ssl/KeyCertPair.h similarity index 63% rename from Sming/Core/Network/Ssl/SslKeyCertPair.h rename to Sming/Components/ssl/include/Network/Ssl/KeyCertPair.h index a14b29e298..f855cc818b 100644 --- a/Sming/Core/Network/Ssl/SslKeyCertPair.h +++ b/Sming/Components/ssl/include/Network/Ssl/KeyCertPair.h @@ -4,23 +4,23 @@ * http://github.com/SmingHub/Sming * All files of the Sming Core are provided under the LGPL v3 license. * - * SslKeyCertPair.h + * KeyCertPair.h * ****/ #pragma once -#include "ssl/ssl.h" #include "WString.h" +namespace Ssl +{ /** * @brief Class to manage an SSL key certificate with optional password - * @ingroup ssl */ -class SslKeyCertPair +class KeyCertPair { public: - bool isValid() + bool isValid() const { return key && certificate; } @@ -33,43 +33,25 @@ class SslKeyCertPair * @param newKeyPassword * @retval bool false on memory allocation failure * @note We take a new copy of the certificate + * @{ */ bool assign(const uint8_t* newKey, unsigned newKeyLength, const uint8_t* newCertificate, - unsigned newCertificateLength, const char* newKeyPassword = nullptr) + unsigned newCertificateLength, const char* newKeyPassword = nullptr); + + bool assign(String newKey, String newCertificate, const char* newKeyPassword = nullptr) { - free(); - - if(newKeyLength != 0 && newKey != nullptr) { - if(!key.setLength(newKeyLength)) { - return false; - } - memcpy(key.begin(), newKey, newKeyLength); - } - - if(newCertificateLength != 0 && newCertificate != nullptr) { - if(!certificate.setLength(newCertificateLength)) { - return false; - } - memcpy(certificate.begin(), newCertificate, newCertificateLength); - } - - unsigned passwordLength = (newKeyPassword == nullptr) ? 0 : strlen(newKeyPassword); - if(passwordLength != 0) { - keyPassword.setString(newKeyPassword, passwordLength); - if(!keyPassword) { - return false; - } - } - - return true; + key = newKey; + certificate = newCertificate; + return key && certificate && setPassword(newKeyPassword); } + /** @} */ /** @brief Assign another certificate to this structure * @param keyCert * @retval bool false on memory allocation failure * @note We take a new copy of the certificate */ - bool assign(const SslKeyCertPair& keyCert) + bool assign(const KeyCertPair& keyCert) { *this = keyCert; return (key == keyCert.key) && (keyPassword == keyCert.keyPassword) && (certificate == keyCert.certificate); @@ -82,33 +64,46 @@ class SslKeyCertPair certificate = nullptr; } - const uint8_t* getKey() + const uint8_t* getKey() const { return reinterpret_cast(key.c_str()); } - unsigned getKeyLength() + unsigned getKeyLength() const { return key.length(); } - const char* getKeyPassword() + const char* getKeyPassword() const { return keyPassword.c_str(); } - const uint8_t* getCertificate() + const uint8_t* getCertificate() const { return reinterpret_cast(certificate.c_str()); } - unsigned getCertificateLength() + unsigned getCertificateLength() const { return certificate.length(); } +private: + bool setPassword(const char* newKeyPassword); + private: String key; String keyPassword; String certificate; }; + +} // namespace Ssl + +/** + * @deprecated Use Ssl::KeyCertPair instead + * @{ + */ +typedef Ssl::KeyCertPair SslKeyCertPair SMING_DEPRECATED; +typedef Ssl::KeyCertPair SSLKeyCertPair SMING_DEPRECATED; +/** @} */ diff --git a/Sming/Components/ssl/include/Network/Ssl/Session.h b/Sming/Components/ssl/include/Network/Ssl/Session.h new file mode 100644 index 0000000000..20ce51c24d --- /dev/null +++ b/Sming/Components/ssl/include/Network/Ssl/Session.h @@ -0,0 +1,187 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Session.h + * + ****/ + +#pragma once + +#include "Context.h" +#include "KeyCertPair.h" +#include "Validator.h" +#include + +class TcpConnection; + +namespace Ssl +{ +/** + * @brief Indicate to SSL how much memory (approximately) to commit for buffers + * + * A remote SSL server may require data transfers in large (16K) fragments, + * so restricting buffer sizes may cause connections to such servers to fail. + * + * This must be balanced against other requirements for RAM by the application, + * therefore this setting can be used to restrict RAM usage. + * + * @note The ordinal value of this enumeration corresponds to SSL fragment size as defined in + * Maximum Fragment Length Negotiation https://tools.ietf.org/html/rfc6066 + */ +enum class MaxBufferSize { + Default = 0, ///< Let SSL implementation decide + B512, ///< 512 bytes + K1, ///< 1024 bytes + K2, + K4, + K8, + K16, +}; + +__forceinline size_t maxBufferSizeToBytes(MaxBufferSize value) +{ + return (value == MaxBufferSize::Default) ? 0 : 256U << size_t(value); +} + +/** + * @brief Configurable options + */ +struct Options { + bool sessionResume : 1; ///< Keep a note of session ID for later re-use + bool clientAuthentication : 1; + bool verifyLater : 1; ///< Allow handshake to complete before verifying certificate + bool freeKeyCertAfterHandshake : 1; + + Options() : sessionResume(false), clientAuthentication(false), verifyLater(false), freeKeyCertAfterHandshake(false) + { + } + + String toString() const; +}; + +/** + * @brief Handles all SSL activity for a TCP connection + * + * Each TCP connection creates a session at a suitable + * ManagerTop-level layer for SSL Contains configuration for each SSL connection + */ +class Session +{ +public: + using InitDelegate = Delegate; + + String hostName; ///< Used for SNI https://en.wikipedia.org/wiki/Server_Name_Indication + KeyCertPair keyCert; + Options options; + MaxBufferSize maxBufferSize = MaxBufferSize::Default; + /** + * Server: Number of cached client sessions. Suggested value: 10 + * Client: Number of cached session ids. Suggested value: 1 + */ + int cacheSize = 10; + // client + ValidatorList validators; + +public: + ~Session() + { + close(); + delete sessionId; + } + + const SessionId* getSessionId() const + { + return sessionId; + } + + /** + * @brief Called when a client connection is made via server TCP socket + * @brief client The client TCP socket + * @brief tcp The low-level TCP connection to use for reading and writing + */ + bool onAccept(TcpConnection* client, tcp_pcb* tcp); + + /** + * @brief Called by TcpConnection to set the established SSL connection + */ + void setConnection(Connection* connection) + { + assert(this->connection == nullptr); + this->connection = connection; + } + + /** + * @brief Get the currently active SSL connection object + */ + Connection* getConnection() + { + return connection; + } + + /** + * @brief Handle connection event + * @retval err_t + */ + bool onConnect(tcp_pcb* tcp); + + /** + * @brief Determine if an SSL connection has been fully established + */ + bool isConnected() const + { + return connection ? connection->isHandshakeDone() : false; + } + + /** + * @brief End the session + */ + void close(); + + /** + * @brief Read data from SSL connection + * @param input Source encrypted data + * @param output Points to decrypted content + * @retval int Size of decrypted data returned, or negative on error + */ + int read(InputBuffer& input, uint8_t*& output); + + /** + * @brief Write data to SSL connection + * @param data + * @param length + * @retval int Quantity of bytes actually written, or tcp error code + */ + int write(const uint8_t* data, size_t length); + + /** + * @brief Called by SSL adapter when certificate validation is required + * @retval bool true if validation is success, false to abort connection + */ + bool validateCertificate(); + + /** + * @brief Called by SSL adapter when handshake has been completed + * @param success Indicates if handshake was successful + */ + void handshakeComplete(bool success); + + /** + * @brief For debugging + */ + size_t printTo(Print& p) const; + +private: + void beginHandshake(); + void endHandshake(); + +private: + Context* context = nullptr; + Connection* connection = nullptr; + SessionId* sessionId = nullptr; + CpuFrequency curFreq = CpuFrequency(0); +}; + +}; // namespace Ssl diff --git a/Sming/Core/Network/Ssl/SslSessionId.h b/Sming/Components/ssl/include/Network/Ssl/SessionId.h similarity index 55% rename from Sming/Core/Network/Ssl/SslSessionId.h rename to Sming/Components/ssl/include/Network/Ssl/SessionId.h index 8e10bb6d9b..335cbb7592 100644 --- a/Sming/Core/Network/Ssl/SslSessionId.h +++ b/Sming/Components/ssl/include/Network/Ssl/SessionId.h @@ -4,33 +4,34 @@ * http://github.com/SmingHub/Sming * All files of the Sming Core are provided under the LGPL v3 license. * - * SslSessionId.h + * SessionId.h * ****/ #pragma once -#include "ssl/ssl.h" #include "WString.h" +#include +namespace Ssl +{ /** * @brief Manages buffer to store SSL Session ID - * @ingroup ssl */ -class SslSessionId +class SessionId { public: - const uint8_t* getValue() + const uint8_t* getValue() const { return reinterpret_cast(value.c_str()); } - unsigned getLength() + unsigned getLength() const { return value.length(); } - bool isValid() + bool isValid() const { return getLength() != 0; } @@ -44,6 +45,29 @@ class SslSessionId return true; } + /** + * @brief Return a string representation of the session ID + */ + String toString() const + { + return makeHexString(getValue(), getLength()); + } + + operator String() const + { + return toString(); + } + private: String value; }; + +} // namespace Ssl + +/** + * @deprecated Use Ssl::SessionId instead + * @{ + */ +typedef Ssl::SessionId SslSessionId SMING_DEPRECATED; +typedef Ssl::SessionId SSLSessionId SMING_DEPRECATED; +/** @} */ diff --git a/Sming/Components/ssl/include/Network/Ssl/Validator.h b/Sming/Components/ssl/include/Network/Ssl/Validator.h new file mode 100644 index 0000000000..5f4d9403ea --- /dev/null +++ b/Sming/Components/ssl/include/Network/Ssl/Validator.h @@ -0,0 +1,87 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Validator.h + * + * @author: 2018 - Slavey Karadzhov + * + ****/ + +#pragma once + +#include +#include + +#include "Fingerprints.h" +#include "Certificate.h" + +namespace Ssl +{ +/** + * @brief Defines a validator instance + */ +struct Validator { + /** @brief Validator callback function + * @param ssl Contains certificate to validate (may be NULL) + * @param data Data for the callback to use + * @retval bool true if validation succeeded + * @note Callback must ALWAYS release any allocate memory before returning. + * If called with certificate = NULL then just release memory and return false. + */ + using Callback = Delegate; + + Callback callback; + void* data; ///< Callback-specific data, e.g. fingerprint to compare against +}; + +/** + * @brief List of validators to perform certificate checking + */ +class ValidatorList : private Vector +{ +public: + ~ValidatorList() + { + // Make sure memory gets released + validate(nullptr); + } + + bool add(Validator::Callback callback, void* data) + { + return Vector::add(Validator{callback, data}); + } + + /** + * @brief Add a standard fingerprint validator + * @param fingerprint The fingerprint data against which the match should be performed. + * Must be allocated on the heap and will be deleted after use. + * @param type The fingerprint type - see FingerprintType for details. + * @retval bool true on success, false on failure + */ + bool add(const uint8_t* fingerprint, FingerprintType type); + + /** + * @brief Add validators for standard fingerprints + * @param fingerprints Will be invalid after returning as data is moved rather than copied + * @retval bool true on success, false on failure + */ + bool add(Fingerprints& fingerprints); + + /** @brief Used to validate certificate by invoking each validator callback until successful + * @param certificate When called with nullptr will simply de-allocate any validator memory + * @retval bool true on success, false on failure + */ + bool validate(const Certificate* certificate); + + using Vector::count; +}; + +} // namespace Ssl + +/** + * @deprecated Use `Ssl::Validator::Callback` instead + */ +typedef Ssl::Validator::Callback SslValidatorCallback SMING_DEPRECATED; diff --git a/Sming/Components/ssl/include/SslDebug.h b/Sming/Components/ssl/include/SslDebug.h new file mode 100644 index 0000000000..12ceda12a0 --- /dev/null +++ b/Sming/Components/ssl/include/SslDebug.h @@ -0,0 +1,21 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * SslDebug.h + * + * #include this file ahead of others to enable informational and debug messages only when SSL_DEBUG is defined. + * + ****/ + +#pragma once + +#if !defined(SSL_DEBUG) && (DEBUG_VERBOSE_LEVEL >= 2) +#undef DEBUG_VERBOSE_LEVEL +#define DEBUG_VERBOSE_LEVEL 1 +#endif + +#include +#include diff --git a/Sming/Components/ssl/index.rst b/Sming/Components/ssl/index.rst new file mode 100644 index 0000000000..fb81128cf8 --- /dev/null +++ b/Sming/Components/ssl/index.rst @@ -0,0 +1,25 @@ +SSL: Secure Sockets Layer +========================= + +https://en.m.wikipedia.org/wiki/Transport_Layer_Security + +Configuration Variables +----------------------- + +Sming supports multiple SSL implementations. +At the moment there are SSL adapters based on `axTLS `__ and `BearSSL `__ +If you want to use SSL then take a look at the :sample:`Basic_Ssl` for creating SSL clients and :sample:`HttpServer_ConfigNetwork` +for SSL servers. + +.. envvar:: ENABLE_SSL + + - 0 (default): SSL requires lots of RAM and some intensive processing, so to conserve resources it is disabled by default. + - 1: to enable the default SSL implementation. At the moment that is Axtls. + - Axtls: to enable SSL support using the :component:`axtls-8266` component. + - Bearssl: to enable SSL support using the :component:`bearssl-esp8266` component. + +.. toctree:: + :glob: + :maxdepth: 1 + + * diff --git a/Sming/Components/ssl/src/Alert.cpp b/Sming/Components/ssl/src/Alert.cpp new file mode 100644 index 0000000000..3c792a9a43 --- /dev/null +++ b/Sming/Components/ssl/src/Alert.cpp @@ -0,0 +1,30 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Alert.cpp + * + ****/ + +#include +#include + +namespace Ssl +{ +#define XX(tag, code) DEFINE_FSTR_LOCAL(alertStr_##tag, #tag) +SSL_ALERT_CODE_MAP(XX) +#undef XX + +#define XX(tag, code) {Alert::tag, &alertStr_##tag}, +DEFINE_FSTR_MAP_LOCAL(alertCodeMap, Alert, FSTR::String, SSL_ALERT_CODE_MAP(XX)); +#undef XX + +String getAlertString(Alert alert) +{ + auto s = String(alertCodeMap[alert]); + return s ?: F("ALERT_") + String(unsigned(alert)); +} + +} // namespace Ssl diff --git a/Sming/Components/ssl/src/Certificate.cpp b/Sming/Components/ssl/src/Certificate.cpp new file mode 100644 index 0000000000..6134993f47 --- /dev/null +++ b/Sming/Components/ssl/src/Certificate.cpp @@ -0,0 +1,45 @@ +#include +#include + +namespace Ssl +{ +#define XX(tag, a, b, c, d) DEFINE_FSTR_LOCAL(rdnStr_##tag, #tag) +SSL_X509_RDN_OID_MAP(XX) +#undef XX + +#define XX(tag, a, b, c, d) &rdnStr_##tag, +DEFINE_FSTR_VECTOR_LOCAL(rdnStrings, FSTR::String, SSL_X509_RDN_OID_MAP(XX)); +#undef XX + +String Certificate::getRdnTypeString(Certificate::RDN rdn) +{ + return rdnStrings[unsigned(rdn)]; +} + +size_t Certificate::printTo(Print& p) const +{ + size_t n = 0; + + auto printName = [&](DN dn) { + for(unsigned i = 0; i < unsigned(RDN::MAX); ++i) { + auto rdn = RDN(i); + auto s = getName(dn, rdn); + if(!s) { + continue; + } + n += p.print(" "); + n += p.print(getRdnTypeString(rdn)); + n += p.print(": "); + n += p.println(s); + } + }; + + n += p.println(_F("Subject:")); + printName(DN::SUBJECT); + n += p.println(_F("Issuer:")); + printName(DN::ISSUER); + + return n; +} + +} // namespace Ssl diff --git a/Sming/Components/ssl/src/CipherSuite.cpp b/Sming/Components/ssl/src/CipherSuite.cpp new file mode 100644 index 0000000000..e7c3b8ac02 --- /dev/null +++ b/Sming/Components/ssl/src/CipherSuite.cpp @@ -0,0 +1,36 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * CipherSuite.cpp + * + ****/ + +#include +#include + +namespace Ssl +{ +#define XX(tag, code) DEFINE_FSTR_LOCAL(cipherSuite_##tag, #tag) +SSL_CIPHER_SUITE_MAP(XX) +#undef XX + +#define XX(tag, code) {CipherSuite::tag, &cipherSuite_##tag}, +DEFINE_FSTR_MAP_LOCAL(cipherSuiteNames, CipherSuite, FSTR::String, SSL_CIPHER_SUITE_MAP(XX)); +#undef XX + +String getCipherSuiteName(CipherSuite id) +{ + auto entry = cipherSuiteNames[id]; + if(entry) { + return String(entry); + } + + char buf[32]; + auto len = m_snprintf(buf, sizeof(buf), _F("{ 0x%04X }"), unsigned(id)); + return String(buf, len); +} + +} // namespace Ssl diff --git a/Sming/Components/ssl/src/Connection.cpp b/Sming/Components/ssl/src/Connection.cpp new file mode 100644 index 0000000000..aa5d17120c --- /dev/null +++ b/Sming/Components/ssl/src/Connection.cpp @@ -0,0 +1,77 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Connection.cpp + * + ****/ + +#include +#include +#include + +namespace Ssl +{ +size_t Connection::printTo(Print& p) const +{ + size_t n = 0; + auto cert = getCertificate(); + if(cert != nullptr) { + n += cert->printTo(p); + } + n += p.println(_F("SSL Connection Information:")); + n += p.print(_F(" Cipher: ")); + n += p.println(getCipherSuiteName(getCipherSuite())); + n += p.print(_F(" Session ID: ")); + n += p.println(getSessionId()); + return n; +} + +int Connection::writeTcpData(uint8_t* data, size_t length) +{ + if(data == nullptr || length == 0) { + debug_w("writeTcpData: Return Zero."); + return 0; + } + + size_t tcp_len = tcp_sndbuf(tcp); + if(tcp_len < length) { + if(tcp_len == 0) { + tcp_output(tcp); + debug_d("writeTcpData: Send buffer full, will retry later."); + return 0; + } + } else { + tcp_len = length; + } + + if(tcp_len > 2 * tcp->mss) { + tcp_len = 2 * tcp->mss; + } + + err_t err; + while((err = tcp_write(tcp, data, tcp_len, TCP_WRITE_FLAG_COPY)) == ERR_MEM) { + debug_e("writeTcpData: Not enough memory to write data with length: %d (%d)", tcp_len, length); + tcp_len /= 2; + if(tcp_len <= 1) { + tcp_len = 0; + break; + } + } + + if(err == ERR_OK) { + debug_d("writeTcpData: length %d (%d)", tcp_len, length); + err = tcp_output(tcp); + if(err != ERR_OK) { + debug_e("writeTcpData: tcp_output got err: %d", err); + } + } else { + debug_e("writeTcpData: Got error: %d", err); + } + + return tcp_len; +} + +} // namespace Ssl diff --git a/Sming/Components/ssl/src/Fingerprints.cpp b/Sming/Components/ssl/src/Fingerprints.cpp new file mode 100644 index 0000000000..86ba4a0bd2 --- /dev/null +++ b/Sming/Components/ssl/src/Fingerprints.cpp @@ -0,0 +1,84 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Fingerprints.cpp + * + ****/ + +#include +#include + +namespace Ssl +{ +static void freeValue(const uint8_t*& ptr) +{ + delete[] ptr; + ptr = nullptr; +} + +void Fingerprints::free() +{ + freeValue(certSha1); + freeValue(pkSha256); +} + +bool Fingerprints::setValue(const uint8_t*& value, unsigned requiredLength, const uint8_t* newValue, unsigned newLength) +{ + if(newValue == nullptr || newLength == 0) { + freeValue(value); + return true; + } + + if(newLength != requiredLength) { + debug_w("Warning: Invalid fingerprint length"); + // Copy data anyway to prevent false positive validation + } + + if(value == nullptr) { + value = new uint8_t[requiredLength]; + if(value == nullptr) { + return false; + } + } + + // If new value is longer than buffer, copy short + unsigned length = std::min(newLength, requiredLength); + // Behave properly when source is flash memory and length is wrong or buffers misaligned + if(isFlashPtr(newValue)) { + memcpy_P(const_cast(value), newValue, length); + } else { + memcpy(const_cast(value), newValue, length); + } + + return true; +} + +Fingerprints& Fingerprints::operator=(Fingerprints& source) +{ + if(this != &source) { + freeValue(certSha1); + certSha1 = source.certSha1; + source.certSha1 = nullptr; + + freeValue(pkSha256); + pkSha256 = source.pkSha256; + source.pkSha256 = nullptr; + } + + return *this; +} + +Fingerprints& Fingerprints::operator=(const Fingerprints& source) +{ + if(this != &source) { + setSha1(source.certSha1, SHA1_SIZE); + setSha256(source.pkSha256, SHA256_SIZE); + } + + return *this; +} + +} // namespace Ssl diff --git a/Sming/Components/ssl/src/InputBuffer.cpp b/Sming/Components/ssl/src/InputBuffer.cpp new file mode 100644 index 0000000000..bf61bc0969 --- /dev/null +++ b/Sming/Components/ssl/src/InputBuffer.cpp @@ -0,0 +1,32 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * InputBuffer.cpp + * + ****/ + +#include +#include + +namespace Ssl +{ +size_t InputBuffer::read(uint8_t* buffer, size_t bufSize) +{ + if(buf == nullptr) { + return 0; + } + + unsigned len = pbuf_copy_partial(buf, buffer, bufSize, offset); + offset += len; + + if(len < bufSize) { + debug_d("SSL read input: Bytes needed: %d, Bytes read: %u", bufSize, len); + } + + return len; +} + +} // namespace Ssl diff --git a/Sming/Components/ssl/src/KeyCertPair.cpp b/Sming/Components/ssl/src/KeyCertPair.cpp new file mode 100644 index 0000000000..0de627b986 --- /dev/null +++ b/Sming/Components/ssl/src/KeyCertPair.cpp @@ -0,0 +1,49 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * KeyCertPair.cpp + * + ****/ + +#include + +namespace Ssl +{ +bool KeyCertPair::assign(const uint8_t* newKey, unsigned newKeyLength, const uint8_t* newCertificate, + unsigned newCertificateLength, const char* newKeyPassword) +{ + free(); + + if(newKeyLength != 0 && newKey != nullptr) { + if(!key.setLength(newKeyLength)) { + return false; + } + memcpy(key.begin(), newKey, newKeyLength); + } + + if(newCertificateLength != 0 && newCertificate != nullptr) { + if(!certificate.setLength(newCertificateLength)) { + return false; + } + memcpy(certificate.begin(), newCertificate, newCertificateLength); + } + + return setPassword(newKeyPassword); +} + +bool KeyCertPair::setPassword(const char* newKeyPassword) +{ + unsigned passwordLength = (newKeyPassword == nullptr) ? 0 : strlen(newKeyPassword); + if(passwordLength == 0) { + keyPassword = nullptr; + return true; + } + + keyPassword.setString(newKeyPassword, passwordLength); + return keyPassword; +} + +} // namespace Ssl diff --git a/Sming/Components/ssl/src/Session.cpp b/Sming/Components/ssl/src/Session.cpp new file mode 100644 index 0000000000..584ea6fc0d --- /dev/null +++ b/Sming/Components/ssl/src/Session.cpp @@ -0,0 +1,241 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Session.cpp + * + ****/ + +#include +#include +#include +#include +#include + +namespace Ssl +{ +String Options::toString() const +{ + String s; + +#define ADD(field) \ + if(field) { \ + if(s) { \ + s += ", "; \ + s += _F(#field); \ + } \ + } + + ADD(sessionResume); + ADD(clientAuthentication); + ADD(verifyLater); + ADD(freeKeyCertAfterHandshake); + +#undef ADD + + return s; +} + +bool Session::onAccept(TcpConnection* client, tcp_pcb* tcp) +{ + debug_i("SSL %p onAccept(%p, %p)", this, client, tcp); + + if(!keyCert.isValid()) { + debug_e("SSL: server certificate and key are not provided!"); + return false; + } + + if(context == nullptr) { + assert(factory != nullptr); + context = factory->createContext(*this); + if(context == nullptr) { + return false; + } + + if(!context->init()) { + return false; + } + + // TODO: test: free the certificate data on server destroy... + options.freeKeyCertAfterHandshake = true; + } + + beginHandshake(); + + auto server = context->createServer(tcp); + return client->setSslConnection(server); +} + +bool Session::onConnect(tcp_pcb* tcp) +{ + debug_d("SSL %p: Starting connection...", this); + + assert(connection == nullptr); + assert(context == nullptr); + + // Client Session + delete context; + assert(factory != nullptr); + context = factory->createContext(*this); + if(context == nullptr) { + return false; + } + + cacheSize = 1; + + if(!context->init()) { + return false; + } + + if(sessionId != nullptr && sessionId->isValid()) { + debug_d("-----BEGIN SSL SESSION PARAMETERS-----"); + debug_d("SessionId: %s", sessionId->toString().c_str()); + debug_d("------END SSL SESSION PARAMETERS------"); + } + + beginHandshake(); + + connection = context->createClient(tcp); + if(connection == nullptr) { + endHandshake(); + return false; + } + + return true; +} + +void Session::beginHandshake() +{ + debug_d("SSL: handshake start"); +#ifndef SSL_SLOW_CONNECT + curFreq = System.getCpuFrequency(); + if(curFreq != eCF_160MHz) { + debug_d("SSL: Switching to 160 MHz"); + System.setCpuFrequency(eCF_160MHz); // For shorter waiting time, more power consumption. + } +#endif +} + +void Session::endHandshake() +{ +#ifndef SSL_SLOW_CONNECT + if(curFreq != System.getCpuFrequency()) { + debug_d("SSL: Switching back to %u MHz", curFreq); + System.setCpuFrequency(curFreq); + } +#endif + debug_d("SSL: Handshake done"); +} + +void Session::close() +{ + debug_d("SSL %p: closing ...", this); + + delete connection; + connection = nullptr; + + delete context; + context = nullptr; + + hostName = nullptr; + maxBufferSize = MaxBufferSize::Default; +} + +int Session::read(InputBuffer& input, uint8_t*& output) +{ + assert(connection != nullptr); + int len = connection->read(input, output); + if(len < 0) { + debug_w("SSL: Got error: %d (%s)", len, connection->getErrorString(len).c_str()); + auto alert = connection->getAlert(len); + if(alert == Alert::CERTIFICATE_UNKNOWN) { + debug_w("SSL: Client didn't like certificate, continue anyway"); + len = ERR_OK; + } + } + + return len; +} + +int Session::write(const uint8_t* data, size_t length) +{ + if(connection == nullptr) { + debug_e("!! SSL Session connection is NULL"); + return ERR_CONN; + } + + int res = connection->write(data, length); + if(res < 0) { + debug_w("SSL: write returned %d (%s)", res, connection->getErrorString(res).c_str()); + return ERR_BUF; + } + + return res; +} + +bool Session::validateCertificate() +{ + if(connection == nullptr) { + debug_w("SSL: connection not set, assuming cert. is OK"); + return true; + } + + if(validators.validate(connection->getCertificate())) { + debug_i("SSL validation passed, heap free = %u", system_get_free_heap_size()); + return true; + } + + debug_w("SSL Validation failed"); + return false; +} + +void Session::handshakeComplete(bool success) +{ + endHandshake(); + + if(success) { + // If requested, take a copy of the session ID for later re-use + if(options.sessionResume) { + if(sessionId == nullptr) { + sessionId = new SessionId; + } + *sessionId = connection->getSessionId(); + } + } else { + debug_w("SSL Handshake failed"); + } + + if(options.freeKeyCertAfterHandshake && connection != nullptr) { + connection->freeCertificate(); + } +} + +size_t Session::printTo(Print& p) const +{ + size_t n = 0; + + n += p.println(_F("SSL Session:")); + n += p.print(_F(" Options: ")); + n += p.println(options.toString()); + n += p.print(_F(" Host name: ")); + n += p.println(hostName); + n += p.print(_F(" Cache Size: ")); + n += p.println(cacheSize); + n += p.print(_F(" Max Buffer Size: ")); + n += p.println(maxBufferSizeToBytes(maxBufferSize)); + n += p.print(_F(" Validators: ")); + n += p.println(validators.count()); + n += p.print(_F(" Cert Length: ")); + n += p.println(keyCert.getCertificateLength()); + n += p.print(_F(" Cert PK Length: ")); + n += p.println(keyCert.getKeyLength()); + if(connection != nullptr) { + n += connection->printTo(p); + } + + return n; +} + +}; // namespace Ssl diff --git a/Sming/Core/Network/Ssl/SslValidator.cpp b/Sming/Components/ssl/src/Validator.cpp similarity index 68% rename from Sming/Core/Network/Ssl/SslValidator.cpp rename to Sming/Components/ssl/src/Validator.cpp index f86793d450..10383153f8 100644 --- a/Sming/Core/Network/Ssl/SslValidator.cpp +++ b/Sming/Components/ssl/src/Validator.cpp @@ -4,22 +4,24 @@ * http://github.com/SmingHub/Sming * All files of the Sming Core are provided under the LGPL v3 license. * - * SslValidator.cpp + * Validator.cpp * * @author: 2018 - Slavey Karadzhov * ****/ -#include "SslValidator.h" +#include #include -static bool sslValidateCertificateSha1(SSL* ssl, void* data) +namespace Ssl +{ +static bool validateCertificateSha1(const Certificate* certificate, void* data) { uint8_t* hash = static_cast(data); bool success = false; if(hash != nullptr) { - if(ssl != nullptr) { - success = (ssl_match_fingerprint(ssl, hash) == 0); + if(certificate != nullptr) { + success = certificate->matchFingerprint(hash); } delete[] hash; } @@ -27,13 +29,13 @@ static bool sslValidateCertificateSha1(SSL* ssl, void* data) return success; } -static bool sslValidatePublicKeySha256(SSL* ssl, void* data) +static bool validatePublicKeySha256(const Certificate* certificate, void* data) { uint8_t* hash = static_cast(data); bool success = false; if(hash != nullptr) { - if(ssl != nullptr) { - success = (ssl_match_spki_sha256(ssl, hash) == 0); + if(certificate != nullptr) { + success = certificate->matchPki(hash); } delete[] hash; } @@ -41,11 +43,9 @@ static bool sslValidatePublicKeySha256(SSL* ssl, void* data) return success; } -/* SslValidatorList */ - -bool SslValidatorList::validate(SSL* ssl) +bool ValidatorList::validate(const Certificate* certificate) { - if(ssl != nullptr && count() == 0) { + if(certificate != nullptr && count() == 0) { // No validators specified, always succeed debug_d("SSL Validator: list empty, allow connection"); return true; @@ -59,7 +59,7 @@ bool SslValidatorList::validate(SSL* ssl) for(unsigned i = 0; i < count(); i++) { auto& validator = operator[](i); // If we've already succeeded, then just release validator data without checking - if(validator.callback(success ? nullptr : ssl, validator.data)) { + if(validator.callback(success ? nullptr : certificate, validator.data)) { debug_d("SSL validator: positive match"); success = true; } @@ -67,22 +67,22 @@ bool SslValidatorList::validate(SSL* ssl) validator.data = nullptr; } - if(ssl != nullptr && !success) { + if(certificate != nullptr && !success) { debug_d("SSL validator: NO match"); } return success; } -bool SslValidatorList::add(const uint8_t* fingerprint, SslFingerprintType type) +bool ValidatorList::add(const uint8_t* fingerprint, FingerprintType type) { - SslValidatorCallback callback = nullptr; + Validator::Callback callback = nullptr; switch(type) { case eSFT_CertSha1: - callback = sslValidateCertificateSha1; + callback = validateCertificateSha1; break; case eSFT_PkSha256: - callback = sslValidatePublicKeySha256; + callback = validatePublicKeySha256; break; default: debug_d("Unsupported SSL certificate fingerprint type"); @@ -96,7 +96,7 @@ bool SslValidatorList::add(const uint8_t* fingerprint, SslFingerprintType type) return add(callback, const_cast(fingerprint)); } -bool SslValidatorList::add(SslFingerprints& fingerprints) +bool ValidatorList::add(Fingerprints& fingerprints) { bool success = false; if(fingerprints.certSha1 != nullptr) { @@ -111,3 +111,5 @@ bool SslValidatorList::add(SslFingerprints& fingerprints) return success; } + +} // namespace Ssl diff --git a/Sming/Core/Network/Http/HttpClientConnection.cpp b/Sming/Core/Network/Http/HttpClientConnection.cpp index a42da1655a..e047132a1a 100644 --- a/Sming/Core/Network/Http/HttpClientConnection.cpp +++ b/Sming/Core/Network/Http/HttpClientConnection.cpp @@ -17,7 +17,7 @@ #include "Data/Stream/ChunkedStream.h" #include "Data/Stream/UrlencodedOutputStream.h" -bool HttpClientConnection::connect(const String& host, int port, bool useSsl, uint32_t sslOptions) +bool HttpClientConnection::connect(const String& host, int port, bool useSsl) { debug_d("HttpClientConnection::connect: TCP state: %d, isStarted: %d, isActive: %d", (tcp != nullptr ? tcp->state : -1), (int)(getConnectionState() != eTCS_Ready), (int)isActive()); @@ -36,7 +36,7 @@ bool HttpClientConnection::connect(const String& host, int port, bool useSsl, ui debug_d("HttpClientConnection::connecting ..."); - return TcpClient::connect(host, port, useSsl, sslOptions); + return TcpClient::connect(host, port, useSsl); } bool HttpClientConnection::send(HttpRequest* request) @@ -49,19 +49,6 @@ bool HttpClientConnection::send(HttpRequest* request) } bool useSsl = (request->uri.Scheme == URI_SCHEME_HTTP_SECURE); - -#ifdef ENABLE_SSL - // Based on the URL decide if we should reuse the SSL and TCP pool - if(useSsl) { - if(sslSessionId == nullptr) { - sslSessionId = new SslSessionId; - } - addSslOptions(request->getSslOptions()); - pinCertificate(request->sslFingerprints); - setSslKeyCert(request->sslKeyCertPair); - } -#endif - return connect(request->uri.Host, request->uri.getPort(), useSsl); } diff --git a/Sming/Core/Network/Http/HttpClientConnection.h b/Sming/Core/Network/Http/HttpClientConnection.h index 20af68eca3..513a60b069 100644 --- a/Sming/Core/Network/Http/HttpClientConnection.h +++ b/Sming/Core/Network/Http/HttpClientConnection.h @@ -39,13 +39,9 @@ class HttpClientConnection : public HttpConnection while(waitingQueue.count() != 0) { delete waitingQueue.dequeue(); } - -#ifdef ENABLE_SSL - delete sslSessionId; -#endif } - bool connect(const String& host, int port, bool useSsl = false, uint32_t sslOptions = 0) override; + bool connect(const String& host, int port, bool useSsl = false) override; bool send(HttpRequest* request) override; @@ -69,6 +65,15 @@ class HttpClientConnection : public HttpConnection void cleanup() override; + void sslInitSession(Ssl::Session& session) override + { + TcpClient::sslInitSession(session); + auto request = waitingQueue.peek(); + if(request != nullptr && request->sslInitDelegate) { + request->sslInitDelegate(session, *request); + } + } + private: void sendRequestHeaders(HttpRequest* request); bool sendRequestBody(HttpRequest* request); diff --git a/Sming/Core/Network/Http/HttpRequest.cpp b/Sming/Core/Network/Http/HttpRequest.cpp index 8c9cbbd3f0..165d2c70cd 100644 --- a/Sming/Core/Network/Http/HttpRequest.cpp +++ b/Sming/Core/Network/Http/HttpRequest.cpp @@ -13,17 +13,6 @@ #include "HttpRequest.h" #include "Data/Stream/MemoryDataStream.h" -HttpRequest::HttpRequest(const HttpRequest& value) - : uri(value.uri), method(value.method), headers(value.headers), postParams(value.postParams), - headersCompletedDelegate(value.headersCompletedDelegate), requestBodyDelegate(value.requestBodyDelegate), - requestCompletedDelegate(value.requestCompletedDelegate) -#ifdef ENABLE_SSL - , - sslOptions(value.sslOptions), sslFingerprints(value.sslFingerprints), sslKeyCertPair(value.sslKeyCertPair) -#endif -{ -} - String HttpRequest::getBody() { if(bodyStream == nullptr || bodyStream->getStreamType() != eSST_Memory) { @@ -46,11 +35,6 @@ String HttpRequest::getBody() return ret; } -IDataSourceStream* HttpRequest::getBodyStream() -{ - return bodyStream; -} - HttpRequest* HttpRequest::setResponseStream(ReadWriteStream* stream) { if(responseStream != nullptr) { @@ -103,17 +87,6 @@ void HttpRequest::reset() String HttpRequest::toString() { String content; -#ifdef ENABLE_SSL - content += F("> SSL options: ") + String(sslOptions) + '\n'; - content += - F("> SSL Cert Fingerprint Length: ") + String((sslFingerprints.certSha1 == nullptr) ? 0 : SHA1_SIZE) + '\n'; - content += - F("> SSL PK Fingerprint Length: ") + String((sslFingerprints.pkSha256 == nullptr) ? 0 : SHA256_SIZE) + '\n'; - content += F("> SSL ClientCert Length: ") + String(sslKeyCertPair.getCertificateLength()) + '\n'; - content += F("> SSL ClientCert PK Length: ") + String(sslKeyCertPair.getKeyLength()) + '\n'; - content += '\n'; -#endif - content += String(http_method_str(method)) + ' ' + uri.getPathWithQuery() + _F(" HTTP/1.1\n"); content += headers.toString(HTTP_HEADER_HOST, uri.getHostWithPort()); for(unsigned i = 0; i < headers.count(); i++) { diff --git a/Sming/Core/Network/Http/HttpRequest.h b/Sming/Core/Network/Http/HttpRequest.h index de0001e761..8c86b901e2 100644 --- a/Sming/Core/Network/Http/HttpRequest.h +++ b/Sming/Core/Network/Http/HttpRequest.h @@ -17,6 +17,7 @@ #include "HttpRequestAuth.h" #endif #include "../TcpConnection.h" +#include #include "Data/Stream/DataSourceStream.h" #include "Data/Stream/MultipartStream.h" #include "HttpHeaders.h" @@ -50,7 +51,12 @@ class HttpRequest * @brief Copy constructor * @note Internal streams are not copied so these must be dealt with afterwards */ - HttpRequest(const HttpRequest& value); + HttpRequest(const HttpRequest& value) + : uri(value.uri), method(value.method), headers(value.headers), postParams(value.postParams), + headersCompletedDelegate(value.headersCompletedDelegate), requestBodyDelegate(value.requestBodyDelegate), + requestCompletedDelegate(value.requestCompletedDelegate), sslInitDelegate(value.sslInitDelegate) + { + } /** * @brief Clone this request into a new object using the copy constructor @@ -175,7 +181,10 @@ class HttpRequest * @retval IDataSourceStream* * @note may return null */ - IDataSourceStream* getBodyStream(); + IDataSourceStream* getBodyStream() + { + return bodyStream; + } HttpRequest* setBody(const String& body) { @@ -225,43 +234,20 @@ class HttpRequest /** @brief Clear buffers and reset to default state in preparation for another request */ void reset(); -#ifdef ENABLE_SSL - HttpRequest* setSslOptions(uint32_t sslOptions) - { - this->sslOptions = sslOptions; - return this; - } - - uint32_t getSslOptions() - { - return sslOptions; - } - /** - * @brief Requires(pins) the remote SSL certificate to match certain fingerprints - * Check if SHA256 hash of Subject Public Key Info matches the one given. - * @param fingerprints - passes the certificate fingerprints by reference. - * - * @retval bool true of success, false or failure + * @brief Callback delegate type used to initialise an SSL session for a given request */ - HttpRequest* pinCertificate(SslFingerprints& fingerprints) - { - sslFingerprints = fingerprints; - return this; - } + using SslInitDelegate = Delegate; /** - * @brief Sets client private key, certificate and password from memory - * @param keyCertPair - * - * @retval HttpRequest* Pointer to this request + * @brief To customise SSL session options, provide a callback + * @param delegate Invoked before creating SSL connection */ - HttpRequest* setSslKeyCert(const SslKeyCertPair& keyCertPair) + HttpRequest* onSslInit(SslInitDelegate delegate) { - sslKeyCertPair = keyCertPair; + sslInitDelegate = delegate; return this; } -#endif #ifndef SMING_RELEASE /** @@ -286,6 +272,7 @@ class HttpRequest RequestHeadersCompletedDelegate headersCompletedDelegate; RequestBodyDelegate requestBodyDelegate; RequestCompletedDelegate requestCompletedDelegate; + SslInitDelegate sslInitDelegate; IDataSourceStream* bodyStream = nullptr; ReadWriteStream* responseStream = nullptr; ///< User-requested stream to store response @@ -294,12 +281,6 @@ class HttpRequest AuthAdapter* auth = nullptr; #endif -#ifdef ENABLE_SSL - uint32_t sslOptions = 0; - SslFingerprints sslFingerprints; - SslKeyCertPair sslKeyCertPair; -#endif - private: HttpParams* queryParams = nullptr; // << @todo deprecate }; diff --git a/Sming/Core/Network/HttpServer.cpp b/Sming/Core/Network/HttpServer.cpp index 6bb71345d3..50dc6d4f72 100644 --- a/Sming/Core/Network/HttpServer.cpp +++ b/Sming/Core/Network/HttpServer.cpp @@ -33,9 +33,6 @@ void HttpServer::configure(const HttpServerSettings& settings) } setKeepAlive(settings.keepAliveSeconds); -#ifdef ENABLE_SSL - sslSessionCacheSize = settings.sslSessionCacheSize; -#endif } TcpConnection* HttpServer::createClient(tcp_pcb* clientTcp) diff --git a/Sming/Core/Network/HttpServer.h b/Sming/Core/Network/HttpServer.h index 19835d8c9e..46b211f4a6 100644 --- a/Sming/Core/Network/HttpServer.h +++ b/Sming/Core/Network/HttpServer.h @@ -31,10 +31,6 @@ typedef struct { bool useDefaultBodyParsers = 1; ///< if the default body parsers, as form-url-encoded, should be used bool closeOnContentError = true; ///< close the connection if a body parser or resource fails to parse the body content. -#ifdef ENABLE_SSL - int sslSessionCacheSize = - 10; ///< number of SSL session ids to cache. Setting this to 0 will disable SSL session resumption. -#endif } HttpServerSettings; class HttpServer : public TcpServer diff --git a/Sming/Core/Network/MqttClient.cpp b/Sming/Core/Network/MqttClient.cpp index f3b4f309b5..9325126e94 100644 --- a/Sming/Core/Network/MqttClient.cpp +++ b/Sming/Core/Network/MqttClient.cpp @@ -209,7 +209,7 @@ bool MqttClient::setWill(const String& topic, const String& message, uint8_t fla copyString(connectMessage.connect.will_message, message); } -bool MqttClient::connect(const Url& url, const String& clientName, uint32_t sslOptions) +bool MqttClient::connect(const Url& url, const String& clientName) { this->url = url; bool useSsl = (url.Scheme == URI_SCHEME_MQTT_SECURE); @@ -251,7 +251,7 @@ bool MqttClient::connect(const Url& url, const String& clientName, uint32_t sslO } connectQueued = true; - return TcpClient::connect(url.Host, url.getPort(), useSsl, sslOptions); + return TcpClient::connect(url.Host, url.getPort(), useSsl); } bool MqttClient::publish(const String& topic, const String& content, uint8_t flags) diff --git a/Sming/Core/Network/MqttClient.h b/Sming/Core/Network/MqttClient.h index d7125708cf..7f4cd451a1 100644 --- a/Sming/Core/Network/MqttClient.h +++ b/Sming/Core/Network/MqttClient.h @@ -88,10 +88,9 @@ class MqttClient : protected TcpClient /** @brief Connect to a MQTT server * @param url URL in the form "mqtt://user:password@server:port" or "mqtts://user:password@server:port" * @param uniqueClientName - * @param sslOptions * @retval bool */ - bool connect(const Url& url, const String& uniqueClientName, uint32_t sslOptions = 0); + bool connect(const Url& url, const String& uniqueClientName); bool publish(const String& topic, const String& message, uint8_t flags = 0); bool publish(const String& topic, IDataSourceStream* stream, uint8_t flags = 0); @@ -159,21 +158,17 @@ class MqttClient : protected TcpClient TcpClient::setCompleteDelegate(handler); } -#ifdef ENABLE_SSL - using TcpClient::addSslOptions; - using TcpClient::addSslValidator; - using TcpClient::freeSslKeyCert; using TcpClient::getSsl; - using TcpClient::pinCertificate; - using TcpClient::setSslKeyCert; -#endif + using TcpClient::setSslInitHandler; - // deprecated methods below using TcpClient::setCompleteDelegate; using TcpClient::getConnectionState; using TcpClient::isProcessing; + using TcpClient::getRemoteIp; + using TcpClient::getRemotePort; + #ifndef MQTT_NO_COMPAT /** * @todo deprecate: Use setWill(const String& topic, const String& message,uint8_t flags) instead diff --git a/Sming/Core/Network/SmtpClient.cpp b/Sming/Core/Network/SmtpClient.cpp index 5550d8b457..b5cd8de2be 100644 --- a/Sming/Core/Network/SmtpClient.cpp +++ b/Sming/Core/Network/SmtpClient.cpp @@ -18,18 +18,10 @@ #include "SmtpClient.h" #include "WebHelpers/base64.h" -#include "Data/Stream/QuotedPrintableOutputStream.h" -#include "Data/Stream/Base64OutputStream.h" -#include "Data/HexString.h" - -#if !defined(ENABLE_SSL) || ENABLE_SSL == 0 -// if our SSL is not used then we try to use the one coming from the SDK -#define MD5_SIZE 16 -extern "C" { -void ssl_hmac_md5(const uint8_t* msg, int length, const uint8_t* key, int key_len, uint8_t* digest); -} -#define hmac_md5(A, B, C, D, E) ssl_hmac_md5(A, B, C, D, E) -#endif +#include +#include +#include +#include #define ADVANCE \ { \ @@ -424,12 +416,9 @@ int SmtpClient::smtpParse(char* buffer, size_t len) if(isLastLine) { state = eSMTP_Ready; -#ifdef ENABLE_SSL if(!useSsl && (options & SMTP_OPT_STARTTLS)) { state = eSMTP_StartTLS; - } else -#endif - if(url.User && authMethods.count()) { + } else if(url.User && authMethods.count()) { state = eSMTP_SendAuth; } } diff --git a/Sming/Core/Network/SmtpClient.h b/Sming/Core/Network/SmtpClient.h index 6f44e79a9a..d10c8a8206 100644 --- a/Sming/Core/Network/SmtpClient.h +++ b/Sming/Core/Network/SmtpClient.h @@ -165,16 +165,9 @@ class SmtpClient : protected TcpClient errorCallback = callback; } - using TcpClient::setTimeOut; - -#ifdef ENABLE_SSL - using TcpClient::addSslOptions; - using TcpClient::addSslValidator; - using TcpClient::freeSslKeyCert; using TcpClient::getSsl; - using TcpClient::pinCertificate; - using TcpClient::setSslKeyCert; -#endif + using TcpClient::setSslInitHandler; + using TcpClient::setTimeOut; protected: err_t onReceive(pbuf* buf) override; diff --git a/Sming/Core/Network/Ssl/SslFingerprints.cpp b/Sming/Core/Network/Ssl/SslFingerprints.cpp deleted file mode 100644 index ac675825a5..0000000000 --- a/Sming/Core/Network/Ssl/SslFingerprints.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * SslFingerprints.cpp - * - ****/ - -#ifdef ENABLE_SSL - -#include "SslFingerprints.h" -#include - -static inline void freeValue(const uint8_t*& ptr) -{ - delete[] ptr; - ptr = nullptr; -} - -void SslFingerprints::free() -{ - freeValue(certSha1); - freeValue(pkSha256); -} - -bool SslFingerprints::setValue(const uint8_t*& value, unsigned requiredLength, const uint8_t* newValue, - unsigned newLength) -{ - if(newValue == nullptr || newLength == 0) { - freeValue(value); - return true; - } else { - if(newLength != requiredLength) { - debug_w("Warning: Invalid fingerprint length"); - // Copy data anyway to prevent false positive validation - } - if(value == nullptr) { - value = new uint8_t[requiredLength]; - if(value == nullptr) { - return false; - } - } - // If new value is longer than buffer, copy short - unsigned length = std::min(newLength, requiredLength); - // Behave properly when source is flash memory and length is wrong or buffers misaligned - if(isFlashPtr(newValue)) { - memcpy_P(const_cast(value), newValue, length); - } else { - memcpy(const_cast(value), newValue, length); - } - return true; - } -} - -SslFingerprints& SslFingerprints::operator=(SslFingerprints& source) -{ - if(this != &source) { - freeValue(certSha1); - certSha1 = source.certSha1; - source.certSha1 = nullptr; - - freeValue(pkSha256); - pkSha256 = source.pkSha256; - source.pkSha256 = nullptr; - } - - return *this; -} - -/** @brief Make copy of values from source */ -SslFingerprints& SslFingerprints::operator=(const SslFingerprints& source) -{ - if(this != &source) { - setSha1(source.certSha1, SHA1_SIZE); - setSha256(source.pkSha256, SHA256_SIZE); - } - - return *this; -} - -#endif // ENABLE_SSL diff --git a/Sming/Core/Network/Ssl/SslStructs.h b/Sming/Core/Network/Ssl/SslStructs.h deleted file mode 100644 index 7ddb6d6fad..0000000000 --- a/Sming/Core/Network/Ssl/SslStructs.h +++ /dev/null @@ -1,28 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * SslStructs.h - * - ****/ - -#pragma once - -#include "SslFingerprints.h" -#include "SslKeyCertPair.h" -#include "SslSessionId.h" - -/** - * @defgroup ssl SSL Support - * @ingroup tcp - */ - -/* - * These structures have been renamed, please use the revised convention SslXXX - */ -typedef SslKeyCertPair SSLKeyCertPair SMING_DEPRECATED; ///< @deprecated Use SslKeyCertPair instead -typedef SslSessionId SSLSessionId SMING_DEPRECATED; ///< @deprecated Use SslSessionId instead -typedef SslFingerprints SSLFingerprints SMING_DEPRECATED; ///< @deprecated Use SslFingerprints instead -typedef SslFingerprintType SSLFingerprintType SMING_DEPRECATED; ///< @deprecated Use SslFingerprintType instead diff --git a/Sming/Core/Network/Ssl/SslValidator.h b/Sming/Core/Network/Ssl/SslValidator.h deleted file mode 100644 index 8df3819615..0000000000 --- a/Sming/Core/Network/Ssl/SslValidator.h +++ /dev/null @@ -1,79 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * SslValidator.h - * - * @author: 2018 - Slavey Karadzhov - * - ****/ - -#pragma once - -#include -#include - -#include -#include - -#include "SslFingerprints.h" - -/** - * @ingroup ssl - * @{ - */ - -/** @brief Validator callback function - * @param ssl Contains certificate to validate (may be NULL) - * @param data Data for the callback to use - * @retval bool true if validation succeeded - * @note Callback must ALWAYS release any allocate memory before returning. - * If called with ssl = NULL then just release memory and return false. - */ -typedef Delegate SslValidatorCallback; - -struct SslValidator { - SslValidatorCallback callback; - void* data; ///< Callback-specific data, e.g. fingerprint to compare against -}; - -class SslValidatorList : private Vector -{ -public: - ~SslValidatorList() - { - // Make sure memory gets released - validate(nullptr); - } - - bool add(SslValidatorCallback callback, void* data) - { - return Vector::add(SslValidator{callback, data}); - } - - /** - * @brief Add a standard fingerprint validator - * @param fingerprint The fingerprint data against which the match should be performed. - * Must be allocated on the heap and will be deleted after use. - * @param type The fingerprint type - see SslFingerprintType for details. - * @retval bool true on success, false on failure - */ - bool add(const uint8_t* fingerprint, SslFingerprintType type); - - /** - * @brief Add validators for standard fingerprints - * @param fingerprints Will be invalid after returning as data is moved rather than copied - * @retval bool true on success, false on failure - */ - bool add(SslFingerprints& fingerprints); - - /** @brief Used to validate certificate by invoking each validator callback until successful - * @param ssl When called with nullptr will simply de-allocate any validator memory - * @retval bool true on success, false on failure - */ - bool validate(SSL* ssl); -}; - -/** @} */ diff --git a/Sming/Core/Network/TcpClient.cpp b/Sming/Core/Network/TcpClient.cpp index 13677d61c4..bec3ca2bbd 100644 --- a/Sming/Core/Network/TcpClient.cpp +++ b/Sming/Core/Network/TcpClient.cpp @@ -32,24 +32,24 @@ void TcpClient::setBuffer(ReadWriteStream* stream) this->stream = buffer; } -bool TcpClient::connect(const String& server, int port, bool useSsl, uint32_t sslOptions) +bool TcpClient::connect(const String& server, int port, bool useSsl) { if(isProcessing()) { return false; } state = eTCS_Connecting; - return TcpConnection::connect(server.c_str(), port, useSsl, sslOptions); + return TcpConnection::connect(server.c_str(), port, useSsl); } -bool TcpClient::connect(IpAddress addr, uint16_t port, bool useSsl, uint32_t sslOptions) +bool TcpClient::connect(IpAddress addr, uint16_t port, bool useSsl) { if(isProcessing()) { return false; } state = eTCS_Connecting; - return TcpConnection::connect(addr, port, useSsl, sslOptions); + return TcpConnection::connect(addr, port, useSsl); } bool TcpClient::send(const char* data, uint16_t len, bool forceCloseAfterSent) @@ -138,12 +138,7 @@ void TcpClient::onReadyToSendData(TcpConnectionEvent sourceEvent) void TcpClient::close() { if(state != eTCS_Successful && state != eTCS_Failed) { - state = (totalSentConfirmedBytes == totalSentBytes) ? eTCS_Successful : eTCS_Failed; -#ifdef ENABLE_SSL - if(ssl && sslConnected) { - state = (totalSentBytes == 0 || (totalSentConfirmedBytes > totalSentBytes)) ? eTCS_Successful : eTCS_Failed; - } -#endif + state = (totalSentConfirmedBytes >= totalSentBytes) ? eTCS_Successful : eTCS_Failed; totalSentBytes = 0; totalSentConfirmedBytes = 0; onFinished(state); @@ -164,13 +159,7 @@ void TcpClient::pushAsyncPart() if(stream->isFinished()) { debug_d("TcpClient stream finished"); freeStreams(); - - if(getAvailableWriteSize() > 0) { - // if there is space in the output buffer - // then don't wait for tcp sent confirmation and try sending more data now - onReadyToSendData(TcpConnectionEvent::eTCE_Poll); - } - + trySend(eTCE_Poll); flush(); } } diff --git a/Sming/Core/Network/TcpClient.h b/Sming/Core/Network/TcpClient.h index 2a8b9ace33..89d5eb731b 100644 --- a/Sming/Core/Network/TcpClient.h +++ b/Sming/Core/Network/TcpClient.h @@ -18,10 +18,6 @@ #include "TcpConnection.h" -#ifdef ENABLE_SSL -#include "Ssl/SslValidator.h" -#endif - class TcpClient; class ReadWriteStream; class IpAddress; @@ -30,8 +26,19 @@ typedef Delegate TcpCli typedef Delegate TcpClientCompleteDelegate; typedef Delegate TcpClientDataDelegate; -enum TcpClientState { eTCS_Ready, eTCS_Connecting, eTCS_Connected, eTCS_Successful, eTCS_Failed }; -enum TcpClientCloseAfterSentState { eTCCASS_None, eTCCASS_AfterSent, eTCCASS_AfterSent_Ignore_Received }; +enum TcpClientState { + eTCS_Ready, + eTCS_Connecting, + eTCS_Connected, + eTCS_Successful, + eTCS_Failed, +}; + +enum TcpClientCloseAfterSentState { + eTCCASS_None, + eTCCASS_AfterSent, + eTCCASS_AfterSent_Ignore_Received, +}; // By default a TCP client connection has 70 seconds timeout #define TCP_CLIENT_TIMEOUT 70 @@ -73,9 +80,8 @@ class TcpClient : public TcpConnection freeStreams(); } -public: - bool connect(const String& server, int port, bool useSsl = false, uint32_t sslOptions = 0) override; - bool connect(IpAddress addr, uint16_t port, bool useSsl = false, uint32_t sslOptions = 0) override; + bool connect(const String& server, int port, bool useSsl = false) override; + bool connect(IpAddress addr, uint16_t port, bool useSsl = false) override; void close() override; /** @brief Set or clear the callback for received data @@ -120,49 +126,6 @@ class TcpClient : public TcpConnection closeAfterSent = ignoreIncomingData ? eTCCASS_AfterSent_Ignore_Received : eTCCASS_AfterSent; } -#ifdef ENABLE_SSL - /** - * @brief Allows setting of multiple SSL validators after a successful handshake - * @param callback The callback function to be invoked on validation - * @param data The data to pass to the callback - * @note The callback is responsible for releasing the data if appropriate. - * See SslValidatorCallback for further details. - * - * @retval bool true on success, false on failure - */ - bool addSslValidator(SslValidatorCallback callback, void* data = nullptr) - { - return sslValidators.add(callback, data); - } - - /** - * @brief Requires (pins) the remote SSL certificate to match certain fingerprints - * @param fingerprint The fingerprint data against which the match should be performed. - * Must be allocated on the heap and will be deleted after use. - * Do not re-use outside of this method. - * @param type The fingerprint type - see SslFingerprintType for details. - * - * @retval bool true on success, false on failure - */ - bool pinCertificate(const uint8_t* fingerprint, SslFingerprintType type) - { - return sslValidators.add(fingerprint, type); - } - - /** - * @brief Requires (pins) the remote SSL certificate to match certain fingerprints - * @note The data inside the fingerprints parameter is passed by reference - * @param fingerprints - passes the certificate fingerprints by reference. - * - * @retval bool true on success, false on failure - */ - bool pinCertificate(SslFingerprints& fingerprints) - { - return sslValidators.add(fingerprints); - } - -#endif - protected: err_t onConnected(err_t err) override; err_t onReceive(pbuf* buf) override; @@ -172,14 +135,6 @@ class TcpClient : public TcpConnection virtual void onFinished(TcpClientState finishState); -#ifdef ENABLE_SSL - err_t onSslConnected(SSL* ssl) override - { - return sslValidators.validate(ssl) ? ERR_OK : ERR_ABRT; - } - -#endif - void pushAsyncPart(); void freeStreams(); @@ -191,16 +146,13 @@ class TcpClient : public TcpConnection private: TcpClientState state = eTCS_Ready; - TcpClientCompleteDelegate completed = nullptr; - TcpClientEventDelegate ready = nullptr; - TcpClientDataDelegate receive = nullptr; + TcpClientCompleteDelegate completed; + TcpClientEventDelegate ready; + TcpClientDataDelegate receive; TcpClientCloseAfterSentState closeAfterSent = eTCCASS_None; uint16_t totalSentConfirmedBytes = 0; uint16_t totalSentBytes = 0; -#ifdef ENABLE_SSL - SslValidatorList sslValidators; -#endif }; /** @} */ diff --git a/Sming/Core/Network/TcpConnection.cpp b/Sming/Core/Network/TcpConnection.cpp index 4c463fe505..d517ff57ed 100644 --- a/Sming/Core/Network/TcpConnection.cpp +++ b/Sming/Core/Network/TcpConnection.cpp @@ -9,16 +9,21 @@ ****/ #include "TcpConnection.h" +#include #include -#include #include "NetUtils.h" #include +#define debug_tcp_e(fmt, ...) debug_e("TCP %p " fmt, this, ##__VA_ARGS__) +#define debug_tcp_w(fmt, ...) debug_w("TCP %p " fmt, this, ##__VA_ARGS__) +#define debug_tcp_i(fmt, ...) debug_i("TCP %p " fmt, this, ##__VA_ARGS__) +#define debug_tcp_d(fmt, ...) debug_d("TCP %p " fmt, this, ##__VA_ARGS__) + #ifdef DEBUG_TCP_EXTENDED -#define debug_tcp(fmt, ...) debug_d(fmt, ##__VA_ARGS__) +#define debug_tcp_ext debug_tcp_d(fmt, ##__VA_ARGS__) #else -#define debug_tcp(fmt, ...) debug_none(fmt, ##__VA_ARGS__) +#define debug_tcp_ext(fmt, ...) debug_none(fmt, ##__VA_ARGS__) #endif TcpConnection::~TcpConnection() @@ -26,14 +31,36 @@ TcpConnection::~TcpConnection() autoSelfDestruct = false; close(); - debug_d("~TCP connection"); + delete ssl; + + debug_tcp_d("~connection"); if(destroyedDelegate) { destroyedDelegate(*this); } } -bool TcpConnection::connect(const String& server, int port, bool useSsl, uint32_t sslOptions) +bool TcpConnection::sslCreateSession() +{ + if(ssl != nullptr) { + return true; + } + + if(Ssl::factory == nullptr) { + debug_tcp_e("SSL required, no factory"); + return false; + } + + ssl = new Ssl::Session; + if(ssl == nullptr) { + return false; + } + + sslInitSession(*ssl); + return true; +} + +bool TcpConnection::connect(const String& server, int port, bool useSsl) { if(tcp == nullptr) { initialize(tcp_new()); @@ -42,18 +69,14 @@ bool TcpConnection::connect(const String& server, int port, bool useSsl, uint32_ ip_addr_t addr; this->useSsl = useSsl; -#ifdef ENABLE_SSL - this->sslOptions |= sslOptions; - if(useSsl) { - ssl_ext_free(sslExtension); - sslExtension = ssl_ext_new(); - ssl_ext_set_host_name(sslExtension, server.c_str()); - ssl_ext_set_max_fragment_size(sslExtension, 4); // 4K max size + if(!sslCreateSession()) { + return false; + } + ssl->hostName = server; } -#endif - debug_d("connect to: %s", server.c_str()); + debug_tcp_d("connect to \"%s\"", server.c_str()); canSend = false; // Wait for connection struct DnsLookup { @@ -79,35 +102,33 @@ bool TcpConnection::connect(const String& server, int port, bool useSsl, uint32_ return (dnslook == ERR_OK) ? internalConnect(addr, port) : false; } -bool TcpConnection::connect(IpAddress addr, uint16_t port, bool useSsl, uint32_t sslOptions) +bool TcpConnection::connect(IpAddress addr, uint16_t port, bool useSsl) { if(tcp == nullptr) { initialize(tcp_new()); } this->useSsl = useSsl; -#ifdef ENABLE_SSL - this->sslOptions |= sslOptions; -#endif + if(useSsl && !sslCreateSession()) { + return false; + } return internalConnect(addr, port); } void TcpConnection::setTimeOut(uint16_t waitTimeOut) { - debug_d("timeout updating: %d -> %d", timeOut, waitTimeOut); + debug_tcp_d("timeout updating: %d -> %d", timeOut, waitTimeOut); timeOut = waitTimeOut; } err_t TcpConnection::onReceive(pbuf* buf) { if(buf == nullptr) { - debug_d("TCP received: (null)"); + debug_tcp_d("received: (null)"); } else { - debug_d("TCP received: %d bytes", buf->tot_len); - if(getAvailableWriteSize() > 0) { - onReadyToSendData(eTCE_Received); - } + debug_tcp_d("received: %d bytes", buf->tot_len); + trySend(eTCE_Received); } return ERR_OK; @@ -115,14 +136,11 @@ err_t TcpConnection::onReceive(pbuf* buf) err_t TcpConnection::onSent(uint16_t len) { - debug_d("TCP sent: %d", len); + debug_tcp_d("sent: %u", len); if(tcp != nullptr) { - debug_tcp("%d %d", tcp->state, tcp->flags); // WRONG! - - if(getAvailableWriteSize() > 0) { - onReadyToSendData(eTCE_Sent); - } + debug_tcp_ext("%d %d", tcp->state, tcp->flags); // WRONG! + trySend(eTCE_Sent); } return ERR_OK; @@ -131,15 +149,13 @@ err_t TcpConnection::onSent(uint16_t len) err_t TcpConnection::onPoll() { if(sleep >= timeOut && timeOut != USHRT_MAX) { - debug_d("TCP connection closed by timeout: %d (from %d)", sleep, timeOut); + debug_tcp_d("connection closed by timeout: %d (from %d)", sleep, timeOut); close(); return ERR_TIMEOUT; } - if(tcp != nullptr && getAvailableWriteSize() > 0) { //(tcp->state >= SYN_SENT && tcp->state <= ESTABLISHED)) - onReadyToSendData(eTCE_Poll); - } + trySend(eTCE_Poll); return ERR_OK; } @@ -147,14 +163,14 @@ err_t TcpConnection::onPoll() err_t TcpConnection::onConnected(err_t err) { if(err != ERR_OK) { - debug_d("TCP connected error status: %d", err); + debug_tcp_d("connected error status: %d", err); } else { - debug_d("TCP connected"); + debug_tcp_d("connected"); } canSend = true; if(err == ERR_OK) { - onReadyToSendData(eTCE_Connected); + trySend(eTCE_Connected); } else { close(); } @@ -164,123 +180,88 @@ err_t TcpConnection::onConnected(err_t err) void TcpConnection::onError(err_t err) { -#ifdef ENABLE_SSL - closeSsl(); -#endif - debug_d("TCP connection error: %d", err); + if(ssl != nullptr) { + ssl->close(); + } + debug_tcp_d("connection error: %d", err); } void TcpConnection::onReadyToSendData(TcpConnectionEvent sourceEvent) { if(sourceEvent != eTCE_Poll) { - debug_d("TCP onReadyToSendData: %d", sourceEvent); + debug_tcp_d("onReadyToSendData: %d", sourceEvent); } } -#ifdef ENABLE_SSL -err_t TcpConnection::onSslConnected(SSL* ssl) -{ - return ERR_OK; -} -#endif - int TcpConnection::write(const char* data, int len, uint8_t apiflags) { - WDT.alive(); + err_t err; - err_t err = ERR_OK; - -#ifdef ENABLE_SSL - if(ssl) { - int expected = ssl_calculate_write_length(ssl, len); - u16_t available = tcp ? tcp_sndbuf(tcp) : 0; - debug_tcp("SSL: Expected: %d, Available: %d", expected, available); - if(expected < 0 || available < expected) { - return -1; // No memory - } - - int written = axl_ssl_write(ssl, (const uint8_t*)data, len); - debug_tcp("SSL: Write len: %d, Written: %d", len, written); - if(written < ERR_OK) { - err = written; - debug_d("SSL: Write Error: %d", err); - } + if(ssl != nullptr) { + len = ssl->write(reinterpret_cast(data), len); + err = (len < 0) ? len : ERR_OK; } else { -#endif u16_t available = getAvailableWriteSize(); if(available < len) { if(available == 0) { - return -1; // No memory - } else { - len = available; + return ERR_MEM; } + + len = available; } - err = tcp_write(tcp, data, len, apiflags); -#ifdef ENABLE_SSL + err = tcp_write(tcp, data, len, apiflags); } -#endif - if(err == ERR_OK) { - debug_tcp("TCP connection send: %d (%d)", len, original); - return len; - } else { - debug_tcp("TCP connection failed with err %d (\"%s\")", err, lwip_strerr(err)); - return -1; + if(err < 0) { + debug_tcp_ext("connection failed with err %d (\"%s\")", err, lwip_strerr(err)); + return err; } + + debug_tcp_ext("connection send: %d", len); + return len; } int TcpConnection::write(IDataSourceStream* stream) { -#ifdef ENABLE_SSL - if(ssl != nullptr && !sslConnected) { + if(ssl != nullptr && !ssl->isConnected()) { // wait until the SSL handshake is done. return 0; } -#endif // Send data from DataStream - bool repeat; - bool space; - int available; - int total = 0; - char buffer[NETWORK_SEND_BUFFER_SIZE]; - - do { - space = (tcp_sndqueuelen(tcp) < TCP_SND_QUEUELEN); - if(!space) { - debug_d("WAIT FOR FREE SPACE"); - flush(); - break; // don't try to send buffers if no free space available + size_t total = 0; + unsigned pushCount = 0; + while((tcp_sndqueuelen(tcp) < TCP_SND_QUEUELEN) && !stream->isFinished() && (pushCount < 25)) { + size_t available = getAvailableWriteSize(); + if(available == 0) { + break; } - // Join small fragments - int pushCount = 0; - do { - pushCount++; - int read = std::min((uint16_t)NETWORK_SEND_BUFFER_SIZE, getAvailableWriteSize()); - if(read > 0) { - available = stream->readMemoryBlock(buffer, read); - } else { - available = 0; - } + char buffer[NETWORK_SEND_BUFFER_SIZE]; + auto bytesRead = stream->readMemoryBlock(buffer, std::min(sizeof(buffer), available)); + if(bytesRead == 0) { + break; + } - if(available > 0) { - int written = write(buffer, available, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE); - total += written; - stream->seek(std::max(written, 0)); - debug_d("TCP Written: %d, Available: %d, isFinished: %d, PushCount: %d [TcpBuf: %d]", written, - available, (stream->isFinished() ? 1 : 0), pushCount, tcp_sndbuf(tcp)); - repeat = written == available && !stream->isFinished() && pushCount < 25; - } else { - repeat = false; - } - } while(repeat); + ++pushCount; - space = (tcp_sndqueuelen(tcp) < TCP_SND_QUEUELEN); // && tcp_sndbuf(tcp) >= FILE_STREAM_BUFFER_SIZE; - } while(repeat && space); + int bytesWritten = write(buffer, bytesRead, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE); + debug_tcp_d("Written: %d, Available: %u, isFinished: %d, PushCount: %u", bytesWritten, available, + stream->isFinished(), pushCount); + if(bytesWritten <= 0) { + continue; + } - if(!space) { + if(bytesWritten > 0) { + total += size_t(bytesWritten); + stream->seek(bytesWritten); + } + } + + if(pushCount == 0) { + debug_tcp_d("WAIT FOR FREE SPACE"); + } else { flush(); } @@ -289,18 +270,14 @@ int TcpConnection::write(IDataSourceStream* stream) void TcpConnection::close() { -#ifdef ENABLE_SSL - closeSsl(); -#endif + if(ssl != nullptr) { + ssl->close(); + } if(tcp == nullptr) { return; } - debug_d("TCP connection closing"); - -#ifdef ENABLE_SSL - axl_free(tcp); -#endif + debug_tcp_d("connection closing"); tcp_poll(tcp, staticOnPoll, 1); tcp_arg(tcp, nullptr); // reset pointer to close connection on next callback @@ -314,9 +291,6 @@ void TcpConnection::initialize(tcp_pcb* pcb) tcp = pcb; sleep = 0; canSend = true; -#ifdef ENABLE_SSL - axl_init(10); -#endif tcp_nagle_disable(tcp); tcp_arg(tcp, this); @@ -353,7 +327,7 @@ void TcpConnection::initialize(tcp_pcb* pcb) tcp_poll(tcp, staticOnPoll, 4); #ifdef NETWORK_DEBUG - debug_d("+TCP connection"); + debug_tcp_d("+connection"); #endif } @@ -383,7 +357,7 @@ void TcpConnection::closeTcpConnection(tcp_pcb* tpcb) void TcpConnection::flush() { if(tcp && tcp->state == ESTABLISHED) { - debug_tcp("TCP flush()"); + debug_tcp_ext("flush()"); tcp_output(tcp); } } @@ -408,79 +382,24 @@ bool TcpConnection::internalConnect(IpAddress addr, uint16_t port) err_t TcpConnection::internalOnConnected(err_t err) { - debug_d("TCP connected"); - -#ifndef ENABLE_SSL - if(useSsl) { - debug_w("WARNING: SSL is not compiled. Make sure to compile Sming with 'make ENABLE_SSL=1' "); - } -#else - debug_d("TCP connected: useSSL: %d, Error: %d", useSsl, err); + debug_tcp_d("connected: useSSL: %d, Error: %d", useSsl, err); if(useSsl && err == ERR_OK) { - int clientfd = axl_append(tcp); - if(clientfd < 0) { - debug_d("SSL: Unable to add LWIP tcp -> clientfd mapping"); + if(ssl == nullptr) { + debug_tcp_e("SSL disabled: aborting connection"); + return ERR_ABRT; + } + if(!ssl->onConnect(tcp)) { + return ERR_ABRT; + } + if(!ssl->isConnected()) { return ERR_OK; - } else { - uint32_t localSslOptions = sslOptions; -#ifdef SSL_DEBUG - localSslOptions |= SSL_DISPLAY_STATES | SSL_DISPLAY_BYTES | SSL_DISPLAY_CERTS; - debug_d("SSL: Show debug data ..."); -#endif - debug_d("SSL: Starting connection..."); -#ifndef SSL_SLOW_CONNECT - debug_d("SSL: Switching to 160 MHz"); - System.setCpuFrequency(eCF_160MHz); // For shorter waiting time, more power consumption. -#endif - debug_d("SSL: handshake start"); - - ssl_ctx_free(sslContext); - sslContext = ssl_ctx_new(SSL_CONNECT_IN_PARTS | localSslOptions, 1); - - if(sslKeyCert.isValid()) { - // if we have client certificate -> try to use it. - if(ssl_obj_memory_load(sslContext, SSL_OBJ_RSA_KEY, sslKeyCert.getKey(), sslKeyCert.getKeyLength(), - sslKeyCert.getKeyPassword()) != SSL_OK) { - debug_d("SSL: Unable to load client private key"); - } else if(ssl_obj_memory_load(sslContext, SSL_OBJ_X509_CERT, sslKeyCert.getCertificate(), - sslKeyCert.getCertificateLength(), nullptr) != SSL_OK) { - debug_d("SSL: Unable to load client certificate"); - } - - if(freeKeyCertAfterHandshake) { - sslKeyCert.free(); - } - } - - if(sslSessionId != nullptr && sslSessionId->isValid()) { - debug_d("-----BEGIN SSL SESSION PARAMETERS-----"); - debug_hex(DBG, "Session", sslSessionId->getValue(), sslSessionId->getLength()); - debug_d("\n-----END SSL SESSION PARAMETERS-----"); - } - - ssl = ssl_client_new(sslContext, clientfd, sslSessionId != nullptr ? sslSessionId->getValue() : nullptr, - sslSessionId != nullptr ? sslSessionId->getLength() : 0, sslExtension); - if(ssl_handshake_status(ssl) != SSL_OK) { - debug_d("SSL: handshake is in progress..."); - return SSL_OK; - } - - if(sslSessionId != nullptr) { - sslSessionId->assign(ssl->session_id, ssl->sess_id_size); - } - -#ifndef SSL_SLOW_CONNECT - debug_d("SSL: Switching back 80 MHz"); - System.setCpuFrequency(eCF_80MHz); -#endif } } -#endif err_t res = onConnected(err); checkSelfFree(); - debug_tcp("tot_len); } else { - debug_d("TCP receive: pbuf is NULL"); + debug_tcp_d("receive: pbuf is NULL"); } -#ifdef ENABLE_SSL if(ssl != nullptr && p != nullptr) { - WDT.alive(); /* SSL handshake needs time. In theory we have max 8 seconds before the hardware watchdog resets the device */ - - struct pbuf* pout; - int read_bytes = axl_ssl_read(ssl, tcp, p, &pout); - - // free the SSL pbuf and put the decrypted data in the brand new pout pbuf - if(p != nullptr) { - pbuf_free(p); - } - - if(read_bytes < SSL_OK) { - debug_d("SSL: Got error: %d", read_bytes); - if(read_bytes == SSL_CLOSE_NOTIFY) { - return ERR_OK; + bool isConnecting = !ssl->isConnected(); + + Ssl::InputBuffer input(p); + + pbuf pbufOut = {}; + while(input.available() > 0) { + uint8_t* output; + int len = ssl->read(input, output); + if(len < 0) { + close(); + closeTcpConnection(tcp); + err = ERR_CONN; + break; } - close(); - closeTcpConnection(tcp); - return read_bytes; - } - - if(read_bytes == 0) { - if(!sslConnected && ssl_handshake_status(ssl) == SSL_OK) { - sslConnected = true; - debug_d("SSL: Handshake done"); -#ifndef SSL_SLOW_CONNECT - debug_d("SSL: Switching back to 80 MHz"); - System.setCpuFrequency(eCF_80MHz); // Preserve some CPU cycles -#endif - if(onSslConnected(ssl) != ERR_OK) { - close(); - closeTcpConnection(tcp); - - return ERR_ABRT; - } - - if(sslSessionId != nullptr) { - sslSessionId->assign(ssl->session_id, ssl->sess_id_size); - } - - err_t res = onConnected(err); - checkSelfFree(); - - return res; + if(isConnecting && ssl->isConnected()) { + err = onConnected(ERR_OK); + } else if(len != 0) { + // Proceed with received decrypted data + pbufOut.payload = output; + pbufOut.tot_len = len; + pbufOut.len = len; + err = onReceive(&pbufOut); } - // No data yet - return ERR_OK; + if(err < 0) { + break; + } } - // we got some decrypted bytes... - debug_d("SSL: Decrypted data len %d", read_bytes); - - // put the decrypted data in a brand new pbuf - p = pout; + } else { + err = onReceive(p); } -#endif - - err_t res = onReceive(p); if(p != nullptr) { pbuf_free(p); @@ -580,8 +472,8 @@ err_t TcpConnection::internalOnReceive(pbuf* p, err_t err) close(); } - debug_tcp(" -#include "Ssl/SslStructs.h" -#endif - +#include #include #define NETWORK_DEBUG @@ -27,14 +23,10 @@ #define NETWORK_SEND_BUFFER_SIZE 1024 enum TcpConnectionEvent { - // Occurs after connection establishment - eTCE_Connected = 0, - // Occurs on data receive - eTCE_Received, - // Occurs when previous sending was completed - eTCE_Sent, - // Occurs on waiting - eTCE_Poll + eTCE_Connected = 0, ///< Occurs after connection establishment + eTCE_Received, ///< Occurs on data receive + eTCE_Sent, //< Occurs when previous sending was completed + eTCE_Poll, //< Occurs on waiting }; struct pbuf; @@ -59,8 +51,8 @@ class TcpConnection virtual ~TcpConnection(); public: - virtual bool connect(const String& server, int port, bool useSsl = false, uint32_t sslOptions = 0); - virtual bool connect(IpAddress addr, uint16_t port, bool useSsl = false, uint32_t sslOptions = 0); + virtual bool connect(const String& server, int port, bool useSsl = false); + virtual bool connect(IpAddress addr, uint16_t port, bool useSsl = false); virtual void close(); // return -1 on error @@ -113,140 +105,74 @@ class TcpConnection this->destroyedDelegate = destroyedDelegate; } -#ifdef ENABLE_SSL - void addSslOptions(uint32_t sslOptions) - { - this->sslOptions |= sslOptions; - } - /** - * @brief Sets client private key, certificate and password from memory - * @deprecated Use `setSslKeyCert(const uint8_t*, int, const uint8_t*, int, const char*, bool)` instead - * - * @note This method makes copy of the data. - * - * @param key - * @param keyLength - * @param certificate - * @param certificateLength - * @param keyPassword - * @param freeAfterHandshake - * - * @return bool true of success, false or failure + * @brief Set the SSL session initialisation callback + * @param handler */ - bool setSslClientKeyCert(const uint8_t* key, int keyLength, const uint8_t* certificate, int certificateLength, - const char* keyPassword = nullptr, bool freeAfterHandshake = false) SMING_DEPRECATED + void setSslInitHandler(Ssl::Session::InitDelegate handler) { - return setSslKeyCert(key, keyLength, certificate, certificateLength, keyPassword, freeAfterHandshake); + sslInit = handler; } - /** - * @brief Sets client private key, certificate and password from memory - * @deprecated Use `setSslKeyCert(const SslKeyCertPair&, bool)` instead - * - * @note This method passes the certificate key chain by reference - * - * @param clientKeyCert - * @param freeAfterHandshake - * - * @return bool true of success, false or failure - */ - bool setSslClientKeyCert(const SslKeyCertPair& clientKeyCert, bool freeAfterHandshake = false) SMING_DEPRECATED + // Called by SSL Session when accepting a client connection + bool setSslConnection(Ssl::Connection* connection) { - return setSslKeyCert(clientKeyCert, freeAfterHandshake); - } - - /** - * @brief Frees the memory used for the key and certificate pair - * @deprecated Use `freeSslKeyCert()` instead - */ - void freeSslClientKeyCert() SMING_DEPRECATED - { - freeSslKeyCert(); + if(!sslCreateSession()) { + return false; + } + ssl->setConnection(connection); + useSsl = true; + return true; } /** - * @brief Sets private key, certificate and password from memory for the SSL connection - * If this methods is called from a client then it sets the client key and certificate - * If it is called from a server then it sets the server certificate and key. - * Server and Client certificates differ. Client certificate is used for identification. - * Server certificate is used for encrypt/decrypt the data. - * Make sure to use the correct certificate for the desired goal. + * @brief Get a pointer to the current SSL session object * - * @note This method makes copy of the data. - * - * @param key - * @param keyLength - * @param certificate - * @param certificateLength - * @param keyPassword - * @param freeAfterHandshake - * - * @return bool true of success, false or failure + * Note that this is typically used so we can query properties of an + * established session. If you need to change session parameters this + * must be done via `setSslInitHandler`. */ - bool setSslKeyCert(const uint8_t* key, int keyLength, const uint8_t* certificate, int certificateLength, - const char* keyPassword = nullptr, bool freeAfterHandshake = false) + Ssl::Session* getSsl() { - freeKeyCertAfterHandshake = freeAfterHandshake; - return sslKeyCert.assign(key, keyLength, certificate, certificateLength, keyPassword); + return ssl; } - /** - * @brief Sets private key, certificate and password from memory for the SSL connection - * If this methods is called from a client then it sets the client key and certificate - * If it is called from a server then it sets the server certificate and key. - * Server and Client certificates differ. Client certificate is used for identification. - * Server certificate is used for encrypt/decrypt the data. - * Make sure to use the correct certificate for the desired goal. - * - * @note This method passes the certificate key chain by reference - * - * @param keyCert - * @param freeAfterHandshake - * - * @retval bool true of success, false or failure - */ - bool setSslKeyCert(const SslKeyCertPair& keyCert, bool freeAfterHandshake = false) - { - freeKeyCertAfterHandshake = freeAfterHandshake; - return sslKeyCert.assign(keyCert); - } +protected: + void initialize(tcp_pcb* pcb); + bool internalConnect(IpAddress addr, uint16_t port); + + bool sslCreateSession(); /** - * @brief Frees the memory used for the key and certificate pair + * @brief Override in inherited classes to perform custom session initialisation + * + * Called when TCP connection is established before initiating handshake. */ - void freeSslKeyCert() - { - sslKeyCert.free(); - } - - // Called by TcpServer - void setSsl(SSL* ssl) + virtual void sslInitSession(Ssl::Session& session) { - this->ssl = ssl; - useSsl = true; - } - - SSL* getSsl() - { - return ssl; + if(sslInit) { + sslInit(session); + } } -#endif - -protected: - void initialize(tcp_pcb* pcb); - bool internalConnect(IpAddress addr, uint16_t port); - virtual err_t onConnected(err_t err); virtual err_t onReceive(pbuf* buf); virtual err_t onSent(uint16_t len); virtual err_t onPoll(); virtual void onError(err_t err); virtual void onReadyToSendData(TcpConnectionEvent sourceEvent); -#ifdef ENABLE_SSL - virtual err_t onSslConnected(SSL* ssl); -#endif + + /* + * If there is space in the TCP output buffer, then don't wait for TCP + * sent confirmation but try to send more data now + * (Invoked from within other TCP callbacks.) + */ + void trySend(TcpConnectionEvent event) + { + if(tcp != nullptr && getAvailableWriteSize() > 0) { + onReadyToSendData(event); + } + } // These methods are called via LWIP handlers err_t internalOnConnected(err_t err); @@ -273,24 +199,12 @@ class TcpConnection uint16_t timeOut = USHRT_MAX; ///< By default a TCP connection does not have a time out bool canSend = true; bool autoSelfDestruct = true; -#ifdef ENABLE_SSL - SSL* ssl = nullptr; - SSLCTX* sslContext = nullptr; - SSL_EXTENSIONS* sslExtension = nullptr; - bool sslConnected = false; - uint32_t sslOptions = 0; - SslKeyCertPair sslKeyCert; - bool freeKeyCertAfterHandshake = false; - SslSessionId* sslSessionId = nullptr; -#endif + Ssl::Session* ssl = nullptr; + Ssl::Session::InitDelegate sslInit; bool useSsl = false; private: TcpConnectionDestroyedDelegate destroyedDelegate = nullptr; - -#ifdef ENABLE_SSL - void closeSsl(); -#endif }; /** @} */ diff --git a/Sming/Core/Network/TcpServer.cpp b/Sming/Core/Network/TcpServer.cpp index f467d69677..2646b137f8 100644 --- a/Sming/Core/Network/TcpServer.cpp +++ b/Sming/Core/Network/TcpServer.cpp @@ -50,38 +50,8 @@ bool TcpServer::listen(int port, bool useSsl) return res; } -#ifdef ENABLE_SSL this->useSsl = useSsl; - if(useSsl) { -#ifdef SSL_DEBUG - sslOptions |= SSL_DISPLAY_STATES | SSL_DISPLAY_BYTES | SSL_DISPLAY_CERTS; -#endif - - sslContext = ssl_ctx_new(sslOptions, sslSessionCacheSize); - - if(!sslKeyCert.isValid()) { - debug_e("SSL: server certificate and key are not provided!"); - return false; - } - - if(ssl_obj_memory_load(sslContext, SSL_OBJ_RSA_KEY, sslKeyCert.getKey(), sslKeyCert.getKeyLength(), - sslKeyCert.getKeyPassword()) != SSL_OK) { - debug_e("SSL: Unable to load server private key"); - return false; - } - - if(ssl_obj_memory_load(sslContext, SSL_OBJ_X509_CERT, sslKeyCert.getCertificate(), - sslKeyCert.getCertificateLength(), nullptr) != SSL_OK) { - debug_e("SSL: Unable to load server certificate"); - return false; - } - - // TODO: test: free the certificate data on server destroy... - freeKeyCertAfterHandshake = true; - } -#endif - tcp = tcp_listen(tcp); tcp_accept(tcp, staticAccept); @@ -93,12 +63,12 @@ err_t TcpServer::onAccept(tcp_pcb* clientTcp, err_t err) { // Anti DDoS :-) if(system_get_free_heap_size() < minHeapSize) { - debug_w("\r\n\r\nCONNECTION DROPPED\r\n\t(%d)\r\n\r\n", system_get_free_heap_size()); + debug_w("\r\n\r\nCONNECTION DROPPED\r\n\t(free heap: %u)\r\n\r\n", system_get_free_heap_size()); return ERR_MEM; } #ifdef NETWORK_DEBUG - debug_d("onAccept state: %d K=%d", err, connections.count()); + debug_d("onAccept, tcp: %p, state: %d K=%d", clientTcp, err, connections.count()); list_mem(); #endif @@ -113,19 +83,16 @@ err_t TcpServer::onAccept(tcp_pcb* clientTcp, err_t err) } client->setTimeOut(keepAlive); -#ifdef ENABLE_SSL if(useSsl) { - int clientfd = axl_append(clientTcp); - if(clientfd == -1) { + if(!sslCreateSession()) { + delete client; + return ERR_ABRT; + } + if(!ssl->onAccept(client, clientTcp)) { delete client; - debug_e("SSL: Unable to initiate tcp "); return ERR_ABRT; } - - debug_d("SSL: handshake start."); - client->setSsl(ssl_server_new(sslContext, clientfd)); } -#endif client->setDestroyedDelegate(TcpConnectionDestroyedDelegate(&TcpServer::onClientDestroy, this)); @@ -213,7 +180,7 @@ void TcpServer::shutdown() void TcpServer::onClientDestroy(TcpConnection& connection) { - connections.removeElement((TcpConnection*)&connection); + connections.removeElement(&connection); debug_d("Destroying connection. Total connections: %d", connections.count()); if(active) { diff --git a/Sming/Core/Network/TcpServer.h b/Sming/Core/Network/TcpServer.h index c13d4fb651..303eb6f77a 100644 --- a/Sming/Core/Network/TcpServer.h +++ b/Sming/Core/Network/TcpServer.h @@ -59,29 +59,12 @@ class TcpServer : public TcpConnection debug_i("TcpServer destroyed"); } -public: virtual bool listen(int port, bool useSsl = false); void setKeepAlive(uint16_t seconds); void shutdown(); -#ifdef ENABLE_SSL - /** - * @brief Adds SSL support and specifies the server certificate and private key. - * @deprecated Use `setSslKeyCert()` instead - */ - void setServerKeyCert(const SslKeyCertPair& serverKeyCert) SMING_DEPRECATED - { - setSslKeyCert(serverKeyCert); - } - - /** - * @brief Adds SSL support and specifies the server certificate and private key. - */ - using TcpConnection::setSslKeyCert; -#endif - protected: // Overload this method in your derived class! virtual TcpConnection* createClient(tcp_pcb* clientTcp); @@ -100,12 +83,7 @@ class TcpServer : public TcpConnection uint16_t activeClients = 0; protected: -#ifdef ENABLE_SSL - int sslSessionCacheSize = 50; size_t minHeapSize = 16384; -#else - size_t minHeapSize = 3000; -#endif bool active = true; Vector connections; diff --git a/Sming/Core/Network/WebsocketClient.cpp b/Sming/Core/Network/WebsocketClient.cpp index 38125bd26c..36dd1546ff 100644 --- a/Sming/Core/Network/WebsocketClient.cpp +++ b/Sming/Core/Network/WebsocketClient.cpp @@ -29,16 +29,13 @@ HttpConnection* WebsocketClient::getHttpConnection() return connection; } -bool WebsocketClient::connect(const Url& url, uint32_t sslOptions) +bool WebsocketClient::connect(const Url& url) { uri = url; - bool useSsl = false; - if(uri.Scheme == URI_SCHEME_WEBSOCKET_SECURE) { - useSsl = true; - } + bool useSsl = (uri.Scheme == URI_SCHEME_WEBSOCKET_SECURE); HttpConnection* httpConnection = getHttpConnection(); - if(!httpConnection) { + if(httpConnection == nullptr) { return false; } @@ -46,7 +43,8 @@ bool WebsocketClient::connect(const Url& url, uint32_t sslOptions) return false; } - httpConnection->connect(uri.Host, uri.getPort(), useSsl, sslOptions); + httpConnection->setSslInitHandler(sslInitHandler); + httpConnection->connect(uri.Host, uri.getPort(), useSsl); state = eWSCS_Ready; diff --git a/Sming/Core/Network/WebsocketClient.h b/Sming/Core/Network/WebsocketClient.h index 670b031872..a62e0996af 100644 --- a/Sming/Core/Network/WebsocketClient.h +++ b/Sming/Core/Network/WebsocketClient.h @@ -48,9 +48,8 @@ class WebsocketClient : protected WebsocketConnection /** @brief Connects websocket client to server * @param url Url address of websocket server - * @param sslOptions Specify the SSL options to be used when calling websocket server over SSL */ - bool connect(const Url& url, uint32_t sslOptions = 0); + bool connect(const Url& url); using WebsocketConnection::send; using WebsocketConnection::sendBinary; @@ -82,6 +81,15 @@ class WebsocketClient : protected WebsocketConnection using WebsocketConnection::close; using WebsocketConnection::getState; + /** + * @brief Set the SSL session initialisation callback + * @param handler + */ + void setSslInitHandler(Ssl::Session::InitDelegate handler) + { + sslInitHandler = handler; + } + /** @brief Disconnects websocket client from server * @deprecated Use `close()` instead */ @@ -96,6 +104,7 @@ class WebsocketClient : protected WebsocketConnection private: Url uri; String key; + Ssl::Session::InitDelegate sslInitHandler; }; /** @} */ diff --git a/Sming/README.rst b/Sming/README.rst index 5e38828623..f199106129 100644 --- a/Sming/README.rst +++ b/Sming/README.rst @@ -20,9 +20,7 @@ Serial Communications Note that this will change the default speed used for both flashing and serial comms. See also :component-esp8266:`esptool` and :component:`terminal` for further details. -The default rate for serial ports is 115200 baud. You can change it like this: - -:: +The default rate for serial ports is 115200 baud. You can change it like this:: make COM_SPEED=921600 @@ -39,9 +37,7 @@ Debug information log level and format * 2: information (default) * 3: debug -Change it like this: - -:: +Change it like this:: make DEBUG_VERBOSE_LEVEL=3 @@ -56,7 +52,7 @@ Change it like this: need to recompile all components like this: :: - + make components-clean make DEBUG_VERBOSE_LEVEL=3 @@ -67,16 +63,12 @@ Release builds .. envvar:: SMING_RELEASE By default, this value is undefined to produce a build with debug output. - To build for release, do this: + To build for release, do this:: - :: - make SMING_RELEASE=1 - This remains in force until you change it back: - - :: - + This remains in force until you change it back:: + make SMING_RELEASE= diff --git a/Sming/build.mk b/Sming/build.mk index b56b2f02f2..69584301f9 100644 --- a/Sming/build.mk +++ b/Sming/build.mk @@ -290,7 +290,7 @@ define TryApplyPatch $(call ApplyPatch,../.patches/$2); \ fi && \ if [ -d ../.patches/$(basename $2)/ ]; then \ - cp -f ../.patches/$(basename $2)/* . ; \ + cp -rf ../.patches/$(basename $2)/* . ; \ fi endef diff --git a/Sming/component.mk b/Sming/component.mk index 097b412ffd..abdc432bbe 100644 --- a/Sming/component.mk +++ b/Sming/component.mk @@ -21,6 +21,7 @@ COMPONENT_DEPENDS := \ ws_parser \ mqtt-codec \ libyuarel \ + ssl \ terminal COMPONENT_DOCFILES := \ @@ -33,7 +34,6 @@ COMPONENT_DOCFILES := \ Core/Data/*.rst COMPONENT_DOXYGEN_PREDEFINED := \ - ENABLE_SSL=1 \ ENABLE_CMD_EXECUTOR=1 COMPONENT_DOXYGEN_INPUT := \ @@ -44,28 +44,6 @@ COMPONENT_DOXYGEN_INPUT := \ Wiring \ System -# => SSL -COMPONENT_VARS := ENABLE_SSL -ifeq ($(ENABLE_SSL),1) - SMING_FEATURES := SSL - GLOBAL_CFLAGS += -DENABLE_SSL=1 - COMPONENT_DEPENDS += axtls-8266 - COMPONENT_VARS += SSL_DEBUG -else - SMING_FEATURES := none - COMPONENT_SRCDIRS := $(filter-out %/Ssl,$(COMPONENT_SRCDIRS)) -endif - -# Prints SSL status when App gets built -CUSTOM_TARGETS += check-ssl -.PHONY:check-ssl -check-ssl: -ifeq ($(ENABLE_SSL),1) - $(info + SSL support is enabled) -else - $(warning ! SSL support is not enabled. To enable it type: 'make clean; make ENABLE_SSL=1') -endif - # => Disable CommandExecutor functionality if not used and save some ROM and RAM COMPONENT_VARS += ENABLE_CMD_EXECUTOR ENABLE_CMD_EXECUTOR ?= 1 diff --git a/docs/source/experimental/httpserver-ssl.rst b/docs/source/experimental/httpserver-ssl.rst index 0ace98b23c..b4cf95f491 100644 --- a/docs/source/experimental/httpserver-ssl.rst +++ b/docs/source/experimental/httpserver-ssl.rst @@ -1,18 +1,17 @@ **************************** -How to use SSL in HttpServer +Enabling SSL in HttpServer **************************** .. highlight:: c++ -I will try to split my answer into two main areas - -* At the moment the HttpServer (also with Websockets) supports setting TLS/SSL -* but the question is how much sense is that making? +* At the moment any TCP based server in Sming can use TLS/SSL. +* That applies to HttpServer (also with Websockets). +* But make sure to read the security considerations and limitations. Enabling SSL in HttpServer ========================== -The ``listen`` method in the HttpServer class accepts a second optional +The ``listen`` method in the TcpServer, and the child HttpServer class, accepts a second optional parameter. If you look at the original code: :source:`samples/HttpServer_WebSockets/app/application.cpp#L95-L99`. @@ -27,38 +26,35 @@ And what is left is the actual setting of the server certificate:: void startWebServer() { - SSLKeyCertPair clientCertKey; - // TODO: Make sure to set a server certificate and key - // ... // Assign the certificate - server.setSslKeyCert(clientCertKey); + server.setSslInitHandler([](Ssl::Session& session) { + session.keyCert.assign(serverKey, serverCert); + }); server.listen(443, true); -And the final code can be something like:: +The final code can be something like:: - #ifdef ENABLE_SSL - #include "ssl/server_cert.h" - #include "ssl/server_private_key.h" - #endif + void startWebServer() + { + #ifdef ENABLE_SSL + server.setSslInitHandler([](Ssl::Session& session) { + session.keyCert.assign(serverKey, serverCert); + }); + server.listen(443, true); + #else + server.listen(80); + #endif + server.paths.set("/", onIndex); + //... - void startWebServer() - { - #ifdef ENABLE_SSL - clientCertKey.assign( - default_private_key, default_private_key_len, - default_certificate, default_certificate_len, - nullptr /* key password */); - server.setSslKeyCert(clientCertKey); - server.listen(443, true); - #else - server.listen(80, false); - #endif + +Security Considerations +======================= Does it really make sense to use SSL for an HttpServer on an ESP8266 device? -============================================================================ The certificate/private key pair should make it impossible for an -external user do decrypt your traffic so that the things that you sent +external user to decrypt your traffic so that the things that you sent are kept private, but there are some complications with this: - The private key will not stay private for long. The private key should be @@ -76,13 +72,12 @@ are kept private, but there are some complications with this: for example https://letsencrypt.org/. These will expire if not kept up to date so adds additional complexity to your application. -- You can handle up to 2 max 3 connections. SSL needs 16K of memory to +- You can handle up to 2 or maximum 3 connections. SSL needs 16K of memory to make the initial handshake. The memory consumption after a successful handshake can decrease to 4K, just for the SSL, per request. But realistically this means that you will end up with a server that can - handle max 2 or 3 simultaneous connections before the heap memory is + handle maximum 2 or 3 simultaneous connections before the heap memory is consumed and has to be released. -So IMHO it would be better to rely on the WIFI security that your Access -Point (AP) provides and make this AP accessible only for your IoT -devices. +Therefore, in our humble opinion, it would be better to rely on the WIFI security that your Access +Point (AP) provides and make this AP accessible only for your IoT devices. diff --git a/docs/source/framework/core/network/index.rst b/docs/source/framework/core/network/index.rst index 45d2ea9e1f..827b12c7f5 100644 --- a/docs/source/framework/core/network/index.rst +++ b/docs/source/framework/core/network/index.rst @@ -6,3 +6,4 @@ Networking Protocols :maxdepth: 1 * + /_inc/Sming/Components/ssl/index diff --git a/docs/source/framework/core/network/ssl.rst b/docs/source/framework/core/network/ssl.rst deleted file mode 100644 index 4d8ccd5689..0000000000 --- a/docs/source/framework/core/network/ssl.rst +++ /dev/null @@ -1,23 +0,0 @@ -SSL: Secure Sockets Layer -========================= - -https://en.m.wikipedia.org/wiki/Transport_Layer_Security - -Configuration Variables ------------------------ - -.. envvar:: ENABLE_SSL - - Default: undefined (disabled) - - SSL requires lots of RAM and some intensive processing, so to conserve resources it is disabled by default. - If you want to enable it then take a look at the :sample:`Basic_Ssl` sample. - - Set to 1 to enable SSL support using the :component:`axtls-8266` Component. - - -API Documentation ------------------ - -.. doxygengroup:: ssl - :content-only: diff --git a/docs/source/index.rst b/docs/source/index.rst index c0516632d1..223d8ae4b3 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -25,18 +25,19 @@ Summary - Built-in powerful wireless modules - Powerful asynchronous (async) network stack. - - Async TCP and UDP stack based on `LWIP `__ - - With clients supporting: HTTP, MQTT, WebSockets and SMTP - - And servers for: DNS, FTP, HTTP(+ WebSockets), Telnet - - With SSL support for all network clients and servers based on `axTLS 2.1+ `__ with `Lwirax `__. + - Async TCP and UDP stack based on `LWIP `__. + - With clients supporting: HTTP, MQTT, WebSockets and SMTP. + - And servers for: DNS, FTP, HTTP(+ WebSockets), Telnet. + - With :doc:`SSL support <_inc/Sming/Components/ssl/index>` for all network clients and servers. + Based on `axTLS `__ and `BearSSL `__. - Out of the box support for OTA over HTTPS. - ESP8266 specific features - - Integrated boot loader :component:`rboot` with support for 1MB ROMs, OTA firmware updating and ROM switching + - Integrated boot loader :component:`rboot` with support for 1MB ROMs, OTA firmware updating and ROM switching. - :doc:`Crash handlers ` for analyzing/handling system restarts due to fatal errors or WDT resets. - - :component-esp8266:`PWM support ` based on `Stefan Bruens PWM `__ - - Optional :component-esp8266:`custom heap allocation ` based on `Umm Malloc `__ + - :component-esp8266:`PWM support ` based on `Stefan Bruens PWM `__. + - Optional :component-esp8266:`custom heap allocation ` based on `Umm Malloc `__. - Based on :component-esp8266:`Espressif NONOS SDK `. Tested with versions 1.5, 2.0 and 3.0. - Linux/Windows features diff --git a/docs/source/upgrading/4.0-4.1.rst b/docs/source/upgrading/4.0-4.1.rst new file mode 100644 index 0000000000..b7e7bce190 --- /dev/null +++ b/docs/source/upgrading/4.0-4.1.rst @@ -0,0 +1,164 @@ +***************** +From v4.0 to v4.1 +***************** + +Summary +======= + +.. highlight:: c++ + + +With Sming version 4.1 there are some backwards incompatible changes. +This page is provided to help with migrating your applications. + +SSL +=== + +In version 4.1 one can choose between different SSL implementations. +At the moment Sming supports `axTLS `__ and `BearSSL `__ for creating +SSL enabled clients and servers. + +In order to allow multiple SSL adapters and seamless integration the library code had to be refactored and that introduced some breaking changes. + +Using specific SSL adapters +--------------------------- + +The default SSL adapter is still based on axTLS. And it can be enabled in your application by providing +the ENABLE_SSL directive either in your component.mk file or during compilation. +See :doc:`SSL documentation ` for valid values. + +See the :doc:`comparison between the different SSL adapters ` to check which one fits your needs. + +Migration +--------- +The access to the SSL connection from the TcpConnection is simplified and fetching information about SSL certificate and session id is easier +than before. + +The old code was looking like this:: + + SSL* ssl = connection.getSsl(); + if(ssl) { + const char* common_name = ssl_get_cert_dn(ssl, SSL_X509_CERT_COMMON_NAME); + if(common_name) { + debugf("Common Name:\t\t\t%s\n", common_name); + } + displayCipher(ssl); + displaySessionId(ssl); + } + + +Now it should be migrated to the following shorter version:: + + auto ssl = connection.getSsl(); + if(ssl) { + ssl->printTo(Serial); + } + + +SSL initialisation in TCP clients or servers is done using `Ssl::Session::InitDelegate` callback. + +Old code looking like this:: + + MqttClient* getMqttClient() + { + if(mqtt == nullptr) { + mqtt = new MqttClient(); + mqtt->addSslOptions(SSL_SERVER_VERIFY_LATER); // << this is where we were setting SSL options + Url url; + +Has to be migrated to the following code:: + + void sslInit(Ssl::Session& session) + { + session.options.verifyLater = true; + } + + MqttClient* getMqttClient() + { + if(mqtt == nullptr) { + mqtt = new MqttClient(); + mqtt->setSslInitHandler(sslInit); // << this is where the sslInit callback is set + Url url; + +It is possible create SSL enabled server. The excerpt below demonstrates this and it is part of the :sample:`HttpServer_ConfigNetwork` sample. +Pay attention to the :doc:`security considerations ` and limitations using this on a microcontroller with limited RAM memory:: + + void startWebServer() + { + #ifdef ENABLE_SSL + server.setSslInitHandler([](Ssl::Session& session) { + debug_i("SSL Init handler: setting server keyCert"); + session.keyCert.assign(serverKey, serverCert); + }); + server.listen(443, true); + #else + server.listen(80); + #endif + server.paths.set("/", onIndex); + server.paths.set("/ipconfig", onIpConfig); + server.paths.set("/ajax/get-networks", onAjaxNetworkList); + server.paths.set("/ajax/connect", onAjaxConnect); + server.paths.setDefault(onFile); + } + + +Setting client certificates, ssl options and pinning for a HttpRequest is done using onSslInit callback. +If you look at the :sample:`Basic_Ssl` sample you will see that the old way of setting them was as shown below:: + + HttpRequest* request = new HttpRequest(F("https://www.grc.com/fingerprints.htm")); + request->setSslOptions(SSL_SERVER_VERIFY_LATER); + request->pinCertificate(fingerprints); + +The new one is using the following sequence of commands:: + + auto request = new HttpRequest(F("https://www.grc.com/fingerprints.htm")); + request->onSslInit(grcSslInit); + + +A sample `grcSslInit` callback is given below. In the callback the developer +has access to the current SSL session and HTTP request and can modify them accordingly:: + + void grcSslInit(Ssl::Session& session, HttpRequest& request) + { + SslFingerprints fingerprints; + + //... + + session.validators.add(fingerprints); + + // We're using fingerprints, so don't attempt to validate full certificate + session.options.verifyLater = true; + + session.fragmentSize = Ssl::eSEFS_16K; + } + + +Cryptographic functions +~~~~~~~~~~~~~~~~~~~~~~~ +Cryptographic functions are now not included by default. This means that if you want to continue using them they should be included +in your application source code. For example the following old code is using axTLS cryptographic functions:: + + + char* loadPsk(int* keylen) + { + SHA1_CTX sha_ctx; + // ... + SHA1_Init(&sha_ctx); + SHA1_Update(&sha_ctx, (uint8_t*)buffer, strlen(buffer)); + SHA1_Final(digest, &sha_ctx); + +For this code to work you should include the following header:: + + #include + + +And also make sure that your application `component.mk` file has the following line:: + + COMPONENT_DEPENDS += axtls-8266 + +You can use also one SSL adapter, for example Bearssl, with another cryptographic library, for example Axtls. + +SSL namespace +~~~~~~~~~~~~~ +All SSL related classes and types are organized in a separate namespace called `Ssl`. For example you should use `Ssl::KeyCertPair` +instead of `SslKeyCertPair` and `Ssl::Fingerprints` instead of `SslFingerprints`. diff --git a/docs/source/upgrading/index.rst b/docs/source/upgrading/index.rst index 1ac6ae05d3..ee24574f91 100644 --- a/docs/source/upgrading/index.rst +++ b/docs/source/upgrading/index.rst @@ -7,4 +7,5 @@ For newer versions we have dedicated pages. .. toctree:: :maxdepth: 1 + 4.0-4.1 3.8-4.0 diff --git a/samples/Basic_Ssl/app/application.cpp b/samples/Basic_Ssl/app/application.cpp index a039f92a72..79ff459327 100644 --- a/samples/Basic_Ssl/app/application.cpp +++ b/samples/Basic_Ssl/app/application.cpp @@ -1,5 +1,5 @@ #include -#include "Data/HexString.h" +#include // If you want, you can define WiFi settings globally in Eclipse Environment Variables #ifndef WIFI_SSID @@ -10,71 +10,43 @@ Timer procTimer; HttpClient downloadClient; -/* Debug SSL functions */ -void displaySessionId(SSL* ssl) +int onDownload(HttpConnection& connection, bool success) { - const uint8_t* session_id = ssl_get_session_id(ssl); - unsigned sess_id_size = ssl_get_session_id_size(ssl); - - if(sess_id_size != 0) { - debugf("-----BEGIN SSL SESSION PARAMETERS-----"); - m_puts(makeHexString(session_id, sess_id_size).c_str()); - m_putc('\n'); - debugf("-----END SSL SESSION PARAMETERS-----"); + Serial.print(_F("Got response code: ")); + auto status = connection.getResponse()->code; + Serial.print(status); + Serial.print(" ("); + Serial.print(httpGetStatusText(status)); + Serial.print(_F("), success: ")); + Serial.print(success); + + auto stream = connection.getResponse()->stream; + assert(stream != nullptr); + + Serial.print(_F(", received ")); + Serial.print(stream->available()); + Serial.println(_F(" bytes")); + + auto& headers = connection.getResponse()->headers; + for(unsigned i = 0; i < headers.count(); ++i) { + Serial.print(headers[i]); } -} -/** - * Display what cipher we are using - */ -void displayCipher(SSL* ssl) -{ - m_printf("CIPHER is "); - switch(ssl_get_cipher_id(ssl)) { - case SSL_AES128_SHA: - m_printf("AES128-SHA"); - break; - - case SSL_AES256_SHA: - m_printf("AES256-SHA"); - break; - - case SSL_AES128_SHA256: - m_printf("SSL_AES128_SHA256"); - break; - - case SSL_AES256_SHA256: - m_printf("SSL_AES256_SHA256"); - break; - - default: - m_printf("Unknown - %u", ssl_get_cipher_id(ssl)); - break; - } - - m_printf("\n"); -} - -int onDownload(HttpConnection& connection, bool success) -{ - debugf("Got response code: %d", connection.getResponse()->code); - debugf("Success: %d", success); - - SSL* ssl = connection.getSsl(); - if(ssl) { - const char* common_name = ssl_get_cert_dn(ssl, SSL_X509_CERT_COMMON_NAME); - if(common_name) { - debugf("Common Name:\t\t\t%s\n", common_name); - } - displayCipher(ssl); - displaySessionId(ssl); + auto ssl = connection.getSsl(); + if(ssl != nullptr) { + ssl->printTo(Serial); } return 0; // return 0 on success in your callbacks } -void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) +/* + * Initialise SSL session parameters for connecting to the GRC web server + */ +void grcSslInit(Ssl::Session& session, HttpRequest& request) { + debug_i("Initialising SSL session for GRC"); + // Use the Gibson Research fingerprints web page as an example. Unlike Google, the fingerprints don't change! static const uint8_t grcSha1Fingerprint[] PROGMEM = {0x15, 0x9A, 0x76, 0xC5, 0xAE, 0xF4, 0x90, 0x15, 0x79, 0xE6, 0xA4, 0x99, 0x96, 0xC1, 0xD6, 0xA1, 0xD9, 0x3B, 0x07, 0x43}; @@ -83,19 +55,7 @@ void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) 0xEB, 0xA0, 0xFE, 0x70, 0xFE, 0xCB, 0xF8, 0xA8, 0x7A, 0xB9, 0x1D, 0xAC, 0x1E, 0xAC, 0xA0, 0xF6, 0x62, 0xCB, 0xCD, 0xE4, 0x16, 0x72, 0xE6, 0xBC, 0x82, 0x9B, 0x32, 0x39, 0x43, 0x15, 0x76, 0xD4}; - debugf("Connected. Got IP: %s", ip.toString().c_str()); - - HttpRequest* request = new HttpRequest(F("https://www.grc.com/fingerprints.htm")); - request->setSslOptions(SSL_SERVER_VERIFY_LATER); - - /* - * GET probably won't work as sites tend to use 16K blocks which we can't handle, - * so just fetch the header and leave it at that. To return actual data requires a web server - * configured to use smaller encrytion blocks, e.g. 4K. - */ - request->setMethod(HTTP_HEAD); - - SslFingerprints fingerprints; + Ssl::Fingerprints fingerprints; /* * The line below shows how to trust only a certificate that matches the SHA1 fingerprint. @@ -107,15 +67,30 @@ void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) */ fingerprints.setSha256_P(grcPublicKeyFingerprint, sizeof(grcPublicKeyFingerprint)); - request->pinCertificate(fingerprints); - request->onRequestComplete(onDownload); + session.validators.add(fingerprints); + + // We're using fingerprints, so don't attempt to validate full certificate + session.options.verifyLater = true; + // Go with maximum buffer sizes + session.maxBufferSize = Ssl::MaxBufferSize::K16; +} + +void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) +{ + Serial.print(F("Connected. Got IP: ")); + Serial.println(ip); + + auto request = new HttpRequest(F("https://www.grc.com/fingerprints.htm")); + request->onSslInit(grcSslInit); + request->onRequestComplete(onDownload); + request->setResponseStream(new CounterStream); downloadClient.send(request); } void connectFail(const String& ssid, MacAddress bssid, WifiDisconnectReason reason) { - debugf("I'm NOT CONNECTED!"); + Serial.println(F("I'm NOT CONNECTED!")); } void init() diff --git a/samples/Basic_Ssl/include/CounterStream.h b/samples/Basic_Ssl/include/CounterStream.h new file mode 100644 index 0000000000..07b0c66cf1 --- /dev/null +++ b/samples/Basic_Ssl/include/CounterStream.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +/* + * All this stream does is count the number of bytes written to it, + * which we can read by calling `available()`. The data itself is ignored. + */ +class CounterStream : public ReadWriteStream +{ +public: + size_t write(const uint8_t* buffer, size_t size) + { + streamSize += size; + return size; + } + + int available() override + { + return streamSize; + } + + uint16_t readMemoryBlock(char*, int) override + { + return 0; + } + + bool isFinished() override + { + return true; + } + +private: + size_t streamSize = 0; +}; diff --git a/samples/Echo_Ssl/app/application.cpp b/samples/Echo_Ssl/app/application.cpp index e5435ada14..76db2bef0d 100644 --- a/samples/Echo_Ssl/app/application.cpp +++ b/samples/Echo_Ssl/app/application.cpp @@ -24,54 +24,6 @@ TcpClient* client; bool showMeta = true; -/* Debug SSL functions */ -void displaySessionId(SSL* ssl) -{ - int i; - const uint8_t* session_id = ssl_get_session_id(ssl); - int sess_id_size = ssl_get_session_id_size(ssl); - - if(sess_id_size > 0) { - debugf("-----BEGIN SSL SESSION PARAMETERS-----\n"); - for(i = 0; i < sess_id_size; i++) { - debugf("%02x", session_id[i]); - } - - debugf("\n-----END SSL SESSION PARAMETERS-----\n"); - } -} - -/** - * Display what cipher we are using - */ -void displayCipher(SSL* ssl) -{ - Serial.printf("CIPHER is "); - switch(ssl_get_cipher_id(ssl)) { - case SSL_AES128_SHA: - Serial.printf("AES128-SHA"); - break; - - case SSL_AES256_SHA: - Serial.printf("AES256-SHA"); - break; - - case SSL_AES128_SHA256: - Serial.printf("SSL_AES128_SHA256"); - break; - - case SSL_AES256_SHA256: - Serial.printf("SSL_AES256_SHA256"); - break; - - default: - Serial.printf("Unknown - %d", ssl_get_cipher_id(ssl)); - break; - } - - Serial.printf("\n"); -} - bool onReceive(TcpClient& tcpClient, char* data, int size) { debugf("Got data with size: %d", size); @@ -81,14 +33,9 @@ bool onReceive(TcpClient& tcpClient, char* data, int size) } if(showMeta) { - SSL* ssl = tcpClient.getSsl(); - if(ssl) { - const char* common_name = ssl_get_cert_dn(ssl, SSL_X509_CERT_COMMON_NAME); - if(common_name) { - debugf("Common Name:\t\t\t%s\n", common_name); - } - displayCipher(ssl); - displaySessionId(ssl); + auto ssl = tcpClient.getSsl(); + if(ssl != nullptr) { + ssl->printTo(Serial); } debugf("end of meta..."); showMeta = false; @@ -108,7 +55,7 @@ void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) { debugf("IP: %s", ip.toString().c_str()); client = new TcpClient(TcpClientDataDelegate(onReceive)); - client->addSslOptions(SSL_SERVER_VERIFY_LATER); + client->setSslInitHandler([](Ssl::Session& session) { session.options.verifyLater = true; }); client->connect(IpAddress(SERVER_IP), 4444, true); } diff --git a/samples/HttpClient/app/application.cpp b/samples/HttpClient/app/application.cpp index 8b0a2205a2..c54a0cc414 100644 --- a/samples/HttpClient/app/application.cpp +++ b/samples/HttpClient/app/application.cpp @@ -1,6 +1,5 @@ #include - -#include "Network/HttpClient.h" +#include // If you want, you can define WiFi settings globally in Eclipse Environment Variables #ifndef WIFI_SSID @@ -10,54 +9,6 @@ HttpClient httpClient; -/* Debug SSL functions */ -void displaySessionId(SSL* ssl) -{ - int i; - const uint8_t* session_id = ssl_get_session_id(ssl); - int sess_id_size = ssl_get_session_id_size(ssl); - - if(sess_id_size > 0) { - debugf("-----BEGIN SSL SESSION PARAMETERS-----"); - for(i = 0; i < sess_id_size; i++) { - m_printf("%02x", session_id[i]); - } - - debugf("\n-----END SSL SESSION PARAMETERS-----"); - } -} - -/** - * Display what cipher we are using - */ -void displayCipher(SSL* ssl) -{ - m_printf("CIPHER is "); - switch(ssl_get_cipher_id(ssl)) { - case SSL_AES128_SHA: - m_printf("AES128-SHA"); - break; - - case SSL_AES256_SHA: - m_printf("AES256-SHA"); - break; - - case SSL_AES128_SHA256: - m_printf("SSL_AES128_SHA256"); - break; - - case SSL_AES256_SHA256: - m_printf("SSL_AES256_SHA256"); - break; - - default: - m_printf("Unknown - %d", ssl_get_cipher_id(ssl)); - break; - } - - m_printf("\n"); -} - int onDownload(HttpConnection& connection, bool success) { debugf("\n=========[ URL: %s ]============", connection.getRequest()->uri.toString().c_str()); @@ -67,20 +18,16 @@ int onDownload(HttpConnection& connection, bool success) if(connection.getRequest()->method != HTTP_HEAD) { debugf("Got content starting with: %s", connection.getResponse()->getBody().substring(0, 1000).c_str()); } - SSL* ssl = connection.getSsl(); - if(ssl) { - const char* common_name = ssl_get_cert_dn(ssl, SSL_X509_CERT_COMMON_NAME); - if(common_name) { - debugf("Common Name:\t\t\t%s\n", common_name); - } - displayCipher(ssl); - displaySessionId(ssl); + + auto ssl = connection.getSsl(); + if(ssl != nullptr) { + ssl->printTo(Serial); } return 0; // return 0 on success in your callbacks } -void setSslFingerprints(HttpRequest* request) +void sslRequestInit(Ssl::Session& session, HttpRequest& request) { /* * SSL validation: We check the remote server certificate against a fingerprint @@ -89,7 +36,7 @@ void setSslFingerprints(HttpRequest* request) * Note: SSL is not compiled by default. In our example we set the ENABLE_SSL directive to 1 * (See: ../Makefile-user.mk ) */ - request->setSslOptions(SSL_SERVER_VERIFY_LATER); + session.options.verifyLater = true; // These are the fingerprints for httpbin.org static const uint8_t sha1Fingerprint[] PROGMEM = {0x2B, 0xF0, 0x48, 0x9D, 0x78, 0xB4, 0xDE, 0xE9, 0x69, 0xE2, @@ -99,7 +46,7 @@ void setSslFingerprints(HttpRequest* request) 0xE3, 0x88, 0xC4, 0x0A, 0x2A, 0x99, 0x8F, 0xA4, 0x8C, 0x38, 0x4E, 0xE7, 0xCB, 0x4F, 0x8B, 0x99, 0x19, 0x48, 0x63, 0x9A, 0x2E, 0xD6, 0x05, 0x7D, 0xB1, 0xD3, 0x56, 0x6C, 0xC0, 0x7E, 0x74, 0x1A}; - SslFingerprints fingerprints; + Ssl::Fingerprints fingerprints; // Trust only a certificate in which the public key matches the SHA256 fingerprint... fingerprints.setSha256_P(publicKeyFingerprint, sizeof(publicKeyFingerprint)); @@ -107,12 +54,15 @@ void setSslFingerprints(HttpRequest* request) // ... or a certificate that matches the SHA1 fingerprint. fingerprints.setSha1_P(sha1Fingerprint, sizeof(sha1Fingerprint)); - // Attached fingerprints to request for validation - request->pinCertificate(fingerprints); + // Set fingerprints for validation + session.validators.add(fingerprints); } void connectOk(IpAddress ip, IpAddress mask, IpAddress gateway) { + Serial.print(F("Connected. Got IP: ")); + Serial.println(ip); + // [ GET request: The example below shows how to make HTTP requests ] // First: The HttpRequest object contains all the data that needs to be sent @@ -129,13 +79,11 @@ void connectOk(IpAddress ip, IpAddress mask, IpAddress gateway) // ... or like getRequest->setHeader(F("X-Powered-By"), F("Sming")); - // SSL validation and fingerprinting - setSslFingerprints(getRequest); - /* * Notice: If we use SSL we need to set the SSL settings only for the first request * and all consecutive requests to the same host:port will try to reuse those settings */ + getRequest->onSslInit(sslRequestInit); // If we want to process the response we can do it by setting a onRequestCallback getRequest->onRequestComplete(onDownload); diff --git a/samples/HttpServer_ConfigNetwork/app/application.cpp b/samples/HttpServer_ConfigNetwork/app/application.cpp index f63e5b5503..891b47c005 100644 --- a/samples/HttpServer_ConfigNetwork/app/application.cpp +++ b/samples/HttpServer_ConfigNetwork/app/application.cpp @@ -1,7 +1,7 @@ #include #include #include -#include "Data/Stream/TemplateFlashMemoryStream.h" +#include HttpServer server; FtpServer ftp; @@ -13,7 +13,10 @@ Timer connectionTimer; String lastModified; // Instead of using a SPIFFS file, here we demonstrate usage of imported Flash Strings -IMPORT_FSTR(flashSettings, PROJECT_DIR "/web/build/settings.html") +IMPORT_FSTR_LOCAL(flashSettings, PROJECT_DIR "/web/build/settings.html") + +IMPORT_FSTR_LOCAL(serverKey, PROJECT_DIR "/cert/key_1024"); +IMPORT_FSTR_LOCAL(serverCert, PROJECT_DIR "/cert/x509_1024.cer"); void onIndex(HttpRequest& request, HttpResponse& response) { @@ -166,7 +169,15 @@ void onAjaxConnect(HttpRequest& request, HttpResponse& response) void startWebServer() { +#ifdef ENABLE_SSL + server.setSslInitHandler([](Ssl::Session& session) { + debug_i("SSL Init handler: setting server keyCert"); + session.keyCert.assign(serverKey, serverCert); + }); + server.listen(443, true); +#else server.listen(80); +#endif server.paths.set("/", onIndex); server.paths.set("/ipconfig", onIpConfig); server.paths.set("/ajax/get-networks", onAjaxNetworkList); diff --git a/samples/HttpServer_ConfigNetwork/cert/key_1024 b/samples/HttpServer_ConfigNetwork/cert/key_1024 new file mode 100644 index 0000000000000000000000000000000000000000..d315346b3b5152c4c3260f2c68bd425d81a8192e GIT binary patch literal 609 zcmV-n0-pUaf&yIv0RRGlfdJ3qAo183Um^*pGyuvKIsAL`Xw|pVytb#dz6?+Ln0M(v zEHevVv^&GWf00E-*CL*QMS!(&46xTnNUh?ZY*joQ=}I|x#-a*FqPS72rHHJK9*`DH ztRpP?C1QxMr)z_1XziTE_Rwh^Nd(~z_$B_PQ(FKDS*k5@C}?`ckEhyp4zH9_<`WGd$XNAg1uCMDZY$e?fZl*EC~xgMc8^B`k#DjGUodMQ(P zniFnXY~%f|QCBImumNdU*EBY=>dCYN5AqkU!HnU4DYch?VfR_)Q?R0UI9JkMf{Hso z?^Ix)F15anWU(0E9u+k|n(1ZCz%c?r0Q3$}g%Fl&pw{cla_P@m0gQ!*xu6*xRRz~K z8MT?*I+7xd8{oKz<>g9rV<;l);vWG}R~M)RX!L4*wn~h`B?3VJ+76!sWjy-&?D54N z`INwh^>)|plZQQi*+o`;%;8m?!uN@vpYh|{7s<$4Qx%GOlsQf*()qUM92IDlK;zI? z0zm+=DxV7}vUq7G`@a$?XA2I~%CB#7C@CT8euNTM!Cjo|F2~N`05$04-Fe0Bf&@r^ z2Yw;z8(uIjmJ6~(Mi4y$Kz$e(Hyv~jQ?_ilwTgkjwQI^k^fn|$t|z?oa&-UfTJ{Hq zCsEzC=JCy|r_Ho9-amX~kMG@_1s8%`o(nwYKLSAj&$Wm8!-9Jl0gs5J)Y+;q*5JzWp}9OnAn=OZgKrY=CNKRX^gV8}*xTB}TDpA4te9C_g-`qr_%cy> z)8s7IxuQi~oTBS23zt3jdH&U^>(2JS3T;#71zEW;U%4}9@18T(%%N|mWtdL3+h1bQ kK09&yS3a>B`$Rm8ML!jM_1MPRy}vWFSfao6-Khp&0EM2gwg3PC literal 0 HcmV?d00001 diff --git a/samples/HttpServer_ConfigNetwork/cert/x509_1024.pem b/samples/HttpServer_ConfigNetwork/cert/x509_1024.pem new file mode 100644 index 0000000000..3c73078c04 --- /dev/null +++ b/samples/HttpServer_ConfigNetwork/cert/x509_1024.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB1zCCAUACCQCqjdncMD/aTTANBgkqhkiG9w0BAQUFADA0MTIwMAYDVQQKEylT +bWluZyBQcm9qZWN0IERvZGd5IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xOTEy +MjgyMzE2MTVaFw0zMzA5MDUyMzE2MTVaMCwxFjAUBgNVBAoTDVNtaW5nIFByb2pl +Y3QxEjAQBgNVBAMTCTEyNy4wLjAuMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC +gYEAz+Ig8dgYXyIJqDQAyhU5/HvzaNW307y2p7a+DE/6mHfpPywzC1+0O8PBf5FF +RtcinoFFgLVxDLDXR0it4p9sVTwc6Uo5eMaiCkaiuFGqpYisjh6QFkysIyz6JWKI +sKdrg2po7ZzF9tBpHUkE4Q74Jf6mU1sACVmqLXEoaBpjCpMCAwEAATANBgkqhkiG +9w0BAQUFAAOBgQBXOsfFej2bRWzMGCmnVBBR1B7g9hjuk6X8EvdekxbtfcMqLQ7G +OgMFhRPI+1D0kSOyk2pFnRVyigkVrjlxpudM5/U6ity3/SCGlJ5SOkQD1NycvbzM +OwNV7ZZoNZM+v3Q4h5tht/UOFpi+FElzFfJw9Ui2BYu/iWlzGI+F7sqATQ== +-----END CERTIFICATE----- diff --git a/samples/HttpServer_ConfigNetwork/include/ssl/cert.h b/samples/HttpServer_ConfigNetwork/include/ssl/cert.h new file mode 100644 index 0000000000..8515409e43 --- /dev/null +++ b/samples/HttpServer_ConfigNetwork/include/ssl/cert.h @@ -0,0 +1,27 @@ +unsigned char x509_1024_cer[] = { + 0x30, 0x82, 0x01, 0xd7, 0x30, 0x82, 0x01, 0x40, 0x02, 0x09, 0x00, 0xaa, 0x8d, 0xd9, 0xdc, 0x30, 0x3f, 0xda, 0x4d, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x34, 0x31, 0x32, + 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x29, 0x53, 0x6d, 0x69, 0x6e, 0x67, 0x20, 0x50, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x20, 0x44, 0x6f, 0x64, 0x67, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x39, 0x31, + 0x32, 0x32, 0x38, 0x32, 0x33, 0x31, 0x36, 0x31, 0x35, 0x5a, 0x17, 0x0d, 0x33, 0x33, 0x30, 0x39, 0x30, 0x35, 0x32, + 0x33, 0x31, 0x36, 0x31, 0x35, 0x5a, 0x30, 0x2c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, + 0x53, 0x6d, 0x69, 0x6e, 0x67, 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x81, 0x9f, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, + 0x89, 0x02, 0x81, 0x81, 0x00, 0xcf, 0xe2, 0x20, 0xf1, 0xd8, 0x18, 0x5f, 0x22, 0x09, 0xa8, 0x34, 0x00, 0xca, 0x15, + 0x39, 0xfc, 0x7b, 0xf3, 0x68, 0xd5, 0xb7, 0xd3, 0xbc, 0xb6, 0xa7, 0xb6, 0xbe, 0x0c, 0x4f, 0xfa, 0x98, 0x77, 0xe9, + 0x3f, 0x2c, 0x33, 0x0b, 0x5f, 0xb4, 0x3b, 0xc3, 0xc1, 0x7f, 0x91, 0x45, 0x46, 0xd7, 0x22, 0x9e, 0x81, 0x45, 0x80, + 0xb5, 0x71, 0x0c, 0xb0, 0xd7, 0x47, 0x48, 0xad, 0xe2, 0x9f, 0x6c, 0x55, 0x3c, 0x1c, 0xe9, 0x4a, 0x39, 0x78, 0xc6, + 0xa2, 0x0a, 0x46, 0xa2, 0xb8, 0x51, 0xaa, 0xa5, 0x88, 0xac, 0x8e, 0x1e, 0x90, 0x16, 0x4c, 0xac, 0x23, 0x2c, 0xfa, + 0x25, 0x62, 0x88, 0xb0, 0xa7, 0x6b, 0x83, 0x6a, 0x68, 0xed, 0x9c, 0xc5, 0xf6, 0xd0, 0x69, 0x1d, 0x49, 0x04, 0xe1, + 0x0e, 0xf8, 0x25, 0xfe, 0xa6, 0x53, 0x5b, 0x00, 0x09, 0x59, 0xaa, 0x2d, 0x71, 0x28, 0x68, 0x1a, 0x63, 0x0a, 0x93, + 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, + 0x00, 0x03, 0x81, 0x81, 0x00, 0x57, 0x3a, 0xc7, 0xc5, 0x7a, 0x3d, 0x9b, 0x45, 0x6c, 0xcc, 0x18, 0x29, 0xa7, 0x54, + 0x10, 0x51, 0xd4, 0x1e, 0xe0, 0xf6, 0x18, 0xee, 0x93, 0xa5, 0xfc, 0x12, 0xf7, 0x5e, 0x93, 0x16, 0xed, 0x7d, 0xc3, + 0x2a, 0x2d, 0x0e, 0xc6, 0x3a, 0x03, 0x05, 0x85, 0x13, 0xc8, 0xfb, 0x50, 0xf4, 0x91, 0x23, 0xb2, 0x93, 0x6a, 0x45, + 0x9d, 0x15, 0x72, 0x8a, 0x09, 0x15, 0xae, 0x39, 0x71, 0xa6, 0xe7, 0x4c, 0xe7, 0xf5, 0x3a, 0x8a, 0xdc, 0xb7, 0xfd, + 0x20, 0x86, 0x94, 0x9e, 0x52, 0x3a, 0x44, 0x03, 0xd4, 0xdc, 0x9c, 0xbd, 0xbc, 0xcc, 0x3b, 0x03, 0x55, 0xed, 0x96, + 0x68, 0x35, 0x93, 0x3e, 0xbf, 0x74, 0x38, 0x87, 0x9b, 0x61, 0xb7, 0xf5, 0x0e, 0x16, 0x98, 0xbe, 0x14, 0x49, 0x73, + 0x15, 0xf2, 0x70, 0xf5, 0x48, 0xb6, 0x05, 0x8b, 0xbf, 0x89, 0x69, 0x73, 0x18, 0x8f, 0x85, 0xee, 0xca, 0x80, 0x4d}; +unsigned int x509_1024_cer_len = 475; diff --git a/samples/HttpServer_ConfigNetwork/include/ssl/private_key.h b/samples/HttpServer_ConfigNetwork/include/ssl/private_key.h new file mode 100644 index 0000000000..da28f9a732 --- /dev/null +++ b/samples/HttpServer_ConfigNetwork/include/ssl/private_key.h @@ -0,0 +1,35 @@ +unsigned char key_1024[] = { + 0x30, 0x82, 0x02, 0x5d, 0x02, 0x01, 0x00, 0x02, 0x81, 0x81, 0x00, 0xcf, 0xe2, 0x20, 0xf1, 0xd8, 0x18, 0x5f, 0x22, + 0x09, 0xa8, 0x34, 0x00, 0xca, 0x15, 0x39, 0xfc, 0x7b, 0xf3, 0x68, 0xd5, 0xb7, 0xd3, 0xbc, 0xb6, 0xa7, 0xb6, 0xbe, + 0x0c, 0x4f, 0xfa, 0x98, 0x77, 0xe9, 0x3f, 0x2c, 0x33, 0x0b, 0x5f, 0xb4, 0x3b, 0xc3, 0xc1, 0x7f, 0x91, 0x45, 0x46, + 0xd7, 0x22, 0x9e, 0x81, 0x45, 0x80, 0xb5, 0x71, 0x0c, 0xb0, 0xd7, 0x47, 0x48, 0xad, 0xe2, 0x9f, 0x6c, 0x55, 0x3c, + 0x1c, 0xe9, 0x4a, 0x39, 0x78, 0xc6, 0xa2, 0x0a, 0x46, 0xa2, 0xb8, 0x51, 0xaa, 0xa5, 0x88, 0xac, 0x8e, 0x1e, 0x90, + 0x16, 0x4c, 0xac, 0x23, 0x2c, 0xfa, 0x25, 0x62, 0x88, 0xb0, 0xa7, 0x6b, 0x83, 0x6a, 0x68, 0xed, 0x9c, 0xc5, 0xf6, + 0xd0, 0x69, 0x1d, 0x49, 0x04, 0xe1, 0x0e, 0xf8, 0x25, 0xfe, 0xa6, 0x53, 0x5b, 0x00, 0x09, 0x59, 0xaa, 0x2d, 0x71, + 0x28, 0x68, 0x1a, 0x63, 0x0a, 0x93, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x81, 0x80, 0x4c, 0x56, 0x5b, 0x86, 0xb3, + 0xb5, 0xef, 0x69, 0x4e, 0x66, 0x88, 0x02, 0x2a, 0x33, 0x35, 0x41, 0xf4, 0x3a, 0x64, 0x2a, 0xe7, 0x00, 0x47, 0xf2, + 0x43, 0x10, 0x26, 0x25, 0xdb, 0x50, 0xc8, 0xa0, 0x6f, 0xf4, 0x94, 0xc4, 0x81, 0xce, 0xb9, 0x1e, 0xa1, 0x39, 0xf3, + 0x20, 0x63, 0x72, 0x2a, 0x1a, 0x3a, 0x5e, 0x7a, 0x29, 0x53, 0x77, 0x9a, 0x13, 0x6e, 0x5a, 0x6c, 0xe3, 0xfd, 0xae, + 0x51, 0x57, 0x29, 0xb3, 0xb0, 0x01, 0x69, 0x58, 0xd7, 0x34, 0x36, 0xb2, 0xea, 0xc9, 0xb4, 0x03, 0x0f, 0xf2, 0x17, + 0xaf, 0xc1, 0x8c, 0xe1, 0x7e, 0x29, 0xb5, 0x97, 0x80, 0x61, 0xf7, 0x59, 0xe6, 0x53, 0xb0, 0xa2, 0x77, 0x38, 0x57, + 0xd2, 0x5e, 0x82, 0x8a, 0x3b, 0x3f, 0xef, 0x54, 0x60, 0x9f, 0x2e, 0xb5, 0xbe, 0x8e, 0x64, 0xb1, 0x18, 0xdf, 0x1e, + 0x15, 0x35, 0x3f, 0x9a, 0xe9, 0x65, 0xcc, 0xc0, 0x31, 0x02, 0x41, 0x00, 0xf4, 0x0e, 0x50, 0x85, 0x10, 0x96, 0x6b, + 0xa0, 0xd6, 0xeb, 0xcb, 0x72, 0xe9, 0xcf, 0x59, 0x01, 0x8c, 0x85, 0x87, 0xb9, 0xa0, 0x19, 0x1d, 0x55, 0x05, 0xd7, + 0x37, 0x19, 0xb5, 0x99, 0xdc, 0x3a, 0x92, 0x22, 0x8d, 0x1b, 0xe0, 0xb8, 0x88, 0xe5, 0xe5, 0x4a, 0x74, 0x63, 0x28, + 0x22, 0xeb, 0xe2, 0x1f, 0x01, 0x50, 0x57, 0x17, 0xa8, 0x04, 0x68, 0xf4, 0x6a, 0x7d, 0xb6, 0x4a, 0x8c, 0xc2, 0x25, + 0x02, 0x41, 0x00, 0xda, 0x0e, 0x9f, 0x03, 0x65, 0x3c, 0xfa, 0xfa, 0xec, 0xf1, 0xc5, 0x1d, 0xf9, 0x94, 0xc0, 0x86, + 0xf5, 0x76, 0xd7, 0xee, 0x93, 0x87, 0x3d, 0x7e, 0xd9, 0x45, 0x56, 0x7d, 0xcc, 0xe1, 0x55, 0x9d, 0xc2, 0xf7, 0x89, + 0x9f, 0x9f, 0xf1, 0xe3, 0xdb, 0x17, 0xc9, 0xc8, 0x5a, 0x53, 0x15, 0x8a, 0x7a, 0x94, 0x39, 0x4e, 0x29, 0xd2, 0xf9, + 0xb6, 0xe7, 0x1c, 0x15, 0x68, 0x95, 0x40, 0xe3, 0xd0, 0x57, 0x02, 0x41, 0x00, 0xb1, 0x2a, 0x9f, 0x0b, 0x29, 0xb2, + 0x78, 0x69, 0x26, 0xfb, 0xbf, 0x12, 0x29, 0x67, 0x0b, 0x0e, 0xd3, 0xca, 0xaf, 0x6f, 0x72, 0x28, 0x29, 0x21, 0xea, + 0x7e, 0x84, 0x12, 0x56, 0xc1, 0x5d, 0x9c, 0xeb, 0x2e, 0xc7, 0xce, 0xe0, 0x00, 0x35, 0xe8, 0xe5, 0xdd, 0x79, 0xc5, + 0xed, 0x82, 0x04, 0x48, 0x7f, 0x07, 0x7e, 0x21, 0xeb, 0x1b, 0x5e, 0x30, 0x2e, 0x96, 0x0b, 0xb2, 0x44, 0x46, 0x10, + 0x3d, 0x02, 0x40, 0x7d, 0x18, 0x18, 0x37, 0x1d, 0x74, 0x0f, 0x53, 0xb6, 0x6c, 0xb8, 0xb5, 0x8a, 0x81, 0xc0, 0xb5, + 0x6b, 0xca, 0x42, 0xf4, 0x36, 0x24, 0x46, 0xae, 0x27, 0xbc, 0xf4, 0x72, 0x74, 0xff, 0xec, 0x5a, 0xf6, 0x07, 0x86, + 0x27, 0x51, 0xdd, 0xb5, 0xe6, 0xf1, 0xcd, 0xab, 0xa7, 0xcd, 0xb4, 0x34, 0xde, 0x3f, 0x7c, 0x64, 0x8f, 0xef, 0xdd, + 0x9c, 0x05, 0x17, 0x82, 0x5c, 0x9e, 0x0b, 0x3c, 0xe6, 0x3f, 0x02, 0x41, 0x00, 0xcf, 0xb5, 0x87, 0xfa, 0xc3, 0x82, + 0x7b, 0x18, 0x01, 0x8f, 0x88, 0xa4, 0xd4, 0xd9, 0xaa, 0x45, 0x74, 0x83, 0x80, 0x2b, 0xa7, 0x36, 0x2d, 0x91, 0x11, + 0x81, 0x67, 0x44, 0xeb, 0x49, 0xc5, 0x18, 0xbe, 0xff, 0x9a, 0x5b, 0x33, 0x19, 0xf4, 0x40, 0x6e, 0x4d, 0xb9, 0x1b, + 0xc2, 0xda, 0xf8, 0xd3, 0x92, 0x4c, 0x22, 0x6f, 0x25, 0xc6, 0x68, 0x7a, 0x7e, 0x18, 0x99, 0x05, 0x04, 0x91, 0x26, + 0xbf}; +unsigned int key_1024_len = 609; diff --git a/samples/MqttClient_Hello/app/application.cpp b/samples/MqttClient_Hello/app/application.cpp index 5c54a92c96..2e08425d71 100644 --- a/samples/MqttClient_Hello/app/application.cpp +++ b/samples/MqttClient_Hello/app/application.cpp @@ -11,9 +11,13 @@ #define MQTT_URL2 "mqtts://attachix.com:8883" // (Need ENABLE_SSL) #define MQTT_URL3 "mqtt://frank:fiddle@192.168.100.107:1883" +#ifdef ENABLE_SSL +#include +#include +#define MQTT_URL MQTT_URL2 +#else #define MQTT_URL MQTT_URL1 - -const Url url(MQTT_URL); +#endif // Forward declarations void startMqttClient(); @@ -27,9 +31,9 @@ Timer procTimer; void checkMQTTDisconnect(TcpClient& client, bool flag) { if(flag == true) { - Serial.println("MQTT Broker Disconnected!!"); + Serial.println(_F("MQTT Broker Disconnected!!")); } else { - Serial.println("MQTT Broker Unreachable!!"); + Serial.println(_F("MQTT Broker Unreachable!!")); } // Restart connection attempt after few seconds @@ -38,7 +42,7 @@ void checkMQTTDisconnect(TcpClient& client, bool flag) void onMessageDelivered(uint16_t msgId, int type) { - Serial.printf("Message with id %d and QoS %d was delivered successfully.", msgId, + Serial.printf(_F("Message with id %d and QoS %d was delivered successfully."), msgId, (type == MQTT_MSG_PUBREC ? 2 : 1)); } @@ -49,11 +53,11 @@ void publishMessage() startMqttClient(); // Auto reconnect } - Serial.print("Let's publish message now. Memory free="); + Serial.print(_F("Let's publish message now. Memory free=")); Serial.println(system_get_free_heap_size()); - mqtt.publish("main/frameworks/sming", "Hello friends, from Internet of things :)"); + mqtt.publish(F("main/frameworks/sming"), F("Hello friends, from Internet of things :)")); - mqtt.publishWithQoS("important/frameworks/sming", "Request Return Delivery", 1, false, + mqtt.publishWithQoS(F("important/frameworks/sming"), F("Request Return Delivery"), 1, false, onMessageDelivered); // or publishWithQoS } @@ -61,7 +65,7 @@ void publishMessage() void onMessageReceived(String topic, String message) { Serial.print(topic); - Serial.print(":\r\n\t"); // Pretify alignment for printing + Serial.print(":\r\n\t"); // Prettify alignment for printing Serial.println(message); } @@ -71,31 +75,33 @@ void startMqttClient() procTimer.stop(); // 1. [Setup] - if(!mqtt.setWill("last/will", "The connection from this device is lost:(", 1, true)) { + if(!mqtt.setWill(F("last/will"), F("The connection from this device is lost:("), 1, true)) { debugf("Unable to set the last will and testament. Most probably there is not enough memory on the device."); } + mqtt.setConnectedHandler([](MqttClient& client, mqtt_message_t* message) { + Serial.print(_F("Connected to ")); + Serial.println(client.getRemoteIp()); + return 0; + }); + mqtt.setCompleteDelegate(checkMQTTDisconnect); mqtt.setCallback(onMessageReceived); #ifdef ENABLE_SSL - mqtt.addSslOptions(SSL_SERVER_VERIFY_LATER); - -#include -#include - - mqtt.setSslKeyCert(default_private_key, default_private_key_len, default_certificate, default_certificate_len, - nullptr, - /*freeAfterHandshake*/ false); - + mqtt.setSslInitHandler([](Ssl::Session& session) { + session.options.verifyLater = true; + session.keyCert.assign(default_private_key, sizeof(default_private_key), default_certificate, + sizeof(default_certificate), nullptr); + }); #endif // 2. [Connect] Url url(MQTT_URL); - Serial.print("Connecting to \t"); + Serial.print(_F("Connecting to ")); Serial.println(url); - mqtt.connect(url, "esp8266"); - mqtt.subscribe("main/status/#"); + mqtt.connect(url, F("esp8266")); + mqtt.subscribe(F("main/status/#")); } void onConnected(IpAddress ip, IpAddress netmask, IpAddress gateway) diff --git a/samples/SmtpClient/.cproject b/samples/SmtpClient/.cproject new file mode 100644 index 0000000000..4a9fd7f0a1 --- /dev/null +++ b/samples/SmtpClient/.cproject @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/SmtpClient/app/application.cpp b/samples/SmtpClient/app/application.cpp index dc5f8ddd37..840d13ec30 100644 --- a/samples/SmtpClient/app/application.cpp +++ b/samples/SmtpClient/app/application.cpp @@ -44,9 +44,7 @@ int onMailSent(SmtpClient& client, int code, char* status) void onConnected(IpAddress ip, IpAddress mask, IpAddress gateway) { -#ifdef ENABLE_SSL - client.addSslOptions(SSL_SERVER_VERIFY_LATER); -#endif + client.setSslInitHandler([](Ssl::Session& session) { session.options.verifyLater = true; }); client.onServerError(onServerError); diff --git a/samples/Websocket_Client/app/application.cpp b/samples/Websocket_Client/app/application.cpp index 2c1e469c0d..6f07db0bfc 100644 --- a/samples/Websocket_Client/app/application.cpp +++ b/samples/Websocket_Client/app/application.cpp @@ -72,9 +72,6 @@ void restart() msg_cnt = 0; wsClient.connect(String(ws_Url)); -#ifdef ENABLE_SSL - wsClient.getHttpConnection()->addSslOptions(SSL_SERVER_VERIFY_LATER); -#endif } void wsDisconnected(WebsocketConnection& wsConnection) @@ -133,10 +130,8 @@ void STAGotIP(IpAddress ip, IpAddress mask, IpAddress gateway) wsClient.setBinaryHandler(wsBinReceived); wsClient.setDisconnectionHandler(wsDisconnected); wsClient.setConnectionHandler(wsConnected); + wsClient.setSslInitHandler([](Ssl::Session& session) { session.options.verifyLater = true; }); wsClient.connect(String(ws_Url)); -#ifdef ENABLE_SSL - wsClient.getHttpConnection()->addSslOptions(SSL_SERVER_VERIFY_LATER); -#endif } void STADisconnect(const String& ssid, MacAddress bssid, WifiDisconnectReason reason) @@ -150,7 +145,7 @@ void STADisconnect(const String& ssid, MacAddress bssid, WifiDisconnectReason re void init() { Serial.begin(COM_SPEED_SERIAL); - Serial.systemDebugOutput(false); + Serial.systemDebugOutput(true); WifiAccessPoint.enable(false); WifiStation.config(WIFI_SSID, WIFI_PWD);