mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-09-11 17:14:27 +00:00
[MQTT-TLS] Switch to Tasmota's Light-Weight BearSSL lib
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
#include "../Globals/ESPEasy_time.h"
|
||||
|
||||
ESPEasy_time node_time;
|
||||
ESPEasy_time node_time;
|
||||
|
||||
uint32_t getUnixTime() {
|
||||
return node_time.getUnixTime();
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
extern ESPEasy_time node_time;
|
||||
|
||||
// Global function needed for SSL
|
||||
uint32_t getUnixTime();
|
||||
|
||||
|
||||
#endif // GLOBALS_ESPEASY_TIME_H
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user