[MQTT-TLS] Switch to Tasmota's Light-Weight BearSSL lib

This commit is contained in:
TD-er
2024-09-29 21:03:15 +02:00
parent 2db4231e29
commit c83e415a0f
16 changed files with 320 additions and 1266 deletions
@@ -25,9 +25,8 @@
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
*/
#include "tasmota_options.h"
#if defined(ESP8266) && defined(USE_TLS)
#if defined(ESP8266) && FEATURE_TLS
#include <stdint.h>
#include <stdlib.h>
#include "StackThunk_light.h"
@@ -20,8 +20,8 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "tasmota_options.h"
#ifdef USE_TLS
#if FEATURE_TLS
// #define DEBUG_TLS
// #define DEBUG_ESP_SSL
@@ -33,8 +33,15 @@
#include <algorithm>
#include "WiFiClientSecureLightBearSSL.h" // needs to be before "ESP8266WiFi.h" to avoid conflict with Arduino headers
#include "ESP8266WiFi.h"
#include "WiFiHelper.h"
#ifdef ESP32
#include <WiFi.h>
#endif
#ifdef ESP8266
#include <ESP8266WiFi.h>
#endif
#include "WiFiClient.h"
#include "StackThunk_light.h"
#include "lwip/opt.h"
@@ -68,9 +75,6 @@ void _Log_heap_size(const char *msg) {
#define LOG_HEAP_SIZE(a)
#endif
// get UTC time from Tasmota
extern uint32_t UtcTime(void);
extern uint32_t CfgTime(void);
#ifdef ESP8266 // Stack thunk is not needed with ESP32
// Stack thunked versions of calls
@@ -319,7 +323,7 @@ int WiFiClientSecure_light::connect(const char* name, uint16_t port, int32_t tim
DEBUG_BSSL("connect(%s,%d)\n", name, port);
IPAddress remote_addr;
clearLastError();
if (!WiFiHelper::hostByName(name, remote_addr)) {
if (WiFi.hostByName(name, remote_addr) != 1) {
DEBUG_BSSL("connect: Name loopup failure\n");
setLastError(ERR_CANT_RESOLVE_IP);
return 0;
@@ -338,7 +342,7 @@ int WiFiClientSecure_light::connect(const char* name, uint16_t port) {
DEBUG_BSSL("connect(%s,%d)\n", name, port);
IPAddress remote_addr;
clearLastError();
if (!WiFiHelper::hostByName(name, remote_addr)) {
if (WiFi.hostByName(name, remote_addr, 1000) != 1) {
DEBUG_BSSL("connect: Name loopup failure\n");
setLastError(ERR_CANT_RESOLVE_IP);
return 0;
@@ -1010,8 +1014,26 @@ bool WiFiClientSecure_light::_connectSSL(const char* hostName) {
return false;
}
uint32_t WiFiClientSecure_light::UtcTime(void) const
{
if (_UtcTime == nullptr) {
return 0u;
}
return _UtcTime();
}
uint32_t WiFiClientSecure_light::CfgTime(void) const
{
if (_CfgTime == nullptr) {
return 0u;
}
return _CfgTime();
}
};
#include "t_bearssl_tasmota_config.h"
#endif // USE_TLS
#endif // FEATURE_MQTT
@@ -24,7 +24,7 @@
#ifndef wificlientlightbearssl_h
#define wificlientlightbearssl_h
#ifdef USE_TLS
#if FEATURE_TLS
#include <vector>
#include "WiFiClient.h"
#include <t_bearssl.h>
@@ -33,9 +33,15 @@ namespace BearSSL {
class WiFiClientSecure_light : public WiFiClient {
public:
typedef std::function<uint32_t()> UtcTime_fcn;
typedef std::function<uint32_t()> CfgTime_fcn;
WiFiClientSecure_light(int recv, int xmit);
~WiFiClientSecure_light() override;
void setUtcTime_fcn(UtcTime_fcn fcn) { _UtcTime = fcn; }
void setCfgTime_fcn(CfgTime_fcn fcn) { _CfgTime = fcn; }
void allocateBuffers(void);
#ifdef ESP32 // the method to override in ESP32 has timeout argument default #define WIFI_CLIENT_DEF_CONN_TIMEOUT_MS (3000)
@@ -184,6 +190,13 @@ class WiFiClientSecure_light : public WiFiClient {
const char ** _alpn_names;
size_t _alpn_num;
uint32_t UtcTime(void) const;
uint32_t CfgTime(void) const;
UtcTime_fcn _UtcTime{nullptr};
CfgTime_fcn _CfgTime{nullptr};
};
#define ERR_OOM -1000
@@ -296,5 +309,5 @@ class WiFiClientSecure_light : public WiFiClient {
};
#endif // USE_TLS
#endif // FEATURE_MQTT
#endif // wificlientlightbearssl_h
+4
View File
@@ -28,6 +28,7 @@ extra_configs =
platformio_esp32c2_envs.ini
platformio_esp32c6_envs.ini
;default_envs = normal_ESP32_4M
default_envs = max_ESP32_16M8M_LittleFS_ETH
;default_envs = normal_ESP32c6_4M316k_LittleFS_CDC
@@ -107,3 +108,6 @@ src_filter = +<*> -<.git/> -<.svn/> -<example/> -<examples/> -<te
[env]
extends = common
lib_extra_dirs =
lib/lib_ssl
+202
View File
@@ -0,0 +1,202 @@
#ifndef CUSTOMBUILD_CERTIFICATE_CA_H
#define CUSTOMBUILD_CERTIFICATE_CA_H
#include "../../ESPEasy_common.h"
#if FEATURE_TLS
#include <t_bearssl_x509.h>
#if __has_include("../../Custom_CERTS.h")
#include "../../Custom_CERTS.h"
#else
// Root Certificates taken from:
// https://github.com/arendst/Tasmota/blob/b2b839fa83b622c3d28324ac4d6db7ab133741c6/tasmota/tasmota_support/tasmota_ca.ino#L177
#if ! defined(OMIT_LETS_ENCRYPT_CERT)
/*********************************************************************************************\
* LetsEncrypt ISRG Root X1 certificate, RSA 4096 bits SHA 256, valid until 20350604
*
* https://letsencrypt.org/certificates/
* Downloaded from https://letsencrypt.org/certs/isrgrootx1.pem
*
* to convert do: `bearssl ta isrgrootx1.pem`
* then copy and paste below, chain the generic names to the same as below
* remove "static" and add "PROGMEM"
\*********************************************************************************************/
static const unsigned char PROGMEM LetsEncrypt_ISRG_Root_X1_DN[] = {
0x30, 0x4F, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
0x02, 0x55, 0x53, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x0A,
0x13, 0x20, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x65, 0x74, 0x20, 0x53,
0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x20, 0x52, 0x65, 0x73, 0x65,
0x61, 0x72, 0x63, 0x68, 0x20, 0x47, 0x72, 0x6F, 0x75, 0x70, 0x31, 0x15,
0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0C, 0x49, 0x53, 0x52,
0x47, 0x20, 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x58, 0x31
};
static const unsigned char PROGMEM LetsEncrypt_ISRG_Root_X1_RSA_N[] = {
0xAD, 0xE8, 0x24, 0x73, 0xF4, 0x14, 0x37, 0xF3, 0x9B, 0x9E, 0x2B, 0x57,
0x28, 0x1C, 0x87, 0xBE, 0xDC, 0xB7, 0xDF, 0x38, 0x90, 0x8C, 0x6E, 0x3C,
0xE6, 0x57, 0xA0, 0x78, 0xF7, 0x75, 0xC2, 0xA2, 0xFE, 0xF5, 0x6A, 0x6E,
0xF6, 0x00, 0x4F, 0x28, 0xDB, 0xDE, 0x68, 0x86, 0x6C, 0x44, 0x93, 0xB6,
0xB1, 0x63, 0xFD, 0x14, 0x12, 0x6B, 0xBF, 0x1F, 0xD2, 0xEA, 0x31, 0x9B,
0x21, 0x7E, 0xD1, 0x33, 0x3C, 0xBA, 0x48, 0xF5, 0xDD, 0x79, 0xDF, 0xB3,
0xB8, 0xFF, 0x12, 0xF1, 0x21, 0x9A, 0x4B, 0xC1, 0x8A, 0x86, 0x71, 0x69,
0x4A, 0x66, 0x66, 0x6C, 0x8F, 0x7E, 0x3C, 0x70, 0xBF, 0xAD, 0x29, 0x22,
0x06, 0xF3, 0xE4, 0xC0, 0xE6, 0x80, 0xAE, 0xE2, 0x4B, 0x8F, 0xB7, 0x99,
0x7E, 0x94, 0x03, 0x9F, 0xD3, 0x47, 0x97, 0x7C, 0x99, 0x48, 0x23, 0x53,
0xE8, 0x38, 0xAE, 0x4F, 0x0A, 0x6F, 0x83, 0x2E, 0xD1, 0x49, 0x57, 0x8C,
0x80, 0x74, 0xB6, 0xDA, 0x2F, 0xD0, 0x38, 0x8D, 0x7B, 0x03, 0x70, 0x21,
0x1B, 0x75, 0xF2, 0x30, 0x3C, 0xFA, 0x8F, 0xAE, 0xDD, 0xDA, 0x63, 0xAB,
0xEB, 0x16, 0x4F, 0xC2, 0x8E, 0x11, 0x4B, 0x7E, 0xCF, 0x0B, 0xE8, 0xFF,
0xB5, 0x77, 0x2E, 0xF4, 0xB2, 0x7B, 0x4A, 0xE0, 0x4C, 0x12, 0x25, 0x0C,
0x70, 0x8D, 0x03, 0x29, 0xA0, 0xE1, 0x53, 0x24, 0xEC, 0x13, 0xD9, 0xEE,
0x19, 0xBF, 0x10, 0xB3, 0x4A, 0x8C, 0x3F, 0x89, 0xA3, 0x61, 0x51, 0xDE,
0xAC, 0x87, 0x07, 0x94, 0xF4, 0x63, 0x71, 0xEC, 0x2E, 0xE2, 0x6F, 0x5B,
0x98, 0x81, 0xE1, 0x89, 0x5C, 0x34, 0x79, 0x6C, 0x76, 0xEF, 0x3B, 0x90,
0x62, 0x79, 0xE6, 0xDB, 0xA4, 0x9A, 0x2F, 0x26, 0xC5, 0xD0, 0x10, 0xE1,
0x0E, 0xDE, 0xD9, 0x10, 0x8E, 0x16, 0xFB, 0xB7, 0xF7, 0xA8, 0xF7, 0xC7,
0xE5, 0x02, 0x07, 0x98, 0x8F, 0x36, 0x08, 0x95, 0xE7, 0xE2, 0x37, 0x96,
0x0D, 0x36, 0x75, 0x9E, 0xFB, 0x0E, 0x72, 0xB1, 0x1D, 0x9B, 0xBC, 0x03,
0xF9, 0x49, 0x05, 0xD8, 0x81, 0xDD, 0x05, 0xB4, 0x2A, 0xD6, 0x41, 0xE9,
0xAC, 0x01, 0x76, 0x95, 0x0A, 0x0F, 0xD8, 0xDF, 0xD5, 0xBD, 0x12, 0x1F,
0x35, 0x2F, 0x28, 0x17, 0x6C, 0xD2, 0x98, 0xC1, 0xA8, 0x09, 0x64, 0x77,
0x6E, 0x47, 0x37, 0xBA, 0xCE, 0xAC, 0x59, 0x5E, 0x68, 0x9D, 0x7F, 0x72,
0xD6, 0x89, 0xC5, 0x06, 0x41, 0x29, 0x3E, 0x59, 0x3E, 0xDD, 0x26, 0xF5,
0x24, 0xC9, 0x11, 0xA7, 0x5A, 0xA3, 0x4C, 0x40, 0x1F, 0x46, 0xA1, 0x99,
0xB5, 0xA7, 0x3A, 0x51, 0x6E, 0x86, 0x3B, 0x9E, 0x7D, 0x72, 0xA7, 0x12,
0x05, 0x78, 0x59, 0xED, 0x3E, 0x51, 0x78, 0x15, 0x0B, 0x03, 0x8F, 0x8D,
0xD0, 0x2F, 0x05, 0xB2, 0x3E, 0x7B, 0x4A, 0x1C, 0x4B, 0x73, 0x05, 0x12,
0xFC, 0xC6, 0xEA, 0xE0, 0x50, 0x13, 0x7C, 0x43, 0x93, 0x74, 0xB3, 0xCA,
0x74, 0xE7, 0x8E, 0x1F, 0x01, 0x08, 0xD0, 0x30, 0xD4, 0x5B, 0x71, 0x36,
0xB4, 0x07, 0xBA, 0xC1, 0x30, 0x30, 0x5C, 0x48, 0xB7, 0x82, 0x3B, 0x98,
0xA6, 0x7D, 0x60, 0x8A, 0xA2, 0xA3, 0x29, 0x82, 0xCC, 0xBA, 0xBD, 0x83,
0x04, 0x1B, 0xA2, 0x83, 0x03, 0x41, 0xA1, 0xD6, 0x05, 0xF1, 0x1B, 0xC2,
0xB6, 0xF0, 0xA8, 0x7C, 0x86, 0x3B, 0x46, 0xA8, 0x48, 0x2A, 0x88, 0xDC,
0x76, 0x9A, 0x76, 0xBF, 0x1F, 0x6A, 0xA5, 0x3D, 0x19, 0x8F, 0xEB, 0x38,
0xF3, 0x64, 0xDE, 0xC8, 0x2B, 0x0D, 0x0A, 0x28, 0xFF, 0xF7, 0xDB, 0xE2,
0x15, 0x42, 0xD4, 0x22, 0xD0, 0x27, 0x5D, 0xE1, 0x79, 0xFE, 0x18, 0xE7,
0x70, 0x88, 0xAD, 0x4E, 0xE6, 0xD9, 0x8B, 0x3A, 0xC6, 0xDD, 0x27, 0x51,
0x6E, 0xFF, 0xBC, 0x64, 0xF5, 0x33, 0x43, 0x4F
};
static const unsigned char LetsEncrypt_ISRG_Root_X1_RSA_E[] = {
0x01, 0x00, 0x01
};
#endif
#if ! defined(OMIT_AWS_CERT)
/*********************************************************************************************\
* Amazon Root CA, RSA 2048 bits SHA 256, valid until 20380117
*
* https://www.amazontrust.com/repository/
* Downloaded from https://www.amazontrust.com/repository/AmazonRootCA1.pem
*
* to convert do: "bearssl ta AmazonRootCA1.pem"
* then copy and paste below, chain the generic names to the same as below
* remove "static" and add "PROGMEM"
\*********************************************************************************************/
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,
0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10, 0x41, 0x6D, 0x61, 0x7A, 0x6F,
0x6E, 0x20, 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x43, 0x41, 0x20, 0x31
};
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,
0xD3, 0xA0, 0x43, 0x7A, 0x4E, 0xB2, 0xA4, 0xD0, 0x36, 0xBA, 0x01, 0xBE,
0x8D, 0xDB, 0x48, 0xC8, 0x07, 0x17, 0x36, 0x4C, 0xF4, 0xEE, 0x88, 0x23,
0xC7, 0x3E, 0xEB, 0x37, 0xF5, 0xB5, 0x19, 0xF8, 0x49, 0x68, 0xB0, 0xDE,
0xD7, 0xB9, 0x76, 0x38, 0x1D, 0x61, 0x9E, 0xA4, 0xFE, 0x82, 0x36, 0xA5,
0xE5, 0x4A, 0x56, 0xE4, 0x45, 0xE1, 0xF9, 0xFD, 0xB4, 0x16, 0xFA, 0x74,
0xDA, 0x9C, 0x9B, 0x35, 0x39, 0x2F, 0xFA, 0xB0, 0x20, 0x50, 0x06, 0x6C,
0x7A, 0xD0, 0x80, 0xB2, 0xA6, 0xF9, 0xAF, 0xEC, 0x47, 0x19, 0x8F, 0x50,
0x38, 0x07, 0xDC, 0xA2, 0x87, 0x39, 0x58, 0xF8, 0xBA, 0xD5, 0xA9, 0xF9,
0x48, 0x67, 0x30, 0x96, 0xEE, 0x94, 0x78, 0x5E, 0x6F, 0x89, 0xA3, 0x51,
0xC0, 0x30, 0x86, 0x66, 0xA1, 0x45, 0x66, 0xBA, 0x54, 0xEB, 0xA3, 0xC3,
0x91, 0xF9, 0x48, 0xDC, 0xFF, 0xD1, 0xE8, 0x30, 0x2D, 0x7D, 0x2D, 0x74,
0x70, 0x35, 0xD7, 0x88, 0x24, 0xF7, 0x9E, 0xC4, 0x59, 0x6E, 0xBB, 0x73,
0x87, 0x17, 0xF2, 0x32, 0x46, 0x28, 0xB8, 0x43, 0xFA, 0xB7, 0x1D, 0xAA,
0xCA, 0xB4, 0xF2, 0x9F, 0x24, 0x0E, 0x2D, 0x4B, 0xF7, 0x71, 0x5C, 0x5E,
0x69, 0xFF, 0xEA, 0x95, 0x02, 0xCB, 0x38, 0x8A, 0xAE, 0x50, 0x38, 0x6F,
0xDB, 0xFB, 0x2D, 0x62, 0x1B, 0xC5, 0xC7, 0x1E, 0x54, 0xE1, 0x77, 0xE0,
0x67, 0xC8, 0x0F, 0x9C, 0x87, 0x23, 0xD6, 0x3F, 0x40, 0x20, 0x7F, 0x20,
0x80, 0xC4, 0x80, 0x4C, 0x3E, 0x3B, 0x24, 0x26, 0x8E, 0x04, 0xAE, 0x6C,
0x9A, 0xC8, 0xAA, 0x0D
};
static const unsigned char PROGMEM AmazonRootCA1_RSA_E[] = {
0x01, 0x00, 0x01
};
#endif
//
// ========== cumulative CA =================
//
const br_x509_trust_anchor PROGMEM Tasmota_TA[] = {
#if ! defined(OMIT_LETS_ENCRYPT_CERT)
{
{ (unsigned char *)LetsEncrypt_ISRG_Root_X1_DN, sizeof LetsEncrypt_ISRG_Root_X1_DN },
BR_X509_TA_CA,
{
BR_KEYTYPE_RSA,
{ .rsa = {
(unsigned char *)LetsEncrypt_ISRG_Root_X1_RSA_N, sizeof LetsEncrypt_ISRG_Root_X1_RSA_N,
(unsigned char *)LetsEncrypt_ISRG_Root_X1_RSA_E, sizeof LetsEncrypt_ISRG_Root_X1_RSA_E,
} }
}
}
#if ! defined(OMIT_AWS_CERT) || defined(INCLUDE_LOCAL_CERT)
,
#endif
#endif
#if ! defined(OMIT_AWS_CERT)
{
{ (unsigned char *)AmazonRootCA1_DN, sizeof AmazonRootCA1_DN },
BR_X509_TA_CA,
{
BR_KEYTYPE_RSA,
{ .rsa = {
(unsigned char *)AmazonRootCA1_RSA_N, sizeof AmazonRootCA1_RSA_N,
(unsigned char *)AmazonRootCA1_RSA_E, sizeof AmazonRootCA1_RSA_E,
} }
}
}
#if defined(INCLUDE_LOCAL_CERT)
,
#endif
#endif
#if defined(INCLUDE_LOCAL_CERT)
#include <include/local_ca_descriptor.h>
#endif
};
constexpr size_t Tasmota_TA_size = sizeof(Tasmota_TA) / sizeof(br_x509_trust_anchor);
#endif
#endif
#endif
+9
View File
@@ -2970,6 +2970,15 @@ To create/register a plugin, you have to :
#endif
#endif
#if FEATURE_MQTT_TLS
#if defined(FEATURE_TLS) && !FEATURE_TLS
#undef FEATURE_TLS
#endif
#ifndef FEATURE_TLS
#define FEATURE_TLS 1
#endif
#endif
#ifdef USES_ESPEASY_NOW
#if defined(LIMIT_BUILD_SIZE) || defined(ESP8266_1M) || (defined(ESP8266) && defined(PLUGIN_BUILD_IR))
// Will not fit on ESP8266 along with IR plugins included
+40 -41
View File
@@ -7,6 +7,8 @@
#include "../ControllerQueue/MQTT_queue_element.h"
#include "../CustomBuild/Certificate_CA.h"
#include "../DataStructs/ControllerSettingsStruct.h"
#include "../DataStructs/ESPEasy_EventStruct.h"
@@ -210,18 +212,24 @@ bool MQTTConnect(controllerIndex_t controller_idx)
const TLS_types TLS_type = ControllerSettings->TLStype();
if ((TLS_type != TLS_types::NoTLS) && (nullptr == mqtt_tls)) {
# ifdef ESP32
mqtt_tls = new ESPEasy_WiFiClientSecure;
# endif // ifdef ESP32
# ifdef ESP8266
mqtt_tls = new BearSSL::WiFiClientSecure;
# endif // ifdef ESP8266
#ifdef ESP32
#if MQTT_MAX_PACKET_SIZE > 2000
mqtt_tls = new BearSSL::WiFiClientSecure_light(4096,4096);
#else
mqtt_tls = new BearSSL::WiFiClientSecure_light(2048,2048);
#endif
#else // ESP32 - ESP8266
mqtt_tls = new BearSSL::WiFiClientSecure_light(1024,1024);
#endif
mqtt_rootCA.clear();
if (mqtt_tls == nullptr) {
mqtt_tls_last_errorstr = F("MQTT : Could not create TLS client, out of memory");
addLog(LOG_LEVEL_ERROR, mqtt_tls_last_errorstr);
return false;
} else {
mqtt_tls->setUtcTime_fcn(getUnixTime);
mqtt_tls->setCfgTime_fcn(get_build_unixtime);
}
}
@@ -257,24 +265,8 @@ bool MQTTConnect(controllerIndex_t controller_idx)
case TLS_types::TLS_CA_CERT:
{
mqtt_rootCA.clear();
/*
{
static int previousFree = FreeMem();
const int freemem = FreeMem();
String analyse = F(" free memory: ");
analyse += freemem;
analyse += F(" largest free block: ");
analyse += getMaxFreeBlock();
analyse += F(" Difference: ");
analyse += previousFree - freemem;
addLog(LOG_LEVEL_INFO, analyse);
previousFree = freemem;
}
*/
// FIXME TD-er: Must convert rootCA from file to format accepted by bearSSL
if (mqtt_rootCA.isEmpty() && (mqtt_tls != nullptr)) {
LoadCertificate(ControllerSettings->getCertificateFilename(), mqtt_rootCA);
@@ -286,13 +278,14 @@ bool MQTTConnect(controllerIndex_t controller_idx)
return false;
}
# ifdef ESP32
mqtt_tls->setCACert(mqtt_rootCA.c_str());
# endif // ifdef ESP32
# ifdef ESP8266
mqtt_X509List.append(mqtt_rootCA.c_str());
mqtt_tls->setTrustAnchors(&mqtt_X509List);
# endif // ifdef ESP8266
//mqtt_X509List.append(mqtt_rootCA.c_str());
// mqtt_tls->setTrustAnchors(&mqtt_X509List);
}
*/
if (mqtt_tls != nullptr) {
mqtt_tls->setTrustAnchor(Tasmota_TA, Tasmota_TA_size);
}
break;
}
@@ -451,15 +444,11 @@ bool MQTTConnect(controllerIndex_t controller_idx)
if (mqtt_tls != nullptr)
{
char buf[128] = { 0 };
# ifdef ESP8266
mqtt_tls_last_error = mqtt_tls->getLastSSLError(buf, 128);
# endif // ifdef ESP8266
# ifdef ESP32
mqtt_tls_last_error = mqtt_tls->lastError(buf, 128);
mqtt_tls_last_error = mqtt_tls->getLastError();
mqtt_tls->clearLastError();
# endif // ifdef ESP32
mqtt_tls_last_errorstr = buf;
//mqtt_tls_last_errorstr = buf;
}
# ifdef ESP32
@@ -489,6 +478,8 @@ bool MQTTConnect(controllerIndex_t controller_idx)
dn.trim();
}
// FIXME TD-er: Must implement fingerprint verification
/*
if (mqtt_tls != nullptr) {
if (!mqtt_tls->verify(
fp.c_str(),
@@ -499,6 +490,7 @@ bool MQTTConnect(controllerIndex_t controller_idx)
MQTTresult = false;
}
}
*/
}
}
# endif // ifdef ESP32
@@ -538,6 +530,9 @@ bool MQTTConnect(controllerIndex_t controller_idx)
# if FEATURE_MQTT_TLS
# ifdef ESP32
// FIXME TD-er: Must get certificate info
/*
if ((mqtt_tls != nullptr) && loglevelActiveFor(LOG_LEVEL_INFO))
{
String log = F("MQTT : Peer certificate info: ");
@@ -546,6 +541,7 @@ bool MQTTConnect(controllerIndex_t controller_idx)
log += mqtt_tls->getPeerCertificateInfo();
addLogMove(LOG_LEVEL_INFO, log);
}
*/
# endif // ifdef ESP32
# endif // if FEATURE_MQTT_TLS
@@ -902,13 +898,12 @@ bool GetTLSfingerprint(String& fp)
# ifdef ESP32
if (MQTTclient_connected && (mqtt_tls != nullptr)) {
uint8_t sha256_result[32] = { 0 };
if (mqtt_tls->getFingerprintSHA256(sha256_result)) {
const uint8_t *recv_fingerprint = mqtt_tls->getRecvPubKeyFingerprint();
if (recv_fingerprint != nullptr) {
fp.reserve(64);
for (size_t i = 0; i < 32; ++i) {
const String tmp(sha256_result[i], HEX);
for (size_t i = 0; i < 21; ++i) {
const String tmp(recv_fingerprint[i], HEX);
switch (tmp.length()) {
case 0:
@@ -933,6 +928,9 @@ bool GetTLS_Certificate(String& cert, bool caRoot)
{
# ifdef ESP32
// FIXME TD-er: Implement retrieval of certificate
/*
if (MQTTclient_connected && (mqtt_tls != nullptr)) {
String subject;
@@ -940,6 +938,7 @@ bool GetTLS_Certificate(String& cert, bool caRoot)
return true;
}
}
*/
# endif // ifdef ESP32
return false;
}
+5 -1
View File
@@ -1,3 +1,7 @@
#include "../Globals/ESPEasy_time.h"
ESPEasy_time node_time;
ESPEasy_time node_time;
uint32_t getUnixTime() {
return node_time.getUnixTime();
}
+2
View File
@@ -5,6 +5,8 @@
extern ESPEasy_time node_time;
// Global function needed for SSL
uint32_t getUnixTime();
#endif // GLOBALS_ESPEASY_TIME_H
+1 -1
View File
@@ -12,7 +12,7 @@ String mqtt_tls_last_errorstr;
int32_t mqtt_tls_last_error = 0;
# ifdef ESP32
ESPEasy_WiFiClientSecure* mqtt_tls;
BearSSL::WiFiClientSecure_light* mqtt_tls;
# endif // ifdef ESP32
# ifdef ESP8266
BearSSL::WiFiClientSecure* mqtt_tls;
+3 -13
View File
@@ -11,11 +11,7 @@
# include <PubSubClient.h>
# if FEATURE_MQTT_TLS
# ifdef ESP32
# include "../Helpers/ESPEasy_WiFiClientSecure.h"
# else
# include <WiFiClientSecure.h>
# endif
#include <WiFiClientSecureLightBearSSL.h>
# endif // if FEATURE_MQTT_TLS
// MQTT client
@@ -23,14 +19,8 @@ extern WiFiClient mqtt;
# if FEATURE_MQTT_TLS
extern String mqtt_tls_last_errorstr;
extern int32_t mqtt_tls_last_error;
# ifdef ESP32
extern ESPEasy_WiFiClientSecure* mqtt_tls;
# endif // ifdef ESP32
# ifdef ESP8266
extern BearSSL::WiFiClientSecure* mqtt_tls;
extern BearSSL::X509List mqtt_X509List;
# endif // ifdef ESP8266
extern BearSSL::WiFiClientSecure_light* mqtt_tls;
//extern BearSSL::X509List mqtt_X509List;
extern String mqtt_rootCA;
extern String mqtt_fingerprint;
@@ -1,441 +0,0 @@
#include "../Helpers/ESPEasy_WiFiClientSecure.h"
/*
ESPEasy_WiFiClientSecure.cpp - Client Secure class for ESP32
Copyright (c) 2016 Hristo Gochkov All right reserved.
Additions Copyright (C) 2017 Evandro Luis Copercini.
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
*/
#ifdef ESP32
#include <lwip/sockets.h>
#include <lwip/netdb.h>
#include <errno.h>
// FIXME TD-er: Feels wrong this needs to be included here to use mbedtls_pem_write_buffer
#include <mbedtls/pem.h>
#include <mbedtls/oid.h>
#include <vector>
#undef connect
#undef write
#undef read
ESPEasy_WiFiClientSecure::ESPEasy_WiFiClientSecure()
{
_connected = false;
sslclient = new ESPEasy_sslclient_context;
ssl_init(sslclient);
sslclient->socket = -1;
sslclient->handshake_timeout = 120000;
_use_insecure = false;
_CA_cert = NULL;
_cert = NULL;
_private_key = NULL;
_pskIdent = NULL;
_psKey = NULL;
next = NULL;
_alpn_protos = NULL;
}
ESPEasy_WiFiClientSecure::ESPEasy_WiFiClientSecure(int sock)
{
_connected = false;
_timeout = 0;
sslclient = new ESPEasy_sslclient_context;
ssl_init(sslclient);
sslclient->socket = sock;
sslclient->handshake_timeout = 120000;
if (sock >= 0) {
_connected = true;
}
_CA_cert = NULL;
_cert = NULL;
_private_key = NULL;
_pskIdent = NULL;
_psKey = NULL;
next = NULL;
_alpn_protos = NULL;
}
ESPEasy_WiFiClientSecure::~ESPEasy_WiFiClientSecure()
{
stop();
delete sslclient;
}
ESPEasy_WiFiClientSecure &ESPEasy_WiFiClientSecure::operator=(const ESPEasy_WiFiClientSecure &other)
{
stop();
sslclient->socket = other.sslclient->socket;
_connected = other._connected;
return *this;
}
void ESPEasy_WiFiClientSecure::stop()
{
if (sslclient->socket >= 0) {
close(sslclient->socket);
sslclient->socket = -1;
_connected = false;
_peek = -1;
}
stop_ssl_socket(sslclient, _CA_cert, _cert, _private_key);
}
int ESPEasy_WiFiClientSecure::connect(IPAddress ip, uint16_t port)
{
if (_pskIdent && _psKey)
return connect(ip, port, _pskIdent, _psKey);
return connect(ip, port, _CA_cert, _cert, _private_key);
}
int ESPEasy_WiFiClientSecure::connect(IPAddress ip, uint16_t port, int32_t timeout){
_timeout = timeout;
return connect(ip, port);
}
int ESPEasy_WiFiClientSecure::connect(const char *host, uint16_t port)
{
if (_pskIdent && _psKey)
return connect(host, port, _pskIdent, _psKey);
return connect(host, port, _CA_cert, _cert, _private_key);
}
int ESPEasy_WiFiClientSecure::connect(const char *host, uint16_t port, int32_t timeout){
_timeout = timeout;
return connect(host, port);
}
int ESPEasy_WiFiClientSecure::connect(IPAddress ip, uint16_t port, const char *CA_cert, const char *cert, const char *private_key)
{
return connect(ip.toString().c_str(), port, CA_cert, cert, private_key);
}
int ESPEasy_WiFiClientSecure::connect(const char *host, uint16_t port, const char *CA_cert, const char *cert, const char *private_key)
{
if(_timeout > 0){
sslclient->handshake_timeout = _timeout;
}
int ret = start_ssl_client(sslclient, host, port, _timeout, CA_cert, cert, private_key, NULL, NULL, _use_insecure, _alpn_protos);
_lastError = ret;
if (ret < 0) {
log_e("start_ssl_client: %d", ret);
stop();
return 0;
}
_connected = true;
return 1;
}
int ESPEasy_WiFiClientSecure::connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psKey) {
return connect(ip.toString().c_str(), port, pskIdent, psKey);
}
int ESPEasy_WiFiClientSecure::connect(const char *host, uint16_t port, const char *pskIdent, const char *psKey) {
log_v("start_ssl_client with PSK");
if(_timeout > 0){
sslclient->handshake_timeout = _timeout;
}
int ret = start_ssl_client(sslclient, host, port, _timeout, NULL, NULL, NULL, pskIdent, psKey, _use_insecure, _alpn_protos);
_lastError = ret;
if (ret < 0) {
log_e("start_ssl_client: %d", ret);
stop();
return 0;
}
_connected = true;
return 1;
}
int ESPEasy_WiFiClientSecure::peek(){
if(_peek >= 0){
return _peek;
}
_peek = timedRead();
return _peek;
}
size_t ESPEasy_WiFiClientSecure::write(uint8_t data)
{
return write(&data, 1);
}
int ESPEasy_WiFiClientSecure::read()
{
uint8_t data = -1;
int res = read(&data, 1);
if (res < 0) {
return res;
}
return data;
}
size_t ESPEasy_WiFiClientSecure::write(const uint8_t *buf, size_t size)
{
if (!_connected) {
return 0;
}
int res = send_ssl_data(sslclient, buf, size);
if (res < 0) {
stop();
res = 0;
}
return res;
}
int ESPEasy_WiFiClientSecure::read(uint8_t *buf, size_t size)
{
int peeked = 0;
int avail = available();
if ((!buf && size) || avail <= 0) {
return -1;
}
if(!size){
return 0;
}
if(_peek >= 0){
buf[0] = _peek;
_peek = -1;
size--;
avail--;
if(!size || !avail){
return 1;
}
buf++;
peeked = 1;
}
int res = get_ssl_receive(sslclient, buf, size);
if (res < 0) {
stop();
return peeked?peeked:res;
}
return res + peeked;
}
int ESPEasy_WiFiClientSecure::available()
{
int peeked = (_peek >= 0);
if (!_connected) {
return peeked;
}
int res = data_to_read(sslclient);
if (res < 0) {
stop();
return peeked?peeked:res;
}
return res+peeked;
}
uint8_t ESPEasy_WiFiClientSecure::connected()
{
uint8_t dummy = 0;
read(&dummy, 0);
return _connected;
}
void ESPEasy_WiFiClientSecure::setInsecure()
{
_CA_cert = NULL;
_cert = NULL;
_private_key = NULL;
_pskIdent = NULL;
_psKey = NULL;
_use_insecure = true;
}
void ESPEasy_WiFiClientSecure::setCACert (const char *rootCA)
{
_CA_cert = rootCA;
}
void ESPEasy_WiFiClientSecure::setCertificate (const char *client_ca)
{
_cert = client_ca;
}
void ESPEasy_WiFiClientSecure::setPrivateKey (const char *private_key)
{
_private_key = private_key;
}
void ESPEasy_WiFiClientSecure::setPreSharedKey(const char *pskIdent, const char *psKey) {
_pskIdent = pskIdent;
_psKey = psKey;
}
bool ESPEasy_WiFiClientSecure::verify(const char* fp, const char* domain_name)
{
if (!sslclient)
return false;
return verify_ssl_fingerprint(sslclient, fp, domain_name);
}
char *ESPEasy_WiFiClientSecure::_streamLoad(Stream& stream, size_t size) {
char *dest = (char*)malloc(size+1);
if (!dest) {
return nullptr;
}
if (size != stream.readBytes(dest, size)) {
free(dest);
dest = nullptr;
return nullptr;
}
dest[size] = '\0';
return dest;
}
bool ESPEasy_WiFiClientSecure::loadCACert(Stream& stream, size_t size) {
char *dest = _streamLoad(stream, size);
bool ret = false;
if (dest) {
setCACert(dest);
ret = true;
}
return ret;
}
bool ESPEasy_WiFiClientSecure::loadCertificate(Stream& stream, size_t size) {
char *dest = _streamLoad(stream, size);
bool ret = false;
if (dest) {
setCertificate(dest);
ret = true;
}
return ret;
}
bool ESPEasy_WiFiClientSecure::loadPrivateKey(Stream& stream, size_t size) {
char *dest = _streamLoad(stream, size);
bool ret = false;
if (dest) {
setPrivateKey(dest);
ret = true;
}
return ret;
}
int ESPEasy_WiFiClientSecure::lastError(char *buf, const size_t size)
{
if (!_lastError) {
return 0;
}
mbedtls_strerror(_lastError, buf, size);
return _lastError;
}
void ESPEasy_WiFiClientSecure::setHandshakeTimeout(unsigned long handshake_timeout)
{
sslclient->handshake_timeout = handshake_timeout * 1000;
}
void ESPEasy_WiFiClientSecure::setAlpnProtocols(const char **alpn_protos)
{
_alpn_protos = alpn_protos;
}
String ESPEasy_WiFiClientSecure::getPeerCertificateInfo(const mbedtls_x509_crt* peer)
{
if (peer == nullptr) {
peer = getPeerCertificate();
}
String res;
if (peer != nullptr) {
char buf[1024] = {0};
int l = mbedtls_x509_crt_info (buf, sizeof(buf), "", peer);
if (l > 0) {
if (res.reserve(l)) {
for (int i = 0; i < l; ++i) {
res += buf[i];
}
}
}
}
return res;
}
int ESPEasy_WiFiClientSecure::getPeerCertificate(String& pem, String& subject, bool caRoot)
{
const mbedtls_x509_crt *chain;
chain = getPeerCertificate();
int error {0};
bool done = false;
while (chain != nullptr && error == 0 && !done) {
if (!caRoot || (chain->ca_istrue && chain->next == nullptr)) {
done = true;
error = ESPEasy_WiFiClientSecure::cert_to_pem(chain, pem, subject);
}
chain = chain->next;
}
return error;
}
int ESPEasy_WiFiClientSecure::cert_to_pem(const mbedtls_x509_crt *crt, String& pem, String& subject)
{
const String pem_begin_crt = F("-----BEGIN CERTIFICATE-----\n");
const String pem_end_crt = F("-----END CERTIFICATE-----");
pem.clear();
subject.clear();
const mbedtls_asn1_named_data* common_name = &crt->subject;
while (common_name != nullptr) {
// While iterating through DN objects, check for CN object
if (!MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &common_name->oid))
{
subject.reserve(common_name->val.len);
const unsigned char* p = common_name->val.p;
for (auto i = 0; i < common_name->val.len; ++i, ++p) {
subject += static_cast<char>(*p);
}
}
// Fetch next DN object
common_name = common_name->next;
}
size_t written{};
const size_t buffer_size =
pem_begin_crt.length() +
pem_end_crt.length() +
2* crt->raw.len;
std::vector<unsigned char> pem_buf;
pem_buf.resize(buffer_size, 0u);
int ret = mbedtls_pem_write_buffer(
pem_begin_crt.c_str(), pem_end_crt.c_str(),
crt->raw.p, crt->raw.len,
&pem_buf[0], buffer_size, &written);
if (ret == 0) {
pem.reserve(written);
for (auto i = 0; i < written; ++i) {
pem += static_cast<char>(pem_buf[i]);
}
}
return ret;
}
#endif
-125
View File
@@ -1,125 +0,0 @@
/*
ESPEasy_WiFiClientSecure.h - Base class that provides Client SSL to ESP32
Copyright (c) 2011 Adrian McEwen. All right reserved.
Additions Copyright (C) 2017 Evandro Luis Copercini.
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
*/
/*
Almost literal copy of https://github.com/brokentoaster/arduino-esp32/blob/master/libraries/WiFiClientSecure/src/WiFiClientSecure.h
Changed into "ESPEasy" version to incorporate some fixes
for memory leaks not yet present in the "older" core versions.
*/
#ifdef ESP32
#ifndef ESPEASY_WiFiClientSecure_h
#define ESPEASY_WiFiClientSecure_h
#include <Arduino.h>
#include <IPAddress.h>
#include <WiFi.h>
#include "../Helpers/ESPEasy_ssl_client.h"
class ESPEasy_WiFiClientSecure : public WiFiClient
{
protected:
ESPEasy_sslclient_context *sslclient;
int _lastError = 0;
int _peek = -1;
int _timeout = 0;
bool _use_insecure;
const char *_CA_cert;
const char *_cert;
const char *_private_key;
const char *_pskIdent; // identity for PSK cipher suites
const char *_psKey; // key in hex for PSK cipher suites
const char **_alpn_protos;
public:
ESPEasy_WiFiClientSecure *next;
ESPEasy_WiFiClientSecure();
ESPEasy_WiFiClientSecure(int socket);
~ESPEasy_WiFiClientSecure();
int connect(IPAddress ip, uint16_t port);
int connect(IPAddress ip, uint16_t port, int32_t timeout);
int connect(const char *host, uint16_t port);
int connect(const char *host, uint16_t port, int32_t timeout);
int connect(IPAddress ip, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key);
int connect(const char *host, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key);
int connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psKey);
int connect(const char *host, uint16_t port, const char *pskIdent, const char *psKey);
int peek();
size_t write(uint8_t data);
size_t write(const uint8_t *buf, size_t size);
int available();
int read();
int read(uint8_t *buf, size_t size);
void flush() {}
void stop();
uint8_t connected();
int lastError(char *buf, const size_t size);
void clearLastError() { _lastError = 0; }
void setInsecure(); // Don't validate the chain, just accept whatever is given. VERY INSECURE!
void setPreSharedKey(const char *pskIdent, const char *psKey); // psKey in Hex
void setCACert(const char *rootCA);
void setCertificate(const char *client_ca);
void setPrivateKey (const char *private_key);
bool loadCACert(Stream& stream, size_t size);
bool loadCertificate(Stream& stream, size_t size);
bool loadPrivateKey(Stream& stream, size_t size);
bool verify(const char* fingerprint, const char* domain_name);
void setHandshakeTimeout(unsigned long handshake_timeout);
void setAlpnProtocols(const char **alpn_protos);
String getPeerCertificateInfo(const mbedtls_x509_crt* crt = nullptr);
const mbedtls_x509_crt* getPeerCertificate() { return mbedtls_ssl_get_peer_cert(&sslclient->ssl_ctx); };
bool getFingerprintSHA256(uint8_t sha256_result[32]) { return get_peer_fingerprint(sslclient, sha256_result); };
int getPeerCertificate(String& pem, String& subject, bool caRoot);
// See: https://stackoverflow.com/a/63730321/8708166
static int cert_to_pem(const mbedtls_x509_crt *crt, String& pem, String& subject);
operator bool()
{
return connected();
}
ESPEasy_WiFiClientSecure &operator=(const ESPEasy_WiFiClientSecure &other);
bool operator==(const bool value)
{
return bool() == value;
}
bool operator!=(const bool value)
{
return bool() != value;
}
bool operator==(const ESPEasy_WiFiClientSecure &);
bool operator!=(const ESPEasy_WiFiClientSecure &rhs)
{
return !this->operator==(rhs);
};
int socket()
{
return sslclient->socket = -1;
}
private:
char *_streamLoad(Stream& stream, size_t size);
//friend class WiFiServer;
using Print::write;
};
#endif
#endif
-575
View File
@@ -1,575 +0,0 @@
#include "../Helpers/ESPEasy_ssl_client.h"
/* Provide SSL/TLS functions to ESP32 with Arduino IDE
*
* Adapted from the ssl_client1 example of mbedtls.
*
* Original Copyright (C) 2006-2015, ARM Limited, All Rights Reserved, Apache 2.0 License.
* Additions Copyright (C) 2017 Evandro Luis Copercini, Apache 2.0 License.
*/
#ifdef ESP32
#include <Arduino.h>
#include <esp32-hal-log.h>
#include <lwip/err.h>
#include <lwip/sockets.h>
#include <lwip/sys.h>
#include <lwip/netdb.h>
#include <mbedtls/sha256.h>
#include <mbedtls/oid.h>
#include <algorithm>
#include <string>
#include <WiFi.h>
#if !defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED) && !defined(MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED)
# warning "Please configure IDF framework to include mbedTLS -> Enable pre-shared-key ciphersuites and activate at least one cipher"
#else
const char *ESPEasy_pers = "esp32-tls";
static int _handle_error(int err, const char * function, int line)
{
if(err == -30848){
return err;
}
#ifdef MBEDTLS_ERROR_C
char error_buf[100];
mbedtls_strerror(err, error_buf, 100);
log_e("[%s():%d]: (%d) %s", function, line, err, error_buf);
#else
log_e("[%s():%d]: code %d", function, line, err);
#endif
return err;
}
#define handle_error(e) _handle_error(e, __FUNCTION__, __LINE__)
ESPEasy_sslclient_context::ESPEasy_sslclient_context()
{
mbedtls_ssl_init(&ssl_ctx);
mbedtls_ssl_config_init(&ssl_conf);
mbedtls_ctr_drbg_init(&drbg_ctx);
mbedtls_entropy_init(&entropy_ctx);
mbedtls_x509_crt_init(&ca_cert);
mbedtls_x509_crt_init(&client_cert);
mbedtls_pk_init(&client_key);
}
ESPEasy_sslclient_context::~ESPEasy_sslclient_context()
{
free_ca_cert();
free_client_cert();
free_client_key();
}
void ESPEasy_sslclient_context::free_ca_cert()
{
mbedtls_x509_crt_free(&ca_cert);
}
void ESPEasy_sslclient_context::free_client_cert()
{
mbedtls_x509_crt_free(&client_cert);
}
void ESPEasy_sslclient_context::free_client_key()
{
mbedtls_pk_free(&client_key);
}
void ssl_init(ESPEasy_sslclient_context *ssl_client)
{
mbedtls_ssl_free(&ssl_client->ssl_ctx);
mbedtls_ssl_config_free(&ssl_client->ssl_conf);
mbedtls_ctr_drbg_free(&ssl_client->drbg_ctx);
mbedtls_ssl_init(&ssl_client->ssl_ctx);
mbedtls_ssl_config_init(&ssl_client->ssl_conf);
mbedtls_ctr_drbg_init(&ssl_client->drbg_ctx);
}
int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, uint32_t port, int timeout, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey, bool insecure, const char **alpn_protos)
{
char buf[512];
int ret, flags;
int enable = 1;
log_v("Free internal heap before TLS %u", ESP.getFreeHeap());
if (rootCABuff == NULL && pskIdent == NULL && psKey == NULL && !insecure) {
return -1;
}
log_v("Starting socket");
ssl_client->socket = -1;
ssl_client->socket = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ssl_client->socket < 0) {
log_e("ERROR opening socket");
return ssl_client->socket;
}
IPAddress srv((uint32_t)0);
if(!WiFiGenericClass::hostByName(host, srv)){
return -1;
}
fcntl( ssl_client->socket, F_SETFL, fcntl( ssl_client->socket, F_GETFL, 0 ) | O_NONBLOCK );
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = srv;
serv_addr.sin_port = htons(port);
if(timeout <= 0){
timeout = 30000; // Milli seconds.
}
fd_set fdset;
struct timeval tv;
FD_ZERO(&fdset);
FD_SET(ssl_client->socket, &fdset);
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
int res = lwip_connect(ssl_client->socket, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
if (res < 0 && errno != EINPROGRESS) {
log_e("connect on fd %d, errno: %d, \"%s\"", ssl_client->socket, errno, strerror(errno));
close(ssl_client->socket);
return -1;
}
res = select(ssl_client->socket + 1, nullptr, &fdset, nullptr, timeout<0 ? nullptr : &tv);
if (res < 0) {
log_e("select on fd %d, errno: %d, \"%s\"", ssl_client->socket, errno, strerror(errno));
close(ssl_client->socket);
return -1;
} else if (res == 0) {
log_i("select returned due to timeout %d ms for fd %d", timeout, ssl_client->socket);
close(ssl_client->socket);
return -1;
} else {
int sockerr;
socklen_t len = (socklen_t)sizeof(int);
res = getsockopt(ssl_client->socket, SOL_SOCKET, SO_ERROR, &sockerr, &len);
if (res < 0) {
log_e("getsockopt on fd %d, errno: %d, \"%s\"", ssl_client->socket, errno, strerror(errno));
close(ssl_client->socket);
return -1;
}
if (sockerr != 0) {
log_e("socket error on fd %d, errno: %d, \"%s\"", ssl_client->socket, sockerr, strerror(sockerr));
close(ssl_client->socket);
return -1;
}
}
#define ROE(x,msg) { if (((x)<0)) { log_e("LWIP Socket config of " msg " failed."); return -1; }}
ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),"SO_RCVTIMEO");
ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)),"SO_SNDTIMEO");
ROE(lwip_setsockopt(ssl_client->socket, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)),"TCP_NODELAY");
ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable)),"SO_KEEPALIVE");
log_v("Seeding the random number generator");
mbedtls_entropy_free(&ssl_client->entropy_ctx);
mbedtls_entropy_init(&ssl_client->entropy_ctx);
ret = mbedtls_ctr_drbg_seed(&ssl_client->drbg_ctx, mbedtls_entropy_func,
&ssl_client->entropy_ctx, (const unsigned char *) ESPEasy_pers, strlen(ESPEasy_pers));
if (ret < 0) {
return handle_error(ret);
}
log_v("Setting up the SSL/TLS structure...");
if ((ret = mbedtls_ssl_config_defaults(&ssl_client->ssl_conf,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
return handle_error(ret);
}
if (alpn_protos != NULL) {
log_v("Setting ALPN protocols");
if ((ret = mbedtls_ssl_conf_alpn_protocols(&ssl_client->ssl_conf, alpn_protos) ) != 0) {
return handle_error(ret);
}
}
// MBEDTLS_SSL_VERIFY_REQUIRED if a CA certificate is defined on Arduino IDE and
// MBEDTLS_SSL_VERIFY_NONE if not.
if (insecure) {
mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_NONE);
log_i("WARNING: Skipping SSL Verification. INSECURE!");
} else if (rootCABuff != NULL) {
log_v("Loading CA cert");
mbedtls_x509_crt_init(&ssl_client->ca_cert);
mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED);
ret = mbedtls_x509_crt_parse(&ssl_client->ca_cert, (const unsigned char *)rootCABuff, strlen(rootCABuff) + 1);
mbedtls_ssl_conf_ca_chain(&ssl_client->ssl_conf, &ssl_client->ca_cert, NULL);
//mbedtls_ssl_conf_verify(&ssl_client->ssl_ctx, my_verify, NULL );
if (ret < 0) {
// free the ca_cert in the case parse failed, otherwise, the old ca_cert still in the heap memory, that lead to "out of memory" crash.
ssl_client->free_ca_cert();
return handle_error(ret);
}
} else if (pskIdent != NULL && psKey != NULL) {
log_v("Setting up PSK");
// convert PSK from hex to binary
if ((strlen(psKey) & 1) != 0 || strlen(psKey) > 2*MBEDTLS_PSK_MAX_LEN) {
log_e("pre-shared key not valid hex or too long");
return -1;
}
unsigned char psk[MBEDTLS_PSK_MAX_LEN];
size_t psk_len = strlen(psKey)/2;
for (int j=0; j<strlen(psKey); j+= 2) {
char c = psKey[j];
if (c >= '0' && c <= '9') c -= '0';
else if (c >= 'A' && c <= 'F') c -= 'A' - 10;
else if (c >= 'a' && c <= 'f') c -= 'a' - 10;
else return -1;
psk[j/2] = c<<4;
c = psKey[j+1];
if (c >= '0' && c <= '9') c -= '0';
else if (c >= 'A' && c <= 'F') c -= 'A' - 10;
else if (c >= 'a' && c <= 'f') c -= 'a' - 10;
else return -1;
psk[j/2] |= c;
}
// set mbedtls config
ret = mbedtls_ssl_conf_psk(&ssl_client->ssl_conf, psk, psk_len,
(const unsigned char *)pskIdent, strlen(pskIdent));
if (ret != 0) {
log_e("mbedtls_ssl_conf_psk returned %d", ret);
return handle_error(ret);
}
} else {
return -1;
}
if (!insecure && cli_cert != NULL && cli_key != NULL) {
mbedtls_x509_crt_init(&ssl_client->client_cert);
mbedtls_pk_init(&ssl_client->client_key);
log_v("Loading CRT cert");
ret = mbedtls_x509_crt_parse(&ssl_client->client_cert, (const unsigned char *)cli_cert, strlen(cli_cert) + 1);
if (ret < 0) {
// free the client_cert in the case parse failed, otherwise, the old client_cert still in the heap memory, that lead to "out of memory" crash.
ssl_client->free_client_cert();
return handle_error(ret);
}
log_v("Loading private key");
ret = mbedtls_pk_parse_key(&ssl_client->client_key, (const unsigned char *)cli_key, strlen(cli_key) + 1, NULL, 0);
if (ret != 0) {
return handle_error(ret);
}
mbedtls_ssl_conf_own_cert(&ssl_client->ssl_conf, &ssl_client->client_cert, &ssl_client->client_key);
}
log_v("Setting hostname for TLS session...");
// Hostname set here should match CN in server certificate
if((ret = mbedtls_ssl_set_hostname(&ssl_client->ssl_ctx, host)) != 0){
return handle_error(ret);
}
mbedtls_ssl_conf_rng(&ssl_client->ssl_conf, mbedtls_ctr_drbg_random, &ssl_client->drbg_ctx);
if ((ret = mbedtls_ssl_setup(&ssl_client->ssl_ctx, &ssl_client->ssl_conf)) != 0) {
return handle_error(ret);
}
mbedtls_ssl_set_bio(&ssl_client->ssl_ctx, &ssl_client->socket, mbedtls_net_send, mbedtls_net_recv, NULL );
log_v("Performing the SSL/TLS handshake...");
unsigned long handshake_start_time=millis();
while ((ret = mbedtls_ssl_handshake(&ssl_client->ssl_ctx)) != 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
// ++++++++++ ADDED TO FIX MEMORY LEAK ON FAILED CONNECTION ++++++++++
ssl_client->free_client_key();
ssl_client->free_ca_cert();
ssl_client->free_client_cert();
// ++++++++++ END ++++++++++
return handle_error(ret);
}
if ((millis()-handshake_start_time) > ssl_client->handshake_timeout) {
// ++++++++++ ADDED TO FIX MEMORY LEAK ON FAILED CONNECTION ++++++++++
ssl_client->free_client_key();
ssl_client->free_ca_cert();
ssl_client->free_client_cert();
// ++++++++++ END ++++++++++
return -1;
}
vTaskDelay(2);//2 ticks
}
if (cli_cert != NULL && cli_key != NULL) {
log_d("Protocol is %s Ciphersuite is %s", mbedtls_ssl_get_version(&ssl_client->ssl_ctx), mbedtls_ssl_get_ciphersuite(&ssl_client->ssl_ctx));
if ((ret = mbedtls_ssl_get_record_expansion(&ssl_client->ssl_ctx)) >= 0) {
log_d("Record expansion is %d", ret);
} else {
log_w("Record expansion is unknown (compression)");
}
}
log_v("Verifying peer X.509 certificate...");
if ((flags = mbedtls_ssl_get_verify_result(&ssl_client->ssl_ctx)) != 0) {
memset(buf, 0, sizeof(buf));
mbedtls_x509_crt_verify_info(buf, sizeof(buf), " ! ", flags);
log_e("Failed to verify peer certificate! verification info: %s", buf);
stop_ssl_socket(ssl_client, rootCABuff, cli_cert, cli_key); //It's not safe continue.
// ++++++++++ ADDED TO FIX MEMORY LEAK ON FAILED CONNECTION ++++++++++
ssl_client->free_client_key();
ssl_client->free_ca_cert();
ssl_client->free_client_cert();
// ++++++++++ END ++++++++++
return handle_error(ret);
} else {
log_v("Certificate verified.");
}
ssl_client->free_ca_cert();
ssl_client->free_client_cert();
ssl_client->free_client_key();
log_v("Free internal heap after TLS %u", ESP.getFreeHeap());
return ssl_client->socket;
}
void stop_ssl_socket(ESPEasy_sslclient_context *ssl_client, const char *rootCABuff, const char *cli_cert, const char *cli_key)
{
log_v("Cleaning SSL connection.");
if (ssl_client->socket >= 0) {
close(ssl_client->socket);
ssl_client->socket = -1;
}
mbedtls_ssl_free(&ssl_client->ssl_ctx);
mbedtls_ssl_config_free(&ssl_client->ssl_conf);
mbedtls_ctr_drbg_free(&ssl_client->drbg_ctx);
mbedtls_entropy_free(&ssl_client->entropy_ctx);
ssl_client->free_ca_cert();
ssl_client->free_client_cert();
ssl_client->free_client_key();
}
int data_to_read(ESPEasy_sslclient_context *ssl_client)
{
int ret, res;
ret = mbedtls_ssl_read(&ssl_client->ssl_ctx, NULL, 0);
//log_e("RET: %i",ret); //for low level debug
res = mbedtls_ssl_get_bytes_avail(&ssl_client->ssl_ctx);
//log_e("RES: %i",res); //for low level debug
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret < 0) {
return handle_error(ret);
}
return res;
}
int send_ssl_data(ESPEasy_sslclient_context *ssl_client, const uint8_t *data, size_t len)
{
log_v("Writing HTTP request with %d bytes...", len); //for low level debug
int ret = -1;
while ((ret = mbedtls_ssl_write(&ssl_client->ssl_ctx, data, len)) <= 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret < 0) {
log_v("Handling error %d", ret); //for low level debug
return handle_error(ret);
}
//wait for space to become available
vTaskDelay(2);
}
return ret;
}
int get_ssl_receive(ESPEasy_sslclient_context *ssl_client, uint8_t *data, int length)
{
//log_d( "Reading HTTP response..."); //for low level debug
int ret = -1;
ret = mbedtls_ssl_read(&ssl_client->ssl_ctx, data, length);
//log_v( "%d bytes read", ret); //for low level debug
return ret;
}
static bool parseHexNibble(char pb, uint8_t* res)
{
if (pb >= '0' && pb <= '9') {
*res = (uint8_t) (pb - '0'); return true;
} else if (pb >= 'a' && pb <= 'f') {
*res = (uint8_t) (pb - 'a' + 10); return true;
} else if (pb >= 'A' && pb <= 'F') {
*res = (uint8_t) (pb - 'A' + 10); return true;
}
return false;
}
// Compare a name from certificate and domain name, return true if they match
static bool matchName(const std::string& name, const std::string& domainName)
{
size_t wildcardPos = name.find('*');
if (wildcardPos == std::string::npos) {
// Not a wildcard, expect an exact match
return name == domainName;
}
size_t firstDotPos = name.find('.');
if (wildcardPos > firstDotPos) {
// Wildcard is not part of leftmost component of domain name
// Do not attempt to match (rfc6125 6.4.3.1)
return false;
}
if (wildcardPos != 0 || firstDotPos != 1) {
// Matching of wildcards such as baz*.example.com and b*z.example.com
// is optional. Maybe implement this in the future?
return false;
}
size_t domainNameFirstDotPos = domainName.find('.');
if (domainNameFirstDotPos == std::string::npos) {
return false;
}
return domainName.substr(domainNameFirstDotPos) == name.substr(firstDotPos);
}
// Verifies certificate provided by the peer to match specified SHA256 fingerprint
bool verify_ssl_fingerprint(ESPEasy_sslclient_context *ssl_client, const char* fp, const char* domain_name)
{
// Convert hex string to byte array
uint8_t fingerprint_local[32];
int len = strlen(fp);
int pos = 0;
for (size_t i = 0; i < sizeof(fingerprint_local); ++i) {
while (pos < len && ((fp[pos] == ' ') || (fp[pos] == ':'))) {
++pos;
}
if (pos > len - 2) {
log_d("pos:%d len:%d fingerprint too short", pos, len);
return false;
}
uint8_t high, low;
if (!parseHexNibble(fp[pos], &high) || !parseHexNibble(fp[pos+1], &low)) {
log_d("pos:%d len:%d invalid hex sequence: %c%c", pos, len, fp[pos], fp[pos+1]);
return false;
}
pos += 2;
fingerprint_local[i] = low | (high << 4);
}
// Calculate certificate's SHA256 fingerprint
uint8_t fingerprint_remote[32];
if(!get_peer_fingerprint(ssl_client, fingerprint_remote))
return false;
// Check if fingerprints match
if (memcmp(fingerprint_local, fingerprint_remote, 32))
{
log_d("fingerprint doesn't match");
return false;
}
// Additionally check if certificate has domain name if provided
if (domain_name)
return verify_ssl_dn(ssl_client, domain_name);
else
return true;
}
bool get_peer_fingerprint(ESPEasy_sslclient_context *ssl_client, uint8_t sha256[32])
{
if (!ssl_client) {
log_d("Invalid ssl_client pointer");
return false;
};
const mbedtls_x509_crt* crt = mbedtls_ssl_get_peer_cert(&ssl_client->ssl_ctx);
if (!crt) {
log_d("Failed to get peer cert.");
return false;
};
mbedtls_sha256_context sha256_ctx;
mbedtls_sha256_init(&sha256_ctx);
mbedtls_sha256_starts(&sha256_ctx, false);
mbedtls_sha256_update(&sha256_ctx, crt->raw.p, crt->raw.len);
mbedtls_sha256_finish(&sha256_ctx, sha256);
return true;
}
// Checks if peer certificate has specified domain in CN or SANs
bool verify_ssl_dn(ESPEasy_sslclient_context *ssl_client, const char* domain_name)
{
log_d("domain name: '%s'", (domain_name)?domain_name:"(null)");
std::string domain_name_str(domain_name);
std::transform(domain_name_str.begin(), domain_name_str.end(), domain_name_str.begin(), ::tolower);
// Get certificate provided by the peer
const mbedtls_x509_crt* crt = mbedtls_ssl_get_peer_cert(&ssl_client->ssl_ctx);
// Check for domain name in SANs
const mbedtls_x509_sequence* san = &crt->subject_alt_names;
while (san != nullptr)
{
std::string san_str((const char*)san->buf.p, san->buf.len);
std::transform(san_str.begin(), san_str.end(), san_str.begin(), ::tolower);
if (matchName(san_str, domain_name_str))
return true;
log_d("SAN '%s': no match", san_str.c_str());
// Fetch next SAN
san = san->next;
}
// Check for domain name in CN
const mbedtls_asn1_named_data* common_name = &crt->subject;
while (common_name != nullptr)
{
// While iterating through DN objects, check for CN object
if (!MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &common_name->oid))
{
std::string common_name_str((const char*)common_name->val.p, common_name->val.len);
if (matchName(common_name_str, domain_name_str))
return true;
log_d("CN '%s': not match", common_name_str.c_str());
}
// Fetch next DN object
common_name = common_name->next;
}
return false;
}
#endif
#endif
-51
View File
@@ -1,51 +0,0 @@
/* Provide SSL/TLS functions to ESP32 with Arduino IDE
* by Evandro Copercini - 2017 - Apache 2.0 License
*/
#ifdef ESP32
#ifndef ESPEASY_ARD_SSL_H
#define ESPEASY_ARD_SSL_H
#include <mbedtls/platform.h>
#include <mbedtls/net.h>
#include <mbedtls/debug.h>
#include <mbedtls/ssl.h>
#include <mbedtls/entropy.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/error.h>
typedef struct ESPEasy_sslclient_context {
ESPEasy_sslclient_context();
~ESPEasy_sslclient_context();
void free_ca_cert();
void free_client_cert();
void free_client_key();
int socket = -1;
mbedtls_ssl_context ssl_ctx;
mbedtls_ssl_config ssl_conf;
mbedtls_ctr_drbg_context drbg_ctx;
mbedtls_entropy_context entropy_ctx;
mbedtls_x509_crt ca_cert;
mbedtls_x509_crt client_cert;
mbedtls_pk_context client_key;
unsigned long handshake_timeout = 120000;
} ESPEasy_sslclient_context;
void ssl_init(ESPEasy_sslclient_context *ssl_client);
int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, uint32_t port, int timeout, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey, bool insecure, const char **alpn_protos);
void stop_ssl_socket(ESPEasy_sslclient_context *ssl_client, const char *rootCABuff, const char *cli_cert, const char *cli_key);
int data_to_read(ESPEasy_sslclient_context *ssl_client);
int send_ssl_data(ESPEasy_sslclient_context *ssl_client, const uint8_t *data, size_t len);
int get_ssl_receive(ESPEasy_sslclient_context *ssl_client, uint8_t *data, int length);
bool verify_ssl_fingerprint(ESPEasy_sslclient_context *ssl_client, const char* fp, const char* domain_name);
bool verify_ssl_dn(ESPEasy_sslclient_context *ssl_client, const char* domain_name);
bool get_peer_fingerprint(ESPEasy_sslclient_context *ssl_client, uint8_t sha256[32]);
#endif
#endif
+6 -4
View File
@@ -490,6 +490,9 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex
} else {
LoadControllerSettings(controllerindex, *ControllerSettings);
// FIXME TD-er: Implement retrieval of certificate
/*
addFormSubHeader(F("Peer Certificate"));
{
@@ -522,10 +525,8 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex
int error {0};
while (chain != nullptr && error == 0) {
/*
const bool mustShow = !chain->ca_istrue || chain->next == nullptr;
if (mustShow) {
*/
// const bool mustShow = !chain->ca_istrue || chain->next == nullptr;
// if (mustShow) {
String pem, subject;
error = ESPEasy_WiFiClientSecure::cert_to_pem(chain, pem, subject);
{
@@ -567,6 +568,7 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex
chain = chain->next;
}
}
*/
}
}
#endif