From 1d68fe9bc6e770f0f3ac590b98cfcadfe410a52e Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Wed, 10 Jun 2020 20:14:46 +0200 Subject: [PATCH 1/2] Cleaned TLS options and prepare for TELEGRAM --- tasmota/StackThunk_light.cpp | 2 +- tasmota/WiFiClientSecureLightBearSSL.cpp | 1827 +++++++++++----------- tasmota/WiFiClientSecureLightBearSSL.h | 442 +++--- tasmota/my_user_config.h | 12 + tasmota/tasmota.ino | 4 +- tasmota/tasmota_ca.ino | 93 +- tasmota/tasmota_globals.h | 2 +- 7 files changed, 1223 insertions(+), 1159 deletions(-) mode change 100644 => 100755 tasmota/WiFiClientSecureLightBearSSL.cpp mode change 100644 => 100755 tasmota/WiFiClientSecureLightBearSSL.h diff --git a/tasmota/StackThunk_light.cpp b/tasmota/StackThunk_light.cpp index 5dcc20d62..c9f9bc78e 100644 --- a/tasmota/StackThunk_light.cpp +++ b/tasmota/StackThunk_light.cpp @@ -40,7 +40,7 @@ uint32_t *stack_thunk_light_save = NULL; /* Saved A1 while in BearSSL */ uint32_t stack_thunk_light_refcnt = 0; //#define _stackSize (5600/4) -#if defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_TLS_FORCE_EC_CIPHER) +#ifdef USE_MQTT_TLS_FORCE_EC_CIPHER #define _stackSize (5300/4) // using a light version of bearssl we can save 300 bytes #else #define _stackSize (3600/4) // using a light version of bearssl we can save 2k diff --git a/tasmota/WiFiClientSecureLightBearSSL.cpp b/tasmota/WiFiClientSecureLightBearSSL.cpp old mode 100644 new mode 100755 index 434522b14..d0907788e --- a/tasmota/WiFiClientSecureLightBearSSL.cpp +++ b/tasmota/WiFiClientSecureLightBearSSL.cpp @@ -1,912 +1,915 @@ -/* - WiFiClientBearSSL- SSL client/server for esp8266 using BearSSL libraries - - Mostly compatible with Arduino WiFi shield library and standard - WiFiClient/ServerSecure (except for certificate handling). - - Copyright (c) 2018 Earle F. Philhower, III - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#include "my_user_config.h" -//#ifdef USE_MQTT_TLS -#if defined ESP8266 && (defined(USE_MQTT_TLS) || defined (USE_SENDMAIL)) - -//#define DEBUG_TLS - -#define LWIP_INTERNAL - -#include -#include -#include - -extern "C" { -#include "osapi.h" -#include "ets_sys.h" -} -#include "debug.h" -#include "WiFiClientSecureLightBearSSL.h" // needs to be before "ESP8266WiFi.h" to avoid conflict with Arduino headers -#include "ESP8266WiFi.h" -#include "WiFiClient.h" -#include "StackThunk_light.h" -#include "lwip/opt.h" -#include "lwip/ip.h" -#include "lwip/tcp.h" -#include "lwip/inet.h" -#include "lwip/netif.h" -#include -#include "c_types.h" - -#include -#ifndef ARDUINO_ESP8266_RELEASE_2_5_2 -#undef DEBUG_TLS -#endif - -#ifdef DEBUG_TLS -#include "coredecls.h" -#define LOG_HEAP_SIZE(a) _Log_heap_size(a) -void _Log_heap_size(const char *msg) { - register uint32_t *sp asm("a1"); - int freestack = 4 * (sp - g_pcont->stack); - Serial.printf("%s %d, Fragmentation=%d, Thunkstack=%d, Free stack=%d, FreeContStack=%d\n", - msg, ESP.getFreeHeap(), ESP.getHeapFragmentation(), stack_thunk_light_get_max_usage(), - freestack, ESP.getFreeContStack()); -} -#else -#define LOG_HEAP_SIZE(a) -#endif - -// Stack thunked versions of calls -// Initially in BearSSLHelpers.h -extern "C" { -extern unsigned char *thunk_light_br_ssl_engine_recvapp_buf( const br_ssl_engine_context *cc, size_t *len); -extern void thunk_light_br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len); -extern unsigned char *thunk_light_br_ssl_engine_recvrec_buf( const br_ssl_engine_context *cc, size_t *len); -extern void thunk_light_br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len); -extern unsigned char *thunk_light_br_ssl_engine_sendapp_buf( const br_ssl_engine_context *cc, size_t *len); -extern void thunk_light_br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len); -extern unsigned char *thunk_light_br_ssl_engine_sendrec_buf( const br_ssl_engine_context *cc, size_t *len); -extern void thunk_light_br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len); -}; - -// Second stack thunked helpers -make_stack_thunk_light(br_ssl_engine_recvapp_ack); -make_stack_thunk_light(br_ssl_engine_recvapp_buf); -make_stack_thunk_light(br_ssl_engine_recvrec_ack); -make_stack_thunk_light(br_ssl_engine_recvrec_buf); -make_stack_thunk_light(br_ssl_engine_sendapp_ack); -make_stack_thunk_light(br_ssl_engine_sendapp_buf); -make_stack_thunk_light(br_ssl_engine_sendrec_ack); -make_stack_thunk_light(br_ssl_engine_sendrec_buf); - -// create new version of Thunk function to store on SYS stack -// unless the Thunk was initialized. Thanks to AES128 GCM, we can keep -// symetric processing on the stack -void min_br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len) { - if (stack_thunk_light_get_refcnt()) { - return thunk_light_br_ssl_engine_recvapp_ack(cc, len); - } else { - return br_ssl_engine_recvapp_ack(cc, len); - } -} -unsigned char *min_br_ssl_engine_recvapp_buf(const br_ssl_engine_context *cc, size_t *len) { - if (stack_thunk_light_get_refcnt()) { - return thunk_light_br_ssl_engine_recvapp_buf(cc, len); - } else { - return br_ssl_engine_recvapp_buf(cc, len); - } -} -void min_br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len) { - if (stack_thunk_light_get_refcnt()) { - return thunk_light_br_ssl_engine_recvrec_ack(cc, len); - } else { - return br_ssl_engine_recvrec_ack(cc, len); - } -} -unsigned char *min_br_ssl_engine_recvrec_buf(const br_ssl_engine_context *cc, size_t *len) { - if (stack_thunk_light_get_refcnt()) { - return thunk_light_br_ssl_engine_recvrec_buf(cc, len); - } else { - return br_ssl_engine_recvrec_buf(cc, len); - } -} -void min_br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len) { - if (stack_thunk_light_get_refcnt()) { - return thunk_light_br_ssl_engine_sendapp_ack(cc, len); - } else { - return br_ssl_engine_sendapp_ack(cc, len); - } -} -unsigned char *min_br_ssl_engine_sendapp_buf(const br_ssl_engine_context *cc, size_t *len) { - if (stack_thunk_light_get_refcnt()) { - return thunk_light_br_ssl_engine_sendapp_buf(cc, len); - } else { - return br_ssl_engine_sendapp_buf(cc, len); - } -} -void min_br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len) { - if (stack_thunk_light_get_refcnt()) { - return thunk_light_br_ssl_engine_sendrec_ack(cc, len); - } else { - return br_ssl_engine_sendrec_ack(cc, len); - } -} -unsigned char *min_br_ssl_engine_sendrec_buf(const br_ssl_engine_context *cc, size_t *len) { - if (stack_thunk_light_get_refcnt()) { - return thunk_light_br_ssl_engine_sendrec_buf(cc, len); - } else { - return br_ssl_engine_sendrec_buf(cc, len); - } -} - -// Use min_ instead of original thunk_ -#define br_ssl_engine_recvapp_ack min_br_ssl_engine_recvapp_ack -#define br_ssl_engine_recvapp_buf min_br_ssl_engine_recvapp_buf -#define br_ssl_engine_recvrec_ack min_br_ssl_engine_recvrec_ack -#define br_ssl_engine_recvrec_buf min_br_ssl_engine_recvrec_buf -#define br_ssl_engine_sendapp_ack min_br_ssl_engine_sendapp_ack -#define br_ssl_engine_sendapp_buf min_br_ssl_engine_sendapp_buf -#define br_ssl_engine_sendrec_ack min_br_ssl_engine_sendrec_ack -#define br_ssl_engine_sendrec_buf min_br_ssl_engine_sendrec_buf - -//#define DEBUG_ESP_SSL -#ifdef DEBUG_ESP_SSL -#define DEBUG_BSSL(fmt, ...) DEBUG_ESP_PORT.printf_P((PGM_P)PSTR( "BSSL:" fmt), ## __VA_ARGS__) -//#define DEBUG_BSSL(fmt, ...) Serial.printf(fmt, ## __VA_ARGS__) -#else -#define DEBUG_BSSL(...) -#endif - -namespace BearSSL { - -void WiFiClientSecure_light::_clear() { - // TLS handshake may take more than the 5 second default timeout - _timeout = 10000; // 10 seconds max, it should never go over 6 seconds - - _sc = nullptr; - _ctx_present = false; - _eng = nullptr; - _iobuf_in = nullptr; - _iobuf_out = nullptr; - _now = 0; // You can override or ensure time() is correct w/configTime - setBufferSizes(1024, 1024); // reasonable minimum - _handshake_done = false; - _last_error = 0; - _recvapp_buf = nullptr; - _recvapp_len = 0; - _fingerprint_any = true; // by default accept all fingerprints - _fingerprint1 = nullptr; - _fingerprint2 = nullptr; - _chain_P = nullptr; - _sk_ec_P = nullptr; - _ta_P = nullptr; - _max_thunkstack_use = 0; -} - -// Constructor -WiFiClientSecure_light::WiFiClientSecure_light(int recv, int xmit) : WiFiClient() { - _clear(); -LOG_HEAP_SIZE("StackThunk before"); - //stack_thunk_light_add_ref(); -LOG_HEAP_SIZE("StackThunk after"); - // now finish the setup - setBufferSizes(recv, xmit); // reasonable minimum - allocateBuffers(); -} - -WiFiClientSecure_light::~WiFiClientSecure_light() { - if (_client) { - _client->unref(); - _client = nullptr; - } - //_cipher_list = nullptr; // std::shared will free if last reference - _freeSSL(); -} - -void WiFiClientSecure_light::allocateBuffers(void) { - // We prefer to allocate all buffers at start, rather than lazy allocation and deallocation - // in the long run it avoids heap fragmentation and improves stability - LOG_HEAP_SIZE("allocateBuffers before"); - _sc = std::make_shared(); - LOG_HEAP_SIZE("allocateBuffers ClientContext"); - _iobuf_in = std::shared_ptr(new unsigned char[_iobuf_in_size], std::default_delete()); - _iobuf_out = std::shared_ptr(new unsigned char[_iobuf_out_size], std::default_delete()); - LOG_HEAP_SIZE("allocateBuffers after"); -} - -void WiFiClientSecure_light::setClientECCert(const br_x509_certificate *cert, const br_ec_private_key *sk, - unsigned allowed_usages, unsigned cert_issuer_key_type) { - _chain_P = cert; - _sk_ec_P = sk; - _allowed_usages = allowed_usages; - _cert_issuer_key_type = cert_issuer_key_type; -} - -void WiFiClientSecure_light::setTrustAnchor(const br_x509_trust_anchor *ta) { - _ta_P = ta; -} - -void WiFiClientSecure_light::setBufferSizes(int recv, int xmit) { - // Following constants taken from bearssl/src/ssl/ssl_engine.c (not exported unfortunately) - const int MAX_OUT_OVERHEAD = 85; - const int MAX_IN_OVERHEAD = 325; - - // The data buffers must be between 512B and 16KB - recv = std::max(512, std::min(16384, recv)); - xmit = std::max(512, std::min(16384, xmit)); - - // Add in overhead for SSL protocol - recv += MAX_IN_OVERHEAD; - xmit += MAX_OUT_OVERHEAD; - _iobuf_in_size = recv; - _iobuf_out_size = xmit; -} - -bool WiFiClientSecure_light::stop(unsigned int maxWaitMs) { -#ifdef ARDUINO_ESP8266_RELEASE_2_4_2 - WiFiClient::stop(); // calls our virtual flush() - _freeSSL(); - return true; -#else - bool ret = WiFiClient::stop(maxWaitMs); // calls our virtual flush() - _freeSSL(); - return ret; -#endif -} - -bool WiFiClientSecure_light::flush(unsigned int maxWaitMs) { - (void) _run_until(BR_SSL_SENDAPP); -#ifdef ARDUINO_ESP8266_RELEASE_2_4_2 - WiFiClient::flush(); -#else - return WiFiClient::flush(maxWaitMs); -#endif -} - -int WiFiClientSecure_light::connect(IPAddress ip, uint16_t port) { - clearLastError(); - if (!WiFiClient::connect(ip, port)) { - setLastError(ERR_TCP_CONNECT); - return 0; - } - return _connectSSL(nullptr); -} - -int WiFiClientSecure_light::connect(const char* name, uint16_t port) { - IPAddress remote_addr; - clearLastError(); - if (!WiFi.hostByName(name, remote_addr)) { - DEBUG_BSSL("connect: Name loopup failure\n"); - setLastError(ERR_CANT_RESOLVE_IP); - return 0; - } - if (!WiFiClient::connect(remote_addr, port)) { - DEBUG_BSSL("connect: Unable to connect TCP socket\n"); - _last_error = ERR_TCP_CONNECT; - return 0; - } - LOG_HEAP_SIZE("Before calling _connectSSL"); - return _connectSSL(name); -} - -void WiFiClientSecure_light::_freeSSL() { - _ctx_present = false; - _recvapp_buf = nullptr; - _recvapp_len = 0; - // This connection is toast - _handshake_done = false; -} - -bool WiFiClientSecure_light::_clientConnected() { - return (_client && _client->state() == ESTABLISHED); -} - -uint8_t WiFiClientSecure_light::connected() { - if (available() || (_clientConnected() && _handshake_done)) { - return true; - } - return false; -} - -size_t WiFiClientSecure_light::_write(const uint8_t *buf, size_t size, bool pmem) { - size_t sent_bytes = 0; - - if (!connected() || !size || !_handshake_done) { - return 0; - } - - do { - // Ensure we yield if we need multiple fragments to avoid WDT - if (sent_bytes) { - optimistic_yield(1000); - } - - // Get BearSSL to a state where we can send - if (_run_until(BR_SSL_SENDAPP) < 0) { - break; - } - - if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) { - size_t sendapp_len; - unsigned char *sendapp_buf = br_ssl_engine_sendapp_buf(_eng, &sendapp_len); - int to_send = size > sendapp_len ? sendapp_len : size; - if (pmem) { - memcpy_P(sendapp_buf, buf, to_send); - } else { - memcpy(sendapp_buf, buf, to_send); - } - br_ssl_engine_sendapp_ack(_eng, to_send); - br_ssl_engine_flush(_eng, 0); - flush(); - buf += to_send; - sent_bytes += to_send; - size -= to_send; - } else { - break; - } - } while (size); - - LOG_HEAP_SIZE("_write"); - return sent_bytes; -} - -size_t WiFiClientSecure_light::write(const uint8_t *buf, size_t size) { - return _write(buf, size, false); -} - -size_t WiFiClientSecure_light::write_P(PGM_P buf, size_t size) { - return _write((const uint8_t *)buf, size, true); -} - -// We have to manually read and send individual chunks. -size_t WiFiClientSecure_light::write(Stream& stream) { - size_t totalSent = 0; - size_t countRead; - size_t countSent; - - if (!connected() || !_handshake_done) { - DEBUG_BSSL("write: Connect/handshake not completed yet\n"); - return 0; - } - - do { - uint8_t temp[256]; // Temporary chunk size same as ClientContext - countSent = 0; - countRead = stream.readBytes(temp, sizeof(temp)); - if (countRead) { - countSent = _write((const uint8_t*)temp, countRead, true); - totalSent += countSent; - } - yield(); // Feed the WDT - } while ((countSent == countRead) && (countSent > 0)); - return totalSent; -} - -int WiFiClientSecure_light::read(uint8_t *buf, size_t size) { - if (!ctx_present() || !_handshake_done) { - return -1; - } - - int avail = available(); - bool conn = connected(); - if (!avail && conn) { - return 0; // We're still connected, but nothing to read - } - if (!avail && !conn) { - DEBUG_BSSL("read: Not connected, none left available\n"); - return -1; - } - - if (avail) { - // Take data from the recvapp buffer - int to_copy = _recvapp_len < size ? _recvapp_len : size; - memcpy(buf, _recvapp_buf, to_copy); - br_ssl_engine_recvapp_ack(_eng, to_copy); - _recvapp_buf = nullptr; - _recvapp_len = 0; - return to_copy; - } - - if (!conn) { - DEBUG_BSSL("read: Not connected\n"); - return -1; - } - return 0; // If we're connected, no error but no read. -} - -int WiFiClientSecure_light::read() { - uint8_t c; - if (1 == read(&c, 1)) { - return c; - } - DEBUG_BSSL("read: failed\n"); - return -1; -} - -int WiFiClientSecure_light::available() { - if (_recvapp_buf) { - return _recvapp_len; // Anything from last call? - } - _recvapp_buf = nullptr; - _recvapp_len = 0; - if (!ctx_present() || _run_until(BR_SSL_RECVAPP, false) < 0) { - return 0; - } - int st = br_ssl_engine_current_state(_eng); - if (st == BR_SSL_CLOSED) { - return 0; // Nothing leftover, SSL is closed - } - if (st & BR_SSL_RECVAPP) { - _recvapp_buf = br_ssl_engine_recvapp_buf(_eng, &_recvapp_len); - return _recvapp_len; - } - - return 0; -} - -int WiFiClientSecure_light::peek() { - if (!ctx_present() || !available()) { - DEBUG_BSSL("peek: Not connected, none left available\n"); - return -1; - } - if (_recvapp_buf && _recvapp_len) { - return _recvapp_buf[0]; - } - DEBUG_BSSL("peek: No data left\n"); - return -1; -} - -size_t WiFiClientSecure_light::peekBytes(uint8_t *buffer, size_t length) { - size_t to_copy = 0; - if (!ctx_present()) { - DEBUG_BSSL("peekBytes: Not connected\n"); - return 0; - } - - _startMillis = millis(); - while ((available() < (int) length) && ((millis() - _startMillis) < 5000)) { - yield(); - } - - to_copy = _recvapp_len < length ? _recvapp_len : length; - memcpy(buffer, _recvapp_buf, to_copy); - return to_copy; -} - -/* --- Copied almost verbatim from BEARSSL SSL_IO.C --- - Run the engine, until the specified target state is achieved, or - an error occurs. The target state is SENDAPP, RECVAPP, or the - combination of both (the combination matches either). When a match is - achieved, this function returns 0. On error, it returns -1. -*/ -int WiFiClientSecure_light::_run_until(unsigned target, bool blocking) { -//LOG_HEAP_SIZE("_run_until 1"); - if (!ctx_present()) { - DEBUG_BSSL("_run_until: Not connected\n"); - return -1; - } - for (int no_work = 0; blocking || no_work < 2;) { - if (blocking) { - // Only for blocking operations can we afford to yield() - optimistic_yield(100); - } - - int state; - state = br_ssl_engine_current_state(_eng); - if (state & BR_SSL_CLOSED) { - return -1; - } - - if (!(_client->state() == ESTABLISHED) && !WiFiClient::available()) { - return (state & target) ? 0 : -1; - } - - /* - If there is some record data to send, do it. This takes - precedence over everything else. - */ - if (state & BR_SSL_SENDREC) { - unsigned char *buf; - size_t len; - int wlen; - - buf = br_ssl_engine_sendrec_buf(_eng, &len); - wlen = WiFiClient::write(buf, len); - if (wlen <= 0) { - /* - 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. - */ - return -1; - } - if (wlen > 0) { - br_ssl_engine_sendrec_ack(_eng, wlen); - } - no_work = 0; - continue; - } - - /* - If we reached our target, then we are finished. - */ - if (state & target) { - return 0; - } - /* - If some application data must be read, and we did not - exit, then this means that we are trying to write data, - and that's not possible until the application data is - read. This may happen if using a shared in/out buffer, - and the underlying protocol is not strictly half-duplex. - This is unrecoverable here, so we report an error. - */ - if (state & BR_SSL_RECVAPP) { - DEBUG_BSSL("_run_until: Fatal protocol state\n"); - return -1; - } - /* - If we reached that point, then either we are trying - to read data and there is some, or the engine is stuck - until a new record is obtained. - */ - if (state & BR_SSL_RECVREC) { - if (WiFiClient::available()) { - unsigned char *buf; - size_t len; - int rlen; - - buf = br_ssl_engine_recvrec_buf(_eng, &len); - rlen = WiFiClient::read(buf, len); - if (rlen < 0) { - return -1; - } - if (rlen > 0) { - br_ssl_engine_recvrec_ack(_eng, rlen); - } - no_work = 0; - continue; - } - } - /* - We can reach that point if the target RECVAPP, and - the state contains SENDAPP only. This may happen with - a shared in/out buffer. In that case, we must flush - the buffered data to "make room" for a new incoming - record. - */ - br_ssl_engine_flush(_eng, 0); - - no_work++; // We didn't actually advance here - } - // We only get here if we ran through the loop without getting anything done - return -1; -} - -bool WiFiClientSecure_light::_wait_for_handshake() { - _handshake_done = false; - while (!_handshake_done && _clientConnected()) { - int ret = _run_until(BR_SSL_SENDAPP); - if (ret < 0) { - DEBUG_BSSL("_wait_for_handshake: failed\n"); - break; - } - if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) { - _handshake_done = true; - } - optimistic_yield(1000); - } - return _handshake_done; -} - -static uint8_t htoi (unsigned char c) -{ - if (c>='0' && c <='9') return c - '0'; - else if (c>='A' && c<='F') return 10 + c - 'A'; - else if (c>='a' && c<='f') return 10 + c - 'a'; - else return 255; -} - -extern "C" { - - // see https://stackoverflow.com/questions/6357031/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-in-c - void tohex(unsigned char * in, size_t insz, char * out, size_t outsz) { - unsigned char * pin = in; - static const char * hex = "0123456789ABCDEF"; - char * pout = out; - for(; pin < in+insz; pout +=3, pin++){ - pout[0] = hex[(*pin>>4) & 0xF]; - pout[1] = hex[ *pin & 0xF]; - pout[2] = ':'; - if (pout + 3 - out > outsz){ - /* Better to truncate output string than overflow buffer */ - /* it would be still better to either return a status */ - /* or ensure the target buffer is large enough and it never happen */ - break; - } - } - pout[-1] = 0; - } - - - // BearSSL doesn't define a true insecure decoder, so we make one ourselves - // from the simple parser. It generates the issuer and subject hashes and - // the SHA1 fingerprint, only one (or none!) of which will be used to - // "verify" the certificate. - - // Private x509 decoder state - struct br_x509_pubkeyfingerprint_context { - const br_x509_class *vtable; - bool done_cert; // did we parse the first cert already? - bool fingerprint_all; - uint8_t *pubkey_recv_fingerprint; - const uint8_t *fingerprint1; - const uint8_t *fingerprint2; - unsigned usages; // pubkey usage - br_x509_decoder_context ctx; // defined in BearSSL - }; - - // Callback on the first byte of any certificate - static void pubkeyfingerprint_start_chain(const br_x509_class **ctx, const char *server_name) { - br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx; - // Don't process anything but the first certificate in the chain - if (!xc->done_cert) { - br_x509_decoder_init(&xc->ctx, nullptr, nullptr, nullptr, nullptr); - } - (void)server_name; // ignore server name - } - - // Callback for each certificate present in the chain (but only operates - // on the first one by design). - static void pubkeyfingerprint_start_cert(const br_x509_class **ctx, uint32_t length) { - (void) ctx; // do nothing - (void) length; - } - - // Callback for each byte stream in the chain. Only process first cert. - static void pubkeyfingerprint_append(const br_x509_class **ctx, const unsigned char *buf, size_t len) { - br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx; - // Don't process anything but the first certificate in the chain - if (!xc->done_cert) { - br_x509_decoder_push(&xc->ctx, (const void*)buf, len); - } - } - - // Callback on individual cert end. - static void pubkeyfingerprint_end_cert(const br_x509_class **ctx) { - br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx; - xc->done_cert = true; // first cert already processed - } - - static void pubkeyfingerprint_pubkey_fingerprint(br_sha1_context *shactx, br_rsa_public_key rsakey) { - br_sha1_init(shactx); - br_sha1_update(shactx, "ssh-rsa", 7); // tag - br_sha1_update(shactx, rsakey.e, rsakey.elen); // exponent - br_sha1_update(shactx, rsakey.n, rsakey.nlen); // modulus - } - - // Callback when complete chain has been parsed. - // Return 0 on validation success, !0 on validation error - static unsigned pubkeyfingerprint_end_chain(const br_x509_class **ctx) { - br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx; - - br_sha1_context sha1_context; - pubkeyfingerprint_pubkey_fingerprint(&sha1_context, xc->ctx.pkey.key.rsa); - br_sha1_out(&sha1_context, xc->pubkey_recv_fingerprint); // copy to fingerprint - - if (!xc->fingerprint_all) { - if (0 == memcmp(xc->fingerprint1, xc->pubkey_recv_fingerprint, 20)) { - return 0; - } - if (0 == memcmp(xc->fingerprint2, xc->pubkey_recv_fingerprint, 20)) { - return 0; - } - return 1; // no match, error - } else { - // Default (no validation at all) or no errors in prior checks = success. - return 0; - } - } - - // Return the public key from the validator (set by x509_minimal) - static const br_x509_pkey *pubkeyfingerprint_get_pkey(const br_x509_class *const *ctx, unsigned *usages) { - const br_x509_pubkeyfingerprint_context *xc = (const br_x509_pubkeyfingerprint_context *)ctx; - - if (usages != NULL) { - *usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN; // I said we were insecure! - } - return &xc->ctx.pkey; - } - - // Set up the x509 insecure data structures for BearSSL core to use. - void br_x509_pubkeyfingerprint_init(br_x509_pubkeyfingerprint_context *ctx, - const uint8_t *fingerprint1, const uint8_t *fingerprint2, - uint8_t *recv_fingerprint, - bool fingerprint_all) { - static const br_x509_class br_x509_pubkeyfingerprint_vtable PROGMEM = { - sizeof(br_x509_pubkeyfingerprint_context), - pubkeyfingerprint_start_chain, - pubkeyfingerprint_start_cert, - pubkeyfingerprint_append, - pubkeyfingerprint_end_cert, - pubkeyfingerprint_end_chain, - pubkeyfingerprint_get_pkey - }; - - memset(ctx, 0, sizeof * ctx); - ctx->vtable = &br_x509_pubkeyfingerprint_vtable; - ctx->done_cert = false; - ctx->fingerprint1 = fingerprint1; - ctx->fingerprint2 = fingerprint2; - ctx->pubkey_recv_fingerprint = recv_fingerprint; - ctx->fingerprint_all = fingerprint_all; - } - - // We limit to a single cipher to reduce footprint - // we reference it, don't put in PROGMEM - static const uint16_t suites[] = { -#if defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_TLS_FORCE_EC_CIPHER) - BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 -#else - BR_TLS_RSA_WITH_AES_128_GCM_SHA256 -#endif - }; - - // Default initializion for our SSL clients - static void br_ssl_client_base_init(br_ssl_client_context *cc) { - br_ssl_client_zero(cc); - // forbid SSL renegociation, as we free the Private Key after handshake - br_ssl_engine_add_flags(&cc->eng, BR_OPT_NO_RENEGOTIATION); - - br_ssl_engine_set_versions(&cc->eng, BR_TLS12, BR_TLS12); - br_ssl_engine_set_suites(&cc->eng, suites, (sizeof suites) / (sizeof suites[0])); - br_ssl_client_set_default_rsapub(cc); - br_ssl_engine_set_default_rsavrfy(&cc->eng); - - // install hashes - br_ssl_engine_set_hash(&cc->eng, br_sha256_ID, &br_sha256_vtable); - br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf); - - // AES CTR/GCM small version, not contstant time (we don't really care here as there is no TPM anyways) - br_ssl_engine_set_gcm(&cc->eng, &br_sslrec_in_gcm_vtable, &br_sslrec_out_gcm_vtable); - br_ssl_engine_set_aes_ctr(&cc->eng, &br_aes_small_ctr_vtable); - br_ssl_engine_set_ghash(&cc->eng, &br_ghash_ctmul32); - -#if defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_TLS_FORCE_EC_CIPHER) - // we support only P256 EC curve for AWS IoT, no EC curve for Letsencrypt unless forced - br_ssl_engine_set_ec(&cc->eng, &br_ec_p256_m15); -#endif - } -} - -// Called by connect() to do the actual SSL setup and handshake. -// Returns if the SSL handshake succeeded. -bool WiFiClientSecure_light::_connectSSL(const char* hostName) { -#ifdef USE_MQTT_AWS_IOT - if ((!_chain_P) || (!_sk_ec_P)) { - setLastError(ERR_MISSING_EC_KEY); - return false; - } -#endif - - // Validation context, either full CA validation or checking only fingerprints -#ifdef USE_MQTT_TLS_CA_CERT - br_x509_minimal_context *x509_minimal; -#else - br_x509_pubkeyfingerprint_context *x509_insecure; -#endif - - LOG_HEAP_SIZE("_connectSSL.start"); - - do { // used to exit on Out of Memory error and keep all cleanup code at the same place - // ============================================================ - // allocate Thunk stack, move to alternate stack and initialize - stack_thunk_light_add_ref(); - LOG_HEAP_SIZE("Thunk allocated"); - DEBUG_BSSL("_connectSSL: start connection\n"); - _freeSSL(); - clearLastError(); - if (!stack_thunk_light_get_stack_bot()) break; - - _ctx_present = true; - _eng = &_sc->eng; // Allocation/deallocation taken care of by the _sc shared_ptr - - br_ssl_client_base_init(_sc.get()); - - // ============================================================ - // Allocatte and initialize Decoder Context - LOG_HEAP_SIZE("_connectSSL before DecoderContext allocation"); - // Only failure possible in the installation is OOM - #ifdef USE_MQTT_TLS_CA_CERT - x509_minimal = (br_x509_minimal_context*) malloc(sizeof(br_x509_minimal_context)); - if (!x509_minimal) break; - br_x509_minimal_init(x509_minimal, &br_sha256_vtable, _ta_P, 1); - br_x509_minimal_set_rsa(x509_minimal, br_ssl_engine_get_rsavrfy(_eng)); - br_x509_minimal_set_hash(x509_minimal, br_sha256_ID, &br_sha256_vtable); - br_ssl_engine_set_x509(_eng, &x509_minimal->vtable); - - #else - x509_insecure = (br_x509_pubkeyfingerprint_context*) malloc(sizeof(br_x509_pubkeyfingerprint_context)); - //x509_insecure = std::unique_ptr(new br_x509_pubkeyfingerprint_context); - if (!x509_insecure) break; - br_x509_pubkeyfingerprint_init(x509_insecure, _fingerprint1, _fingerprint2, _recv_fingerprint, _fingerprint_any); - br_ssl_engine_set_x509(_eng, &x509_insecure->vtable); - #endif - LOG_HEAP_SIZE("_connectSSL after DecoderContext allocation"); - - // ============================================================ - // Set send/receive buffers - br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size); - - // ============================================================ - // allocate Private key if needed, only if USE_MQTT_AWS_IOT - LOG_HEAP_SIZE("_connectSSL before PrivKey allocation"); - #ifdef USE_MQTT_AWS_IOT - // ============================================================ - // Set the EC Private Key, only USE_MQTT_AWS_IOT - // limited to P256 curve - br_ssl_client_set_single_ec(_sc.get(), _chain_P, 1, - _sk_ec_P, _allowed_usages, - _cert_issuer_key_type, &br_ec_p256_m15, br_ecdsa_sign_asn1_get_default()); - #endif // USE_MQTT_AWS_IOT - - // ============================================================ - // Start TLS connection, ALL - if (!br_ssl_client_reset(_sc.get(), hostName, 0)) break; - - auto ret = _wait_for_handshake(); - #ifdef DEBUG_ESP_SSL - if (!ret) { - DEBUG_BSSL("Couldn't connect. Error = %d\n", getLastError()); - } else { - DEBUG_BSSL("Connected! MFLNStatus = %d\n", getMFLNStatus()); - } - #endif - LOG_HEAP_SIZE("_connectSSL.end"); - _max_thunkstack_use = stack_thunk_light_get_max_usage(); - stack_thunk_light_del_ref(); - //stack_thunk_light_repaint(); - LOG_HEAP_SIZE("_connectSSL.end, freeing StackThunk"); - - #ifdef USE_MQTT_TLS_CA_CERT - free(x509_minimal); - #else - free(x509_insecure); - #endif - LOG_HEAP_SIZE("_connectSSL after release of Priv Key"); - return ret; - } while (0); - - // ============================================================ - // if we arrived here, this means we had an OOM error, cleaning up - setLastError(ERR_OOM); - DEBUG_BSSL("_connectSSL: Out of memory\n"); - stack_thunk_light_del_ref(); -#ifdef USE_MQTT_TLS_CA_CERT - free(x509_minimal); -#else - free(x509_insecure); -#endif - LOG_HEAP_SIZE("_connectSSL clean_on_error"); - return false; -} - -}; - -#include "t_bearssl_tasmota_config.h" - -#endif // USE_MQTT_TLS +/* + WiFiClientBearSSL- SSL client/server for esp8266 using BearSSL libraries + - Mostly compatible with Arduino WiFi shield library and standard + WiFiClient/ServerSecure (except for certificate handling). + + Copyright (c) 2018 Earle F. Philhower, III + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "my_user_config.h" +#if defined(ESP8266) && defined(USE_TLS) + +// #define DEBUG_TLS +// #define DEBUG_ESP_SSL + +#define LWIP_INTERNAL + +#include +#include +#include + +extern "C" { +#include "osapi.h" +#include "ets_sys.h" +} +#include "debug.h" +#include "WiFiClientSecureLightBearSSL.h" // needs to be before "ESP8266WiFi.h" to avoid conflict with Arduino headers +#include "ESP8266WiFi.h" +#include "WiFiClient.h" +#include "StackThunk_light.h" +#include "lwip/opt.h" +#include "lwip/ip.h" +#include "lwip/tcp.h" +#include "lwip/inet.h" +#include "lwip/netif.h" +#include +#include "c_types.h" + +#include +#ifndef ARDUINO_ESP8266_RELEASE_2_5_2 +#undef DEBUG_TLS +#endif + +#ifdef DEBUG_TLS +#include "coredecls.h" +#define LOG_HEAP_SIZE(a) _Log_heap_size(a) +void _Log_heap_size(const char *msg) { + register uint32_t *sp asm("a1"); + int freestack = 4 * (sp - g_pcont->stack); + Serial.printf("%s %d, Fragmentation=%d, Thunkstack=%d, Free stack=%d, FreeContStack=%d\n", + msg, ESP.getFreeHeap(), ESP.getHeapFragmentation(), stack_thunk_light_get_max_usage(), + freestack, ESP.getFreeContStack()); +} +#else +#define LOG_HEAP_SIZE(a) +#endif + +// Stack thunked versions of calls +// Initially in BearSSLHelpers.h +extern "C" { +extern unsigned char *thunk_light_br_ssl_engine_recvapp_buf( const br_ssl_engine_context *cc, size_t *len); +extern void thunk_light_br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len); +extern unsigned char *thunk_light_br_ssl_engine_recvrec_buf( const br_ssl_engine_context *cc, size_t *len); +extern void thunk_light_br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len); +extern unsigned char *thunk_light_br_ssl_engine_sendapp_buf( const br_ssl_engine_context *cc, size_t *len); +extern void thunk_light_br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len); +extern unsigned char *thunk_light_br_ssl_engine_sendrec_buf( const br_ssl_engine_context *cc, size_t *len); +extern void thunk_light_br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len); +}; + +// Second stack thunked helpers +make_stack_thunk_light(br_ssl_engine_recvapp_ack); +make_stack_thunk_light(br_ssl_engine_recvapp_buf); +make_stack_thunk_light(br_ssl_engine_recvrec_ack); +make_stack_thunk_light(br_ssl_engine_recvrec_buf); +make_stack_thunk_light(br_ssl_engine_sendapp_ack); +make_stack_thunk_light(br_ssl_engine_sendapp_buf); +make_stack_thunk_light(br_ssl_engine_sendrec_ack); +make_stack_thunk_light(br_ssl_engine_sendrec_buf); + +// create new version of Thunk function to store on SYS stack +// unless the Thunk was initialized. Thanks to AES128 GCM, we can keep +// symetric processing on the stack +void min_br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_recvapp_ack(cc, len); + } else { + return br_ssl_engine_recvapp_ack(cc, len); + } +} +unsigned char *min_br_ssl_engine_recvapp_buf(const br_ssl_engine_context *cc, size_t *len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_recvapp_buf(cc, len); + } else { + return br_ssl_engine_recvapp_buf(cc, len); + } +} +void min_br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_recvrec_ack(cc, len); + } else { + return br_ssl_engine_recvrec_ack(cc, len); + } +} +unsigned char *min_br_ssl_engine_recvrec_buf(const br_ssl_engine_context *cc, size_t *len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_recvrec_buf(cc, len); + } else { + return br_ssl_engine_recvrec_buf(cc, len); + } +} +void min_br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_sendapp_ack(cc, len); + } else { + return br_ssl_engine_sendapp_ack(cc, len); + } +} +unsigned char *min_br_ssl_engine_sendapp_buf(const br_ssl_engine_context *cc, size_t *len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_sendapp_buf(cc, len); + } else { + return br_ssl_engine_sendapp_buf(cc, len); + } +} +void min_br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_sendrec_ack(cc, len); + } else { + return br_ssl_engine_sendrec_ack(cc, len); + } +} +unsigned char *min_br_ssl_engine_sendrec_buf(const br_ssl_engine_context *cc, size_t *len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_sendrec_buf(cc, len); + } else { + return br_ssl_engine_sendrec_buf(cc, len); + } +} + +// Use min_ instead of original thunk_ +#define br_ssl_engine_recvapp_ack min_br_ssl_engine_recvapp_ack +#define br_ssl_engine_recvapp_buf min_br_ssl_engine_recvapp_buf +#define br_ssl_engine_recvrec_ack min_br_ssl_engine_recvrec_ack +#define br_ssl_engine_recvrec_buf min_br_ssl_engine_recvrec_buf +#define br_ssl_engine_sendapp_ack min_br_ssl_engine_sendapp_ack +#define br_ssl_engine_sendapp_buf min_br_ssl_engine_sendapp_buf +#define br_ssl_engine_sendrec_ack min_br_ssl_engine_sendrec_ack +#define br_ssl_engine_sendrec_buf min_br_ssl_engine_sendrec_buf + +//#define DEBUG_ESP_SSL +#ifdef DEBUG_ESP_SSL +//#define DEBUG_BSSL(fmt, ...) DEBUG_ESP_PORT.printf_P((PGM_P)PSTR( "BSSL:" fmt), ## __VA_ARGS__) +#define DEBUG_BSSL(fmt, ...) Serial.printf(fmt, ## __VA_ARGS__) +#else +#define DEBUG_BSSL(...) +#endif + +namespace BearSSL { + +void WiFiClientSecure_light::_clear() { + // TLS handshake may take more than the 5 second default timeout + _timeout = 10000; // 10 seconds max, it should never go over 6 seconds + + _sc = nullptr; + _ctx_present = false; + _eng = nullptr; + _iobuf_in = nullptr; + _iobuf_out = nullptr; + _now = 0; // You can override or ensure time() is correct w/configTime + setBufferSizes(1024, 1024); // reasonable minimum + _handshake_done = false; + _last_error = 0; + _recvapp_buf = nullptr; + _recvapp_len = 0; + _fingerprint_any = true; // by default accept all fingerprints + _fingerprint1 = nullptr; + _fingerprint2 = nullptr; + _chain_P = nullptr; + _sk_ec_P = nullptr; + _ta_P = nullptr; + _max_thunkstack_use = 0; +} + +// Constructor +WiFiClientSecure_light::WiFiClientSecure_light(int recv, int xmit) : WiFiClient() { + _clear(); +LOG_HEAP_SIZE("StackThunk before"); + //stack_thunk_light_add_ref(); +LOG_HEAP_SIZE("StackThunk after"); + // now finish the setup + setBufferSizes(recv, xmit); // reasonable minimum + allocateBuffers(); +} + +WiFiClientSecure_light::~WiFiClientSecure_light() { + if (_client) { + _client->unref(); + _client = nullptr; + } + //_cipher_list = nullptr; // std::shared will free if last reference + _freeSSL(); +} + +void WiFiClientSecure_light::allocateBuffers(void) { + // We prefer to allocate all buffers at start, rather than lazy allocation and deallocation + // in the long run it avoids heap fragmentation and improves stability + LOG_HEAP_SIZE("allocateBuffers before"); + _sc = std::make_shared(); + LOG_HEAP_SIZE("allocateBuffers ClientContext"); + _iobuf_in = std::shared_ptr(new unsigned char[_iobuf_in_size], std::default_delete()); + _iobuf_out = std::shared_ptr(new unsigned char[_iobuf_out_size], std::default_delete()); + LOG_HEAP_SIZE("allocateBuffers after"); +} + +void WiFiClientSecure_light::setClientECCert(const br_x509_certificate *cert, const br_ec_private_key *sk, + unsigned allowed_usages, unsigned cert_issuer_key_type) { + _chain_P = cert; + _sk_ec_P = sk; + _allowed_usages = allowed_usages; + _cert_issuer_key_type = cert_issuer_key_type; +} + +void WiFiClientSecure_light::setTrustAnchor(const br_x509_trust_anchor *ta) { + _ta_P = ta; +} + +void WiFiClientSecure_light::setBufferSizes(int recv, int xmit) { + // Following constants taken from bearssl/src/ssl/ssl_engine.c (not exported unfortunately) + const int MAX_OUT_OVERHEAD = 85; + const int MAX_IN_OVERHEAD = 325; + + // The data buffers must be between 512B and 16KB + recv = std::max(512, std::min(16384, recv)); + xmit = std::max(512, std::min(16384, xmit)); + + // Add in overhead for SSL protocol + recv += MAX_IN_OVERHEAD; + xmit += MAX_OUT_OVERHEAD; + _iobuf_in_size = recv; + _iobuf_out_size = xmit; +} + +bool WiFiClientSecure_light::stop(unsigned int maxWaitMs) { +#ifdef ARDUINO_ESP8266_RELEASE_2_4_2 + WiFiClient::stop(); // calls our virtual flush() + _freeSSL(); + return true; +#else + bool ret = WiFiClient::stop(maxWaitMs); // calls our virtual flush() + _freeSSL(); + return ret; +#endif +} + +bool WiFiClientSecure_light::flush(unsigned int maxWaitMs) { + (void) _run_until(BR_SSL_SENDAPP); +#ifdef ARDUINO_ESP8266_RELEASE_2_4_2 + WiFiClient::flush(); +#else + return WiFiClient::flush(maxWaitMs); +#endif +} + +int WiFiClientSecure_light::connect(IPAddress ip, uint16_t port) { + DEBUG_BSSL("connect(%s,%d)", ip.toString().c_str(), port); + clearLastError(); + if (!WiFiClient::connect(ip, port)) { + setLastError(ERR_TCP_CONNECT); + return 0; + } + return _connectSSL(nullptr); +} + +int WiFiClientSecure_light::connect(const char* name, uint16_t port) { + DEBUG_BSSL("connect(%s,%d)\n", name, port); + IPAddress remote_addr; + clearLastError(); + if (!WiFi.hostByName(name, remote_addr)) { + DEBUG_BSSL("connect: Name loopup failure\n"); + setLastError(ERR_CANT_RESOLVE_IP); + return 0; + } + DEBUG_BSSL("connect(%s,%d)\n", remote_addr.toString().c_str(), port); + if (!WiFiClient::connect(remote_addr, port)) { + DEBUG_BSSL("connect: Unable to connect TCP socket\n"); + _last_error = ERR_TCP_CONNECT; + return 0; + } + LOG_HEAP_SIZE("Before calling _connectSSL"); + return _connectSSL(name); +} + +void WiFiClientSecure_light::_freeSSL() { + _ctx_present = false; + _recvapp_buf = nullptr; + _recvapp_len = 0; + // This connection is toast + _handshake_done = false; +} + +bool WiFiClientSecure_light::_clientConnected() { + return (_client && _client->state() == ESTABLISHED); +} + +uint8_t WiFiClientSecure_light::connected() { + if (available() || (_clientConnected() && _handshake_done)) { + return true; + } + return false; +} + +size_t WiFiClientSecure_light::_write(const uint8_t *buf, size_t size, bool pmem) { + size_t sent_bytes = 0; + + if (!connected() || !size || !_handshake_done) { + return 0; + } + + do { + // Ensure we yield if we need multiple fragments to avoid WDT + if (sent_bytes) { + optimistic_yield(1000); + } + + // Get BearSSL to a state where we can send + if (_run_until(BR_SSL_SENDAPP) < 0) { + break; + } + + if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) { + size_t sendapp_len; + unsigned char *sendapp_buf = br_ssl_engine_sendapp_buf(_eng, &sendapp_len); + int to_send = size > sendapp_len ? sendapp_len : size; + if (pmem) { + memcpy_P(sendapp_buf, buf, to_send); + } else { + memcpy(sendapp_buf, buf, to_send); + } + br_ssl_engine_sendapp_ack(_eng, to_send); + br_ssl_engine_flush(_eng, 0); + flush(); + buf += to_send; + sent_bytes += to_send; + size -= to_send; + } else { + break; + } + } while (size); + + LOG_HEAP_SIZE("_write"); + return sent_bytes; +} + +size_t WiFiClientSecure_light::write(const uint8_t *buf, size_t size) { + return _write(buf, size, false); +} + +size_t WiFiClientSecure_light::write_P(PGM_P buf, size_t size) { + return _write((const uint8_t *)buf, size, true); +} + +// We have to manually read and send individual chunks. +size_t WiFiClientSecure_light::write(Stream& stream) { + size_t totalSent = 0; + size_t countRead; + size_t countSent; + + if (!connected() || !_handshake_done) { + DEBUG_BSSL("write: Connect/handshake not completed yet\n"); + return 0; + } + + do { + uint8_t temp[256]; // Temporary chunk size same as ClientContext + countSent = 0; + countRead = stream.readBytes(temp, sizeof(temp)); + if (countRead) { + countSent = _write((const uint8_t*)temp, countRead, true); + totalSent += countSent; + } + yield(); // Feed the WDT + } while ((countSent == countRead) && (countSent > 0)); + return totalSent; +} + +int WiFiClientSecure_light::read(uint8_t *buf, size_t size) { + if (!ctx_present() || !_handshake_done) { + return -1; + } + + int avail = available(); + bool conn = connected(); + if (!avail && conn) { + return 0; // We're still connected, but nothing to read + } + if (!avail && !conn) { + DEBUG_BSSL("read: Not connected, none left available\n"); + return -1; + } + + if (avail) { + // Take data from the recvapp buffer + int to_copy = _recvapp_len < size ? _recvapp_len : size; + memcpy(buf, _recvapp_buf, to_copy); + br_ssl_engine_recvapp_ack(_eng, to_copy); + _recvapp_buf = nullptr; + _recvapp_len = 0; + return to_copy; + } + + if (!conn) { + DEBUG_BSSL("read: Not connected\n"); + return -1; + } + return 0; // If we're connected, no error but no read. +} + +int WiFiClientSecure_light::read() { + uint8_t c; + if (1 == read(&c, 1)) { + return c; + } + DEBUG_BSSL("read: failed\n"); + return -1; +} + +int WiFiClientSecure_light::available() { + if (_recvapp_buf) { + return _recvapp_len; // Anything from last call? + } + _recvapp_buf = nullptr; + _recvapp_len = 0; + if (!ctx_present() || _run_until(BR_SSL_RECVAPP, false) < 0) { + return 0; + } + int st = br_ssl_engine_current_state(_eng); + if (st == BR_SSL_CLOSED) { + return 0; // Nothing leftover, SSL is closed + } + if (st & BR_SSL_RECVAPP) { + _recvapp_buf = br_ssl_engine_recvapp_buf(_eng, &_recvapp_len); + return _recvapp_len; + } + + return 0; +} + +int WiFiClientSecure_light::peek() { + if (!ctx_present() || !available()) { + DEBUG_BSSL("peek: Not connected, none left available\n"); + return -1; + } + if (_recvapp_buf && _recvapp_len) { + return _recvapp_buf[0]; + } + DEBUG_BSSL("peek: No data left\n"); + return -1; +} + +size_t WiFiClientSecure_light::peekBytes(uint8_t *buffer, size_t length) { + size_t to_copy = 0; + if (!ctx_present()) { + DEBUG_BSSL("peekBytes: Not connected\n"); + return 0; + } + + _startMillis = millis(); + while ((available() < (int) length) && ((millis() - _startMillis) < 5000)) { + yield(); + } + + to_copy = _recvapp_len < length ? _recvapp_len : length; + memcpy(buffer, _recvapp_buf, to_copy); + return to_copy; +} + +/* --- Copied almost verbatim from BEARSSL SSL_IO.C --- + Run the engine, until the specified target state is achieved, or + an error occurs. The target state is SENDAPP, RECVAPP, or the + combination of both (the combination matches either). When a match is + achieved, this function returns 0. On error, it returns -1. +*/ +int WiFiClientSecure_light::_run_until(unsigned target, bool blocking) { +//LOG_HEAP_SIZE("_run_until 1"); + if (!ctx_present()) { + DEBUG_BSSL("_run_until: Not connected\n"); + return -1; + } + for (int no_work = 0; blocking || no_work < 2;) { + if (blocking) { + // Only for blocking operations can we afford to yield() + optimistic_yield(100); + } + + int state; + state = br_ssl_engine_current_state(_eng); + if (state & BR_SSL_CLOSED) { + return -1; + } + + if (!(_client->state() == ESTABLISHED) && !WiFiClient::available()) { + return (state & target) ? 0 : -1; + } + + /* + If there is some record data to send, do it. This takes + precedence over everything else. + */ + if (state & BR_SSL_SENDREC) { + unsigned char *buf; + size_t len; + int wlen; + + buf = br_ssl_engine_sendrec_buf(_eng, &len); + wlen = WiFiClient::write(buf, len); + if (wlen <= 0) { + /* + 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. + */ + return -1; + } + if (wlen > 0) { + br_ssl_engine_sendrec_ack(_eng, wlen); + } + no_work = 0; + continue; + } + + /* + If we reached our target, then we are finished. + */ + if (state & target) { + return 0; + } + /* + If some application data must be read, and we did not + exit, then this means that we are trying to write data, + and that's not possible until the application data is + read. This may happen if using a shared in/out buffer, + and the underlying protocol is not strictly half-duplex. + This is unrecoverable here, so we report an error. + */ + if (state & BR_SSL_RECVAPP) { + DEBUG_BSSL("_run_until: Fatal protocol state\n"); + return -1; + } + /* + If we reached that point, then either we are trying + to read data and there is some, or the engine is stuck + until a new record is obtained. + */ + if (state & BR_SSL_RECVREC) { + if (WiFiClient::available()) { + unsigned char *buf; + size_t len; + int rlen; + + buf = br_ssl_engine_recvrec_buf(_eng, &len); + rlen = WiFiClient::read(buf, len); + if (rlen < 0) { + return -1; + } + if (rlen > 0) { + br_ssl_engine_recvrec_ack(_eng, rlen); + } + no_work = 0; + continue; + } + } + /* + We can reach that point if the target RECVAPP, and + the state contains SENDAPP only. This may happen with + a shared in/out buffer. In that case, we must flush + the buffered data to "make room" for a new incoming + record. + */ + br_ssl_engine_flush(_eng, 0); + + no_work++; // We didn't actually advance here + } + // We only get here if we ran through the loop without getting anything done + return -1; +} + +bool WiFiClientSecure_light::_wait_for_handshake() { + _handshake_done = false; + while (!_handshake_done && _clientConnected()) { + int ret = _run_until(BR_SSL_SENDAPP); + if (ret < 0) { + DEBUG_BSSL("_wait_for_handshake: failed\n"); + break; + } + if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) { + _handshake_done = true; + } + optimistic_yield(1000); + } + return _handshake_done; +} + +static uint8_t htoi (unsigned char c) +{ + if (c>='0' && c <='9') return c - '0'; + else if (c>='A' && c<='F') return 10 + c - 'A'; + else if (c>='a' && c<='f') return 10 + c - 'a'; + else return 255; +} + +extern "C" { + + // see https://stackoverflow.com/questions/6357031/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-in-c + void tohex(unsigned char * in, size_t insz, char * out, size_t outsz) { + unsigned char * pin = in; + static const char * hex = "0123456789ABCDEF"; + char * pout = out; + for(; pin < in+insz; pout +=3, pin++){ + pout[0] = hex[(*pin>>4) & 0xF]; + pout[1] = hex[ *pin & 0xF]; + pout[2] = ':'; + if (pout + 3 - out > outsz){ + /* Better to truncate output string than overflow buffer */ + /* it would be still better to either return a status */ + /* or ensure the target buffer is large enough and it never happen */ + break; + } + } + pout[-1] = 0; + } + + + // BearSSL doesn't define a true insecure decoder, so we make one ourselves + // from the simple parser. It generates the issuer and subject hashes and + // the SHA1 fingerprint, only one (or none!) of which will be used to + // "verify" the certificate. + + // Private x509 decoder state + struct br_x509_pubkeyfingerprint_context { + const br_x509_class *vtable; + bool done_cert; // did we parse the first cert already? + bool fingerprint_all; + uint8_t *pubkey_recv_fingerprint; + const uint8_t *fingerprint1; + const uint8_t *fingerprint2; + unsigned usages; // pubkey usage + br_x509_decoder_context ctx; // defined in BearSSL + }; + + // Callback on the first byte of any certificate + static void pubkeyfingerprint_start_chain(const br_x509_class **ctx, const char *server_name) { + br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx; + // Don't process anything but the first certificate in the chain + if (!xc->done_cert) { + br_x509_decoder_init(&xc->ctx, nullptr, nullptr, nullptr, nullptr); + } + (void)server_name; // ignore server name + } + + // Callback for each certificate present in the chain (but only operates + // on the first one by design). + static void pubkeyfingerprint_start_cert(const br_x509_class **ctx, uint32_t length) { + (void) ctx; // do nothing + (void) length; + } + + // Callback for each byte stream in the chain. Only process first cert. + static void pubkeyfingerprint_append(const br_x509_class **ctx, const unsigned char *buf, size_t len) { + br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx; + // Don't process anything but the first certificate in the chain + if (!xc->done_cert) { + br_x509_decoder_push(&xc->ctx, (const void*)buf, len); + } + } + + // Callback on individual cert end. + static void pubkeyfingerprint_end_cert(const br_x509_class **ctx) { + br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx; + xc->done_cert = true; // first cert already processed + } + + static void pubkeyfingerprint_pubkey_fingerprint(br_sha1_context *shactx, br_rsa_public_key rsakey) { + br_sha1_init(shactx); + br_sha1_update(shactx, "ssh-rsa", 7); // tag + br_sha1_update(shactx, rsakey.e, rsakey.elen); // exponent + br_sha1_update(shactx, rsakey.n, rsakey.nlen); // modulus + } + + // Callback when complete chain has been parsed. + // Return 0 on validation success, !0 on validation error + static unsigned pubkeyfingerprint_end_chain(const br_x509_class **ctx) { + br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx; + + br_sha1_context sha1_context; + pubkeyfingerprint_pubkey_fingerprint(&sha1_context, xc->ctx.pkey.key.rsa); + br_sha1_out(&sha1_context, xc->pubkey_recv_fingerprint); // copy to fingerprint + + if (!xc->fingerprint_all) { + if (0 == memcmp_P(xc->pubkey_recv_fingerprint, xc->fingerprint1, 20)) { + return 0; + } + if (0 == memcmp_P(xc->pubkey_recv_fingerprint, xc->fingerprint2, 20)) { + return 0; + } + return 1; // no match, error + } else { + // Default (no validation at all) or no errors in prior checks = success. + return 0; + } + } + + // Return the public key from the validator (set by x509_minimal) + static const br_x509_pkey *pubkeyfingerprint_get_pkey(const br_x509_class *const *ctx, unsigned *usages) { + const br_x509_pubkeyfingerprint_context *xc = (const br_x509_pubkeyfingerprint_context *)ctx; + + if (usages != NULL) { + *usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN; // I said we were insecure! + } + return &xc->ctx.pkey; + } + + // Set up the x509 insecure data structures for BearSSL core to use. + void br_x509_pubkeyfingerprint_init(br_x509_pubkeyfingerprint_context *ctx, + const uint8_t *fingerprint1, const uint8_t *fingerprint2, + uint8_t *recv_fingerprint, + bool fingerprint_all) { + static const br_x509_class br_x509_pubkeyfingerprint_vtable PROGMEM = { + sizeof(br_x509_pubkeyfingerprint_context), + pubkeyfingerprint_start_chain, + pubkeyfingerprint_start_cert, + pubkeyfingerprint_append, + pubkeyfingerprint_end_cert, + pubkeyfingerprint_end_chain, + pubkeyfingerprint_get_pkey + }; + + memset(ctx, 0, sizeof * ctx); + ctx->vtable = &br_x509_pubkeyfingerprint_vtable; + ctx->done_cert = false; + ctx->fingerprint1 = fingerprint1; + ctx->fingerprint2 = fingerprint2; + ctx->pubkey_recv_fingerprint = recv_fingerprint; + ctx->fingerprint_all = fingerprint_all; + } + + // We limit to a single cipher to reduce footprint + // we reference it, don't put in PROGMEM + static const uint16_t suites[] = { +#ifdef USE_MQTT_TLS_FORCE_EC_CIPHER + BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 +#else + BR_TLS_RSA_WITH_AES_128_GCM_SHA256 +#endif + }; + + // Default initializion for our SSL clients + static void br_ssl_client_base_init(br_ssl_client_context *cc) { + br_ssl_client_zero(cc); + // forbid SSL renegociation, as we free the Private Key after handshake + br_ssl_engine_add_flags(&cc->eng, BR_OPT_NO_RENEGOTIATION); + + br_ssl_engine_set_versions(&cc->eng, BR_TLS12, BR_TLS12); + br_ssl_engine_set_suites(&cc->eng, suites, (sizeof suites) / (sizeof suites[0])); + br_ssl_client_set_default_rsapub(cc); + br_ssl_engine_set_default_rsavrfy(&cc->eng); + + // install hashes + br_ssl_engine_set_hash(&cc->eng, br_sha256_ID, &br_sha256_vtable); + br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf); + + // AES CTR/GCM small version, not contstant time (we don't really care here as there is no TPM anyways) + br_ssl_engine_set_gcm(&cc->eng, &br_sslrec_in_gcm_vtable, &br_sslrec_out_gcm_vtable); + br_ssl_engine_set_aes_ctr(&cc->eng, &br_aes_small_ctr_vtable); + br_ssl_engine_set_ghash(&cc->eng, &br_ghash_ctmul32); + +#ifdef USE_MQTT_TLS_FORCE_EC_CIPHER + // we support only P256 EC curve for AWS IoT, no EC curve for Letsencrypt unless forced + br_ssl_engine_set_ec(&cc->eng, &br_ec_p256_m15); +#endif + } +} + +// Called by connect() to do the actual SSL setup and handshake. +// Returns if the SSL handshake succeeded. +bool WiFiClientSecure_light::_connectSSL(const char* hostName) { +// #ifdef USE_MQTT_AWS_IOT +// if ((!_chain_P) || (!_sk_ec_P)) { +// setLastError(ERR_MISSING_EC_KEY); +// return false; +// } +// #endif + + // Validation context, either full CA validation or checking only fingerprints +#ifdef USE_MQTT_TLS_CA_CERT + br_x509_minimal_context *x509_minimal; +#else + br_x509_pubkeyfingerprint_context *x509_insecure; +#endif + + LOG_HEAP_SIZE("_connectSSL.start"); + + do { // used to exit on Out of Memory error and keep all cleanup code at the same place + // ============================================================ + // allocate Thunk stack, move to alternate stack and initialize + stack_thunk_light_add_ref(); + LOG_HEAP_SIZE("Thunk allocated"); + DEBUG_BSSL("_connectSSL: start connection\n"); + _freeSSL(); + clearLastError(); + if (!stack_thunk_light_get_stack_bot()) break; + + _ctx_present = true; + _eng = &_sc->eng; // Allocation/deallocation taken care of by the _sc shared_ptr + + br_ssl_client_base_init(_sc.get()); + + // ============================================================ + // Allocatte and initialize Decoder Context + LOG_HEAP_SIZE("_connectSSL before DecoderContext allocation"); + // Only failure possible in the installation is OOM + #ifdef USE_MQTT_TLS_CA_CERT + x509_minimal = (br_x509_minimal_context*) malloc(sizeof(br_x509_minimal_context)); + if (!x509_minimal) break; + br_x509_minimal_init(x509_minimal, &br_sha256_vtable, _ta_P, 1); + br_x509_minimal_set_rsa(x509_minimal, br_ssl_engine_get_rsavrfy(_eng)); + br_x509_minimal_set_hash(x509_minimal, br_sha256_ID, &br_sha256_vtable); + br_ssl_engine_set_x509(_eng, &x509_minimal->vtable); + + #else + x509_insecure = (br_x509_pubkeyfingerprint_context*) malloc(sizeof(br_x509_pubkeyfingerprint_context)); + //x509_insecure = std::unique_ptr(new br_x509_pubkeyfingerprint_context); + if (!x509_insecure) break; + br_x509_pubkeyfingerprint_init(x509_insecure, _fingerprint1, _fingerprint2, _recv_fingerprint, _fingerprint_any); + br_ssl_engine_set_x509(_eng, &x509_insecure->vtable); + #endif + LOG_HEAP_SIZE("_connectSSL after DecoderContext allocation"); + + // ============================================================ + // Set send/receive buffers + br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size); + + // ============================================================ + // allocate Private key if needed, only if USE_MQTT_AWS_IOT + LOG_HEAP_SIZE("_connectSSL before PrivKey allocation"); + #ifdef USE_MQTT_AWS_IOT + // ============================================================ + // Set the EC Private Key, only USE_MQTT_AWS_IOT + // limited to P256 curve + br_ssl_client_set_single_ec(_sc.get(), _chain_P, 1, + _sk_ec_P, _allowed_usages, + _cert_issuer_key_type, &br_ec_p256_m15, br_ecdsa_sign_asn1_get_default()); + #endif // USE_MQTT_AWS_IOT + + // ============================================================ + // Start TLS connection, ALL + if (!br_ssl_client_reset(_sc.get(), hostName, 0)) break; + + auto ret = _wait_for_handshake(); + #ifdef DEBUG_ESP_SSL + if (!ret) { + DEBUG_BSSL("Couldn't connect. Error = %d\n", getLastError()); + } else { + DEBUG_BSSL("Connected! MFLNStatus = %d\n", getMFLNStatus()); + } + #endif + LOG_HEAP_SIZE("_connectSSL.end"); + _max_thunkstack_use = stack_thunk_light_get_max_usage(); + stack_thunk_light_del_ref(); + //stack_thunk_light_repaint(); + LOG_HEAP_SIZE("_connectSSL.end, freeing StackThunk"); + + #ifdef USE_MQTT_TLS_CA_CERT + free(x509_minimal); + #else + free(x509_insecure); + #endif + LOG_HEAP_SIZE("_connectSSL after release of Priv Key"); + return ret; + } while (0); + + // ============================================================ + // if we arrived here, this means we had an OOM error, cleaning up + setLastError(ERR_OOM); + DEBUG_BSSL("_connectSSL: Out of memory\n"); + stack_thunk_light_del_ref(); +#ifdef USE_MQTT_TLS_CA_CERT + free(x509_minimal); +#else + free(x509_insecure); +#endif + LOG_HEAP_SIZE("_connectSSL clean_on_error"); + return false; +} + +}; + +#include "t_bearssl_tasmota_config.h" + +#endif // USE_TLS diff --git a/tasmota/WiFiClientSecureLightBearSSL.h b/tasmota/WiFiClientSecureLightBearSSL.h old mode 100644 new mode 100755 index 5dd0df35e..e5908275e --- a/tasmota/WiFiClientSecureLightBearSSL.h +++ b/tasmota/WiFiClientSecureLightBearSSL.h @@ -1,221 +1,221 @@ -/* - WiFiClientBearSSL- SSL client/server for esp8266 using BearSSL libraries - - Mostly compatible with Arduino WiFi shield library and standard - WiFiClient/ServerSecure (except for certificate handling). - - Copyright (c) 2018 Earle F. Philhower, III - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#include - -#ifndef wificlientlightbearssl_h -#define wificlientlightbearssl_h -#if defined(USE_MQTT_TLS) || defined (USE_SENDMAIL) -#include -#include "WiFiClient.h" -#include - -namespace BearSSL { - -class WiFiClientSecure_light : public WiFiClient { - public: - WiFiClientSecure_light(int recv, int xmit); - ~WiFiClientSecure_light() override; - - void allocateBuffers(void); - - int connect(IPAddress ip, uint16_t port) override; - int connect(const char* name, uint16_t port) override; - - uint8_t connected() override; - size_t write(const uint8_t *buf, size_t size) override; - size_t write_P(PGM_P buf, size_t size) override; - size_t write(const char *buf) { - return write((const uint8_t*)buf, strlen(buf)); - } - size_t write_P(const char *buf) { - return write_P((PGM_P)buf, strlen_P(buf)); - } - size_t write(Stream& stream); // Note this is not virtual - int read(uint8_t *buf, size_t size) override; - int available() override; - int read() override; - int peek() override; - size_t peekBytes(uint8_t *buffer, size_t length) override; - bool flush(unsigned int maxWaitMs); - bool stop(unsigned int maxWaitMs); - void flush() override { (void)flush(0); } - void stop() override { (void)stop(0); } - - // Only check SHA1 fingerprint of public key - void setPubKeyFingerprint(const uint8_t *f1, const uint8_t *f2, - bool f_any = false) { - _fingerprint1 = f1; - _fingerprint2 = f2; - _fingerprint_any = f_any; - } - const uint8_t * getRecvPubKeyFingerprint(void) { - return _recv_fingerprint; - } - - void setClientECCert(const br_x509_certificate *cert, const br_ec_private_key *sk, - unsigned allowed_usages, unsigned cert_issuer_key_type); - - void setTrustAnchor(const br_x509_trust_anchor *ta); - - // Sets the requested buffer size for transmit and receive - void setBufferSizes(int recv, int xmit); - - // Returns whether MFLN negotiation for the above buffer sizes succeeded (after connection) - int getMFLNStatus() { - return connected() && br_ssl_engine_get_mfln_negotiated(_eng); - } - - int32_t getLastError(void) { - if (_last_error) { - return _last_error; - } else { - return br_ssl_engine_last_error(_eng); - } - } - inline void setLastError(int32_t err) { - _last_error = err; - } - inline void clearLastError(void) { - _last_error = 0; - } - inline size_t getMaxThunkStackUse(void) { - return _max_thunkstack_use; - } - - private: - void _clear(); - bool _ctx_present; - std::shared_ptr _sc; - inline bool ctx_present() { - return _ctx_present; - } - br_ssl_engine_context *_eng; // &_sc->eng, to allow for client or server contexts - std::shared_ptr _iobuf_in; - std::shared_ptr _iobuf_out; - time_t _now; - int _iobuf_in_size; - int _iobuf_out_size; - bool _handshake_done; - uint64_t _last_error; - - bool _fingerprint_any; // accept all fingerprints - const uint8_t *_fingerprint1; // fingerprint1 to be checked against - const uint8_t *_fingerprint2; // fingerprint2 to be checked against - uint8_t _recv_fingerprint[20]; // fingerprint received - - unsigned char *_recvapp_buf; - size_t _recvapp_len; - - bool _clientConnected(); // Is the underlying socket alive? - bool _connectSSL(const char *hostName); // Do initial SSL handshake - void _freeSSL(); - int _run_until(unsigned target, bool blocking = true); - size_t _write(const uint8_t *buf, size_t size, bool pmem); - bool _wait_for_handshake(); // Sets and return the _handshake_done after connecting - - // Optional client certificate - const br_x509_certificate *_chain_P; // PROGMEM certificate - const br_ec_private_key *_sk_ec_P; // PROGMEM private key - const br_x509_trust_anchor *_ta_P; // PROGMEM server CA - unsigned _allowed_usages; - unsigned _cert_issuer_key_type; - - // record the maximum use of ThunkStack for monitoring - size_t _max_thunkstack_use; - -}; - -#define ERR_OOM -1000 -#define ERR_CANT_RESOLVE_IP -1001 -#define ERR_TCP_CONNECT -1002 -#define ERR_MISSING_EC_KEY -1003 -#define ERR_MISSING_CA -1004 - -// For reference, BearSSL error codes: -// #define BR_ERR_OK 0 -// #define BR_ERR_BAD_PARAM 1 -// #define BR_ERR_BAD_STATE 2 -// #define BR_ERR_UNSUPPORTED_VERSION 3 -// #define BR_ERR_BAD_VERSION 4 -// #define BR_ERR_BAD_LENGTH 5 -// #define BR_ERR_TOO_LARGE 6 -// #define BR_ERR_BAD_MAC 7 -// #define BR_ERR_NO_RANDOM 8 -// #define BR_ERR_UNKNOWN_TYPE 9 -// #define BR_ERR_UNEXPECTED 10 -// #define BR_ERR_BAD_CCS 12 -// #define BR_ERR_BAD_ALERT 13 -// #define BR_ERR_BAD_HANDSHAKE 14 -// #define BR_ERR_OVERSIZED_ID 15 -// #define BR_ERR_BAD_CIPHER_SUITE 16 -// #define BR_ERR_BAD_COMPRESSION 17 -// #define BR_ERR_BAD_FRAGLEN 18 -// #define BR_ERR_BAD_SECRENEG 19 -// #define BR_ERR_EXTRA_EXTENSION 20 -// #define BR_ERR_BAD_SNI 21 -// #define BR_ERR_BAD_HELLO_DONE 22 -// #define BR_ERR_LIMIT_EXCEEDED 23 -// #define BR_ERR_BAD_FINISHED 24 -// #define BR_ERR_RESUME_MISMATCH 25 -// #define BR_ERR_INVALID_ALGORITHM 26 -// #define BR_ERR_BAD_SIGNATURE 27 -// #define BR_ERR_WRONG_KEY_USAGE 28 -// #define BR_ERR_NO_CLIENT_AUTH 29 -// #define BR_ERR_IO 31 -// #define BR_ERR_RECV_FATAL_ALERT 256 -// #define BR_ERR_SEND_FATAL_ALERT 512 -// #define BR_ERR_X509_OK 32 -// #define BR_ERR_X509_INVALID_VALUE 33 -// #define BR_ERR_X509_TRUNCATED 34 -// #define BR_ERR_X509_EMPTY_CHAIN 35 -// #define BR_ERR_X509_INNER_TRUNC 36 -// #define BR_ERR_X509_BAD_TAG_CLASS 37 -// #define BR_ERR_X509_BAD_TAG_VALUE 38 -// #define BR_ERR_X509_INDEFINITE_LENGTH 39 -// #define BR_ERR_X509_EXTRA_ELEMENT 40 -// #define BR_ERR_X509_UNEXPECTED 41 -// #define BR_ERR_X509_NOT_CONSTRUCTED 42 -// #define BR_ERR_X509_NOT_PRIMITIVE 43 -// #define BR_ERR_X509_PARTIAL_BYTE 44 -// #define BR_ERR_X509_BAD_BOOLEAN 45 -// #define BR_ERR_X509_OVERFLOW 46 -// #define BR_ERR_X509_BAD_DN 47 -// #define BR_ERR_X509_BAD_TIME 48 -// #define BR_ERR_X509_UNSUPPORTED 49 -// #define BR_ERR_X509_LIMIT_EXCEEDED 50 -// #define BR_ERR_X509_WRONG_KEY_TYPE 51 -// #define BR_ERR_X509_BAD_SIGNATURE 52 -// #define BR_ERR_X509_TIME_UNKNOWN 53 -// #define BR_ERR_X509_EXPIRED 54 -// #define BR_ERR_X509_DN_MISMATCH 55 -// #define BR_ERR_X509_BAD_SERVER_NAME 56 -// #define BR_ERR_X509_CRITICAL_EXTENSION 57 -// #define BR_ERR_X509_NOT_CA 58 -// #define BR_ERR_X509_FORBIDDEN_KEY_USAGE 59 -// #define BR_ERR_X509_WEAK_PUBLIC_KEY 60 -// #define BR_ERR_X509_NOT_TRUSTED 62 - -}; - -#endif // USE_MQTT_TLS -#endif // wificlientlightbearssl_h +/* + WiFiClientBearSSL- SSL client/server for esp8266 using BearSSL libraries + - Mostly compatible with Arduino WiFi shield library and standard + WiFiClient/ServerSecure (except for certificate handling). + + Copyright (c) 2018 Earle F. Philhower, III + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#ifndef wificlientlightbearssl_h +#define wificlientlightbearssl_h +#ifdef USE_TLS +#include +#include "WiFiClient.h" +#include + +namespace BearSSL { + +class WiFiClientSecure_light : public WiFiClient { + public: + WiFiClientSecure_light(int recv, int xmit); + ~WiFiClientSecure_light() override; + + void allocateBuffers(void); + + int connect(IPAddress ip, uint16_t port) override; + int connect(const char* name, uint16_t port) override; + + uint8_t connected() override; + size_t write(const uint8_t *buf, size_t size) override; + size_t write_P(PGM_P buf, size_t size) override; + size_t write(const char *buf) { + return write((const uint8_t*)buf, strlen(buf)); + } + size_t write_P(const char *buf) { + return write_P((PGM_P)buf, strlen_P(buf)); + } + size_t write(Stream& stream); // Note this is not virtual + int read(uint8_t *buf, size_t size) override; + int available() override; + int read() override; + int peek() override; + size_t peekBytes(uint8_t *buffer, size_t length) override; + bool flush(unsigned int maxWaitMs); + bool stop(unsigned int maxWaitMs); + void flush() override { (void)flush(0); } + void stop() override { (void)stop(0); } + + // Only check SHA1 fingerprint of public key + void setPubKeyFingerprint(const uint8_t *f1, const uint8_t *f2, + bool f_any = false) { + _fingerprint1 = f1; + _fingerprint2 = f2; + _fingerprint_any = f_any; + } + const uint8_t * getRecvPubKeyFingerprint(void) { + return _recv_fingerprint; + } + + void setClientECCert(const br_x509_certificate *cert, const br_ec_private_key *sk, + unsigned allowed_usages, unsigned cert_issuer_key_type); + + void setTrustAnchor(const br_x509_trust_anchor *ta); + + // Sets the requested buffer size for transmit and receive + void setBufferSizes(int recv, int xmit); + + // Returns whether MFLN negotiation for the above buffer sizes succeeded (after connection) + int getMFLNStatus() { + return connected() && br_ssl_engine_get_mfln_negotiated(_eng); + } + + int32_t getLastError(void) { + if (_last_error) { + return _last_error; + } else { + return br_ssl_engine_last_error(_eng); + } + } + inline void setLastError(int32_t err) { + _last_error = err; + } + inline void clearLastError(void) { + _last_error = 0; + } + inline size_t getMaxThunkStackUse(void) { + return _max_thunkstack_use; + } + + private: + void _clear(); + bool _ctx_present; + std::shared_ptr _sc; + inline bool ctx_present() { + return _ctx_present; + } + br_ssl_engine_context *_eng; // &_sc->eng, to allow for client or server contexts + std::shared_ptr _iobuf_in; + std::shared_ptr _iobuf_out; + time_t _now; + int _iobuf_in_size; + int _iobuf_out_size; + bool _handshake_done; + uint64_t _last_error; + + bool _fingerprint_any; // accept all fingerprints + const uint8_t *_fingerprint1; // fingerprint1 to be checked against + const uint8_t *_fingerprint2; // fingerprint2 to be checked against + uint8_t _recv_fingerprint[20]; // fingerprint received + + unsigned char *_recvapp_buf; + size_t _recvapp_len; + + bool _clientConnected(); // Is the underlying socket alive? + bool _connectSSL(const char *hostName); // Do initial SSL handshake + void _freeSSL(); + int _run_until(unsigned target, bool blocking = true); + size_t _write(const uint8_t *buf, size_t size, bool pmem); + bool _wait_for_handshake(); // Sets and return the _handshake_done after connecting + + // Optional client certificate + const br_x509_certificate *_chain_P; // PROGMEM certificate + const br_ec_private_key *_sk_ec_P; // PROGMEM private key + const br_x509_trust_anchor *_ta_P; // PROGMEM server CA + unsigned _allowed_usages; + unsigned _cert_issuer_key_type; + + // record the maximum use of ThunkStack for monitoring + size_t _max_thunkstack_use; + +}; + +#define ERR_OOM -1000 +#define ERR_CANT_RESOLVE_IP -1001 +#define ERR_TCP_CONNECT -1002 +// #define ERR_MISSING_EC_KEY -1003 // deprecated, AWS IoT is not called if the private key is not present +#define ERR_MISSING_CA -1004 + +// For reference, BearSSL error codes: +// #define BR_ERR_OK 0 +// #define BR_ERR_BAD_PARAM 1 +// #define BR_ERR_BAD_STATE 2 +// #define BR_ERR_UNSUPPORTED_VERSION 3 +// #define BR_ERR_BAD_VERSION 4 +// #define BR_ERR_BAD_LENGTH 5 +// #define BR_ERR_TOO_LARGE 6 +// #define BR_ERR_BAD_MAC 7 +// #define BR_ERR_NO_RANDOM 8 +// #define BR_ERR_UNKNOWN_TYPE 9 +// #define BR_ERR_UNEXPECTED 10 +// #define BR_ERR_BAD_CCS 12 +// #define BR_ERR_BAD_ALERT 13 +// #define BR_ERR_BAD_HANDSHAKE 14 +// #define BR_ERR_OVERSIZED_ID 15 +// #define BR_ERR_BAD_CIPHER_SUITE 16 +// #define BR_ERR_BAD_COMPRESSION 17 +// #define BR_ERR_BAD_FRAGLEN 18 +// #define BR_ERR_BAD_SECRENEG 19 +// #define BR_ERR_EXTRA_EXTENSION 20 +// #define BR_ERR_BAD_SNI 21 +// #define BR_ERR_BAD_HELLO_DONE 22 +// #define BR_ERR_LIMIT_EXCEEDED 23 +// #define BR_ERR_BAD_FINISHED 24 +// #define BR_ERR_RESUME_MISMATCH 25 +// #define BR_ERR_INVALID_ALGORITHM 26 +// #define BR_ERR_BAD_SIGNATURE 27 +// #define BR_ERR_WRONG_KEY_USAGE 28 +// #define BR_ERR_NO_CLIENT_AUTH 29 +// #define BR_ERR_IO 31 +// #define BR_ERR_RECV_FATAL_ALERT 256 +// #define BR_ERR_SEND_FATAL_ALERT 512 +// #define BR_ERR_X509_OK 32 +// #define BR_ERR_X509_INVALID_VALUE 33 +// #define BR_ERR_X509_TRUNCATED 34 +// #define BR_ERR_X509_EMPTY_CHAIN 35 +// #define BR_ERR_X509_INNER_TRUNC 36 +// #define BR_ERR_X509_BAD_TAG_CLASS 37 +// #define BR_ERR_X509_BAD_TAG_VALUE 38 +// #define BR_ERR_X509_INDEFINITE_LENGTH 39 +// #define BR_ERR_X509_EXTRA_ELEMENT 40 +// #define BR_ERR_X509_UNEXPECTED 41 +// #define BR_ERR_X509_NOT_CONSTRUCTED 42 +// #define BR_ERR_X509_NOT_PRIMITIVE 43 +// #define BR_ERR_X509_PARTIAL_BYTE 44 +// #define BR_ERR_X509_BAD_BOOLEAN 45 +// #define BR_ERR_X509_OVERFLOW 46 +// #define BR_ERR_X509_BAD_DN 47 +// #define BR_ERR_X509_BAD_TIME 48 +// #define BR_ERR_X509_UNSUPPORTED 49 +// #define BR_ERR_X509_LIMIT_EXCEEDED 50 +// #define BR_ERR_X509_WRONG_KEY_TYPE 51 +// #define BR_ERR_X509_BAD_SIGNATURE 52 +// #define BR_ERR_X509_TIME_UNKNOWN 53 +// #define BR_ERR_X509_EXPIRED 54 +// #define BR_ERR_X509_DN_MISMATCH 55 +// #define BR_ERR_X509_BAD_SERVER_NAME 56 +// #define BR_ERR_X509_CRITICAL_EXTENSION 57 +// #define BR_ERR_X509_NOT_CA 58 +// #define BR_ERR_X509_FORBIDDEN_KEY_USAGE 59 +// #define BR_ERR_X509_WEAK_PUBLIC_KEY 60 +// #define BR_ERR_X509_NOT_TRUSTED 62 + +}; + +#endif // USE_TLS +#endif // wificlientlightbearssl_h diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 6e20a598e..4a75b4b98 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -753,4 +753,16 @@ #error "Select either USE_RULES or USE_SCRIPT. They can't both be used at the same time" #endif +/*********************************************************************************************\ + * Post-process compile options for TLS +\*********************************************************************************************/ + +#if defined(USE_MQTT_TLS) || defined(USE_SENDMAIL) || defined(USE_TELEGRAM) + #define USE_TLS // flag indicates we need to include TLS code + + #if defined(USE_MQTT_AWS_IOT) || defined(USE_TELEGRAM) + #define USE_MQTT_TLS_FORCE_EC_CIPHER // AWS IoT and TELEGRAM require EC Cipher + #endif +#endif + #endif // _MY_USER_CONFIG_H_ diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index ecf1127ec..78c50edc3 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -36,9 +36,9 @@ #include "tasmota_version.h" // Tasmota version information #include "tasmota.h" // Enumeration used in my_user_config.h #include "my_user_config.h" // Fixed user configurable options -#ifdef USE_MQTT_TLS +#ifdef USE_TLS #include // We need to include before "tasmota_globals.h" to take precedence over the BearSSL version in Arduino -#endif // USE_MQTT_TLS +#endif // USE_TLS #include "tasmota_globals.h" // Function prototypes and global configuration #include "i18n.h" // Language support configured by my_user_config.h #include "tasmota_template.h" // Hardware configuration diff --git a/tasmota/tasmota_ca.ino b/tasmota/tasmota_ca.ino index 8e75f891e..1db0a8c63 100644 --- a/tasmota/tasmota_ca.ino +++ b/tasmota/tasmota_ca.ino @@ -21,9 +21,8 @@ // Please use fingerprint validation instead // However, the CA are available below for future use if it appears to be useful -#ifdef USE_MQTT_TLS_CA_CERT +#if defined(USE_TLS) && defined(USE_MQTT_TLS_CA_CERT) -#ifndef USE_MQTT_AWS_IOT /*********************************************************************************************\ * LetsEncrypt IdenTrust DST Root CA X3 certificate, RSA 2048 bits SHA 256, valid until 20210417 * @@ -35,7 +34,7 @@ * remove "static" and add "PROGMEM" \*********************************************************************************************/ -static const unsigned char PROGMEM TA0_DN[] = { +static const unsigned char PROGMEM LetsEncrypt_DN[] = { 0x30, 0x4A, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x0D, 0x4C, 0x65, 0x74, 0x27, 0x73, 0x20, 0x45, 0x6E, 0x63, 0x72, @@ -45,7 +44,7 @@ static const unsigned char PROGMEM TA0_DN[] = { 0x79, 0x20, 0x58, 0x33 }; -static const unsigned char PROGMEM TA0_RSA_N[] = { +static const unsigned char PROGMEM LetsEncrypt_RSA_N[] = { 0x9C, 0xD3, 0x0C, 0xF0, 0x5A, 0xE5, 0x2E, 0x47, 0xB7, 0x72, 0x5D, 0x37, 0x83, 0xB3, 0x68, 0x63, 0x30, 0xEA, 0xD7, 0x35, 0x26, 0x19, 0x25, 0xE1, 0xBD, 0xBE, 0x35, 0xF1, 0x70, 0x92, 0x2F, 0xB7, 0xB8, 0x4B, 0x41, 0x05, @@ -70,27 +69,22 @@ static const unsigned char PROGMEM TA0_RSA_N[] = { 0xD8, 0x7D, 0xC3, 0x93 }; -static const unsigned char TA0_RSA_E[] = { +static const unsigned char LetsEncrypt_RSA_E[] = { 0x01, 0x00, 0x01 }; static const br_x509_trust_anchor PROGMEM LetsEncryptX3CrossSigned_TA = { - { (unsigned char *)TA0_DN, sizeof TA0_DN }, + { (unsigned char *)LetsEncrypt_DN, sizeof LetsEncrypt_DN }, BR_X509_TA_CA, { BR_KEYTYPE_RSA, { .rsa = { - (unsigned char *)TA0_RSA_N, sizeof TA0_RSA_N, - (unsigned char *)TA0_RSA_E, sizeof TA0_RSA_E, + (unsigned char *)LetsEncrypt_RSA_N, sizeof LetsEncrypt_RSA_N, + (unsigned char *)LetsEncrypt_RSA_E, sizeof LetsEncrypt_RSA_E, } } } }; -#define TAs_NUM 1 - -#endif // not USE_MQTT_AWS_IOT - -#ifdef USE_MQTT_AWS_IOT /*********************************************************************************************\ * Amazon Root CA, RSA 2048 bits SHA 256, valid until 20380117 * @@ -103,7 +97,7 @@ static const br_x509_trust_anchor PROGMEM LetsEncryptX3CrossSigned_TA = { \*********************************************************************************************/ -const unsigned char PROGMEM TA0_DN[] = { +const unsigned char PROGMEM AmazonRootCA1_DN[] = { 0x30, 0x39, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x0F, 0x30, 0x0D, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x06, 0x41, 0x6D, 0x61, 0x7A, 0x6F, 0x6E, 0x31, 0x19, 0x30, 0x17, @@ -111,7 +105,7 @@ const unsigned char PROGMEM TA0_DN[] = { 0x6E, 0x20, 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x43, 0x41, 0x20, 0x31 }; -const unsigned char PROGMEM TA0_RSA_N[] = { +const unsigned char PROGMEM AmazonRootCA1_RSA_N[] = { 0xB2, 0x78, 0x80, 0x71, 0xCA, 0x78, 0xD5, 0xE3, 0x71, 0xAF, 0x47, 0x80, 0x50, 0x74, 0x7D, 0x6E, 0xD8, 0xD7, 0x88, 0x76, 0xF4, 0x99, 0x68, 0xF7, 0x58, 0x21, 0x60, 0xF9, 0x74, 0x84, 0x01, 0x2F, 0xAC, 0x02, 0x2D, 0x86, @@ -136,24 +130,79 @@ const unsigned char PROGMEM TA0_RSA_N[] = { 0x9A, 0xC8, 0xAA, 0x0D }; -static const unsigned char PROGMEM TA0_RSA_E[] = { +static const unsigned char PROGMEM AmazonRootCA1_RSA_E[] = { 0x01, 0x00, 0x01 }; const br_x509_trust_anchor PROGMEM AmazonRootCA1_TA = { - { (unsigned char *)TA0_DN, sizeof TA0_DN }, + { (unsigned char *)AmazonRootCA1_DN, sizeof AmazonRootCA1_DN }, BR_X509_TA_CA, { BR_KEYTYPE_RSA, { .rsa = { - (unsigned char *)TA0_RSA_N, sizeof TA0_RSA_N, - (unsigned char *)TA0_RSA_E, sizeof TA0_RSA_E, + (unsigned char *)AmazonRootCA1_RSA_N, sizeof AmazonRootCA1_RSA_N, + (unsigned char *)AmazonRootCA1_RSA_E, sizeof AmazonRootCA1_RSA_E, } } } }; -#define TAs_NUM 1 +// we add a separate CA for telegram +/*********************************************************************************************\ + * GoDaddy Daddy Secure Certificate Authority - G2, RSA 2048 bits SHA 256, valid until 20220523 + * + * to convert do: "brssl ta GoDaddyCA.pem" + * then copy and paste below, chain the generic names to the same as below + * remove "static" and add "PROGMEM" +\*********************************************************************************************/ -#endif // USE_MQTT_AWS_IOT +const unsigned char GoDaddyCAG2_DN[] PROGMEM = { + 0x30, 0x3E, 0x31, 0x21, 0x30, 0x1F, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, + 0x18, 0x44, 0x6F, 0x6D, 0x61, 0x69, 0x6E, 0x20, 0x43, 0x6F, 0x6E, 0x74, + 0x72, 0x6F, 0x6C, 0x20, 0x56, 0x61, 0x6C, 0x69, 0x64, 0x61, 0x74, 0x65, + 0x64, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10, + 0x61, 0x70, 0x69, 0x2E, 0x74, 0x65, 0x6C, 0x65, 0x67, 0x72, 0x61, 0x6D, + 0x2E, 0x6F, 0x72, 0x67 +}; -#endif // USE_MQTT_TLS_CA_CERT +const unsigned char GoDaddyCAG2_RSA_N[] PROGMEM = { + 0xB4, 0xA3, 0x16, 0x9E, 0x5C, 0x57, 0xC9, 0x89, 0x65, 0xED, 0xEA, 0x78, + 0x0B, 0xAE, 0x8A, 0x58, 0x2F, 0xAE, 0x5A, 0xC8, 0x6E, 0x49, 0x8D, 0xFC, + 0x57, 0xA5, 0x98, 0x88, 0x78, 0x2E, 0x0B, 0x3C, 0x40, 0x3C, 0x21, 0x2E, + 0x9A, 0x94, 0x98, 0x33, 0xA7, 0xE3, 0x42, 0xA7, 0x85, 0xFA, 0xD0, 0x73, + 0x84, 0x01, 0x1C, 0x72, 0x39, 0x37, 0x23, 0xB5, 0x56, 0x1D, 0x43, 0xA5, + 0x71, 0x14, 0x08, 0x24, 0xA5, 0x39, 0xCC, 0xDE, 0x58, 0x53, 0x94, 0x8E, + 0x2A, 0x42, 0xA7, 0x4E, 0x2D, 0x07, 0x32, 0x9E, 0xBA, 0x8B, 0xD3, 0x2A, + 0xA9, 0x9E, 0xC0, 0xE3, 0xCE, 0x9A, 0x10, 0x96, 0x45, 0x58, 0x7A, 0xC7, + 0x1E, 0x45, 0x14, 0x23, 0x92, 0xBB, 0x54, 0x82, 0x88, 0x94, 0x49, 0xB6, + 0xBE, 0x81, 0x21, 0x00, 0x29, 0x6D, 0xC9, 0xCE, 0x8B, 0x39, 0x3A, 0xDC, + 0x35, 0x15, 0xD9, 0xEB, 0x47, 0x9C, 0xEF, 0xBA, 0x09, 0x0E, 0x16, 0xE4, + 0xD9, 0xEB, 0x72, 0x30, 0xFA, 0x49, 0xAB, 0x98, 0x31, 0x7C, 0xB3, 0xAC, + 0x2B, 0x29, 0x91, 0x87, 0x08, 0x41, 0x72, 0x5E, 0x35, 0xC7, 0x87, 0x04, + 0x22, 0xF5, 0x48, 0x76, 0x30, 0x6D, 0x88, 0xDF, 0xF2, 0xA5, 0x29, 0x13, + 0x70, 0xB3, 0x87, 0x02, 0xD5, 0x6B, 0x58, 0xB1, 0xE8, 0x73, 0xC7, 0xE4, + 0xEF, 0x79, 0x86, 0xA4, 0x07, 0x5F, 0x67, 0xB4, 0x79, 0x8D, 0xA4, 0x25, + 0x01, 0x82, 0x8C, 0xE0, 0x30, 0x17, 0xCB, 0x4B, 0x5C, 0xFB, 0xEB, 0x4C, + 0x12, 0x51, 0xB9, 0xC9, 0x04, 0x1F, 0x7E, 0xD2, 0xF8, 0xBA, 0xF5, 0x35, + 0x8D, 0x8A, 0x1C, 0x37, 0x82, 0xF0, 0x15, 0x73, 0x00, 0x6E, 0x3D, 0x1C, + 0x76, 0x8B, 0x01, 0x74, 0x81, 0x3D, 0xE4, 0x2C, 0xA7, 0xCC, 0x2F, 0x66, + 0xDC, 0x44, 0xA8, 0x27, 0x3F, 0xEA, 0xD0, 0xA7, 0xA8, 0xF1, 0xCB, 0xEA, + 0xDA, 0x07, 0x38, 0xBD +}; + +const unsigned char GoDaddyCAG2_RSA_E[] PROGMEM = { + 0x01, 0x00, 0x01 +}; + +const br_x509_trust_anchor GoDaddyCAG2_TA PROGMEM = { + { (unsigned char *)GoDaddyCAG2_DN, sizeof GoDaddyCAG2_DN }, + 0, + { + BR_KEYTYPE_RSA, + { .rsa = { + (unsigned char *)GoDaddyCAG2_RSA_N, sizeof GoDaddyCAG2_RSA_N, + (unsigned char *)GoDaddyCAG2_RSA_E, sizeof GoDaddyCAG2_RSA_E, + } } + } +}; + +#endif // defined(USE_TLS) && defined(USE_MQTT_TLS_CA_CERT) diff --git a/tasmota/tasmota_globals.h b/tasmota/tasmota_globals.h index 44826fa64..f33024c45 100644 --- a/tasmota/tasmota_globals.h +++ b/tasmota/tasmota_globals.h @@ -88,7 +88,7 @@ extern "C" void resetPins(); const uint16_t WEB_LOG_SIZE = 4000; // Max number of characters in weblog #endif -#if defined(USE_MQTT_TLS) && defined(ARDUINO_ESP8266_RELEASE_2_3_0) +#if defined(USE_TLS) && defined(ARDUINO_ESP8266_RELEASE_2_3_0) #error "TLS is no more supported on Core 2.3.0, use 2.4.2 or higher." #endif From dc8d354f161c74fefbcbf4a22b78364d4b25a720 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Wed, 10 Jun 2020 21:14:18 +0200 Subject: [PATCH 2/2] Fix asm for gcc17 --- tasmota/StackThunk_light.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/StackThunk_light.h b/tasmota/StackThunk_light.h index 13a0cb7d3..164417000 100644 --- a/tasmota/StackThunk_light.h +++ b/tasmota/StackThunk_light.h @@ -52,7 +52,7 @@ extern uint32_t stack_thunk_light_refcnt; // Thunking macro #define make_stack_thunk_light(fcnToThunk) \ -__asm("\n\ +__asm__("\n\ .text\n\ .literal_position\n\ .literal .LC_STACK_VALUE"#fcnToThunk", 0xdeadbeef\n\