mirror of https://github.com/arendst/Tasmota.git
Merge pull request #13887 from s-hadinger/tls_dual_mode
MQTT TLS dual mode (CA or fingeprint) in same firmware, ``SetOption132 1`` to force fingerprint
This commit is contained in:
commit
40087595ae
|
@ -14,7 +14,6 @@ Note: `minimal` variant is not listed as it shouldn't be used outside of the [up
|
|||
| USE_DOMOTICZ | - | x / x | x | x | x | - |
|
||||
| USE_HOME_ASSISTANT | - | x / x | x | x | x | - |
|
||||
| USE_MQTT_TLS | - | - / - | - | - | - | - |
|
||||
| USE_MQTT_TLS_CA_CERT | - | - / - | - | - | - | - |
|
||||
| USE_MQTT_AWS_IOT | - | - / - | - | - | - | - |
|
||||
| USE_4K_RSA | - | - / - | - | - | - | - |
|
||||
| USE_TELEGRAM | - | - / - | - | - | - | - |
|
||||
|
|
|
@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file.
|
|||
### Changed
|
||||
- (Internal) Range conversion edge values
|
||||
- NimBLE to v.1.3.3
|
||||
- MQTT TLS dual mode (CA or fingeprint) in same firmware, ``SetOption132 1`` to force fingerprint
|
||||
|
||||
### Fixed
|
||||
- Tuya dimmer range issue (#13849)
|
||||
|
|
|
@ -43,10 +43,8 @@ uint32_t stack_thunk_light_refcnt = 0;
|
|||
//#define _stackSize (5600/4)
|
||||
#if defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_AWS_IOT_LIGHT) || defined(USE_MQTT_AZURE_IOT)
|
||||
#define _stackSize (5300/4) // using a light version of bearssl we can save 300 bytes
|
||||
#elif defined(USE_MQTT_TLS_FORCE_EC_CIPHER) || defined(USE_4K_RSA)
|
||||
#define _stackSize (4800/4) // no private key, we can reduce a little, max observed 4300
|
||||
#else
|
||||
#define _stackSize (3800/4) // using a light version of bearssl we can save 2k
|
||||
#define _stackSize (4800/4) // no private key, we can reduce a little, max observed 4300
|
||||
#endif
|
||||
#define _stackPaint 0xdeadbeef
|
||||
|
||||
|
|
|
@ -191,11 +191,7 @@ void WiFiClientSecure_light::_clear() {
|
|||
_last_error = 0;
|
||||
_recvapp_buf = nullptr;
|
||||
_recvapp_len = 0;
|
||||
#ifdef USE_MQTT_TLS_CA_CERT
|
||||
_insecure = false; // insecure (fingerprint) mode is only enabled if setPubKeyFingerprint() is called
|
||||
#else
|
||||
_insecure = true; // force insecure if CA validation is not enabled
|
||||
#endif
|
||||
_insecure = false; // set to true when calling setPubKeyFingerprint()
|
||||
_fingerprint_any = true; // by default accept all fingerprints
|
||||
_fingerprint1 = nullptr;
|
||||
_fingerprint2 = nullptr;
|
||||
|
@ -920,11 +916,7 @@ extern "C" {
|
|||
// We limit to a single cipher to reduce footprint
|
||||
// we reference it, don't put in PROGMEM
|
||||
static const uint16_t suites[] = {
|
||||
#ifdef USE_MQTT_TLS_FORCE_EC_CIPHER
|
||||
BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|
||||
#else
|
||||
BR_TLS_RSA_WITH_AES_128_GCM_SHA256
|
||||
#endif
|
||||
};
|
||||
|
||||
// Default initializion for our SSL clients
|
||||
|
@ -947,10 +939,8 @@ extern "C" {
|
|||
br_ssl_engine_set_aes_ctr(&cc->eng, &br_aes_small_ctr_vtable);
|
||||
br_ssl_engine_set_ghash(&cc->eng, &br_ghash_ctmul32);
|
||||
|
||||
#ifdef USE_MQTT_TLS_FORCE_EC_CIPHER
|
||||
// we support only P256 EC curve for AWS IoT, no EC curve for Letsencrypt unless forced
|
||||
br_ssl_engine_set_ec(&cc->eng, &br_ec_p256_m15); // TODO
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -958,9 +948,8 @@ extern "C" {
|
|||
// Returns if the SSL handshake succeeded.
|
||||
bool WiFiClientSecure_light::_connectSSL(const char* hostName) {
|
||||
// Validation context, either full CA validation or checking only fingerprints
|
||||
#ifdef USE_MQTT_TLS_CA_CERT
|
||||
|
||||
br_x509_minimal_context *x509_minimal = nullptr;
|
||||
#endif
|
||||
br_x509_pubkeyfingerprint_context *x509_insecure = nullptr;
|
||||
|
||||
LOG_HEAP_SIZE("_connectSSL.start");
|
||||
|
@ -998,7 +987,6 @@ bool WiFiClientSecure_light::_connectSSL(const char* hostName) {
|
|||
br_x509_pubkeyfingerprint_init(x509_insecure, _fingerprint1, _fingerprint2, _recv_fingerprint, _fingerprint_any);
|
||||
br_ssl_engine_set_x509(_eng, &x509_insecure->vtable);
|
||||
|
||||
#ifdef USE_MQTT_TLS_CA_CERT
|
||||
if (!_insecure) {
|
||||
x509_minimal = (br_x509_minimal_context*) malloc(sizeof(br_x509_minimal_context));
|
||||
if (!x509_minimal) break;
|
||||
|
@ -1011,7 +999,6 @@ bool WiFiClientSecure_light::_connectSSL(const char* hostName) {
|
|||
if (cfg_time > now) { now = cfg_time; }
|
||||
br_x509_minimal_set_time(x509_minimal, now / 86400 + 719528, now % 86400);
|
||||
}
|
||||
#endif
|
||||
LOG_HEAP_SIZE("_connectSSL after DecoderContext allocation");
|
||||
|
||||
// ============================================================
|
||||
|
@ -1050,9 +1037,7 @@ bool WiFiClientSecure_light::_connectSSL(const char* hostName) {
|
|||
LOG_HEAP_SIZE("_connectSSL.end, freeing StackThunk");
|
||||
#endif // ESP8266
|
||||
|
||||
#ifdef USE_MQTT_TLS_CA_CERT
|
||||
free(x509_minimal);
|
||||
#endif
|
||||
free(x509_minimal); // safe to call if nullptr
|
||||
free(x509_insecure);
|
||||
LOG_HEAP_SIZE("_connectSSL after release of Priv Key");
|
||||
return ret;
|
||||
|
@ -1065,9 +1050,7 @@ bool WiFiClientSecure_light::_connectSSL(const char* hostName) {
|
|||
#ifdef ESP8266
|
||||
stack_thunk_light_del_ref();
|
||||
#endif
|
||||
#ifdef USE_MQTT_TLS_CA_CERT
|
||||
free(x509_minimal);
|
||||
#endif
|
||||
free(x509_minimal); // safe to call if nullptr
|
||||
free(x509_insecure);
|
||||
LOG_HEAP_SIZE("_connectSSL clean_on_error");
|
||||
return false;
|
||||
|
|
|
@ -371,6 +371,7 @@
|
|||
// Commands xdrv_02_mqtt.ino
|
||||
#define D_SO_MQTTJSONONLY "MqttJSONOnly"
|
||||
#define D_SO_MQTTTLS "MqttTLS"
|
||||
#define D_SO_MQTTTLS_FINGERPRINT "MqttTLSFingerprint"
|
||||
#define D_SO_MQTTNORETAIN "MqttNoRetain"
|
||||
#define D_SO_MQTTDETACHRELAY "MqttDetachRelay"
|
||||
#define D_CMND_MQTTLOG "MqttLog"
|
||||
|
|
|
@ -170,6 +170,7 @@
|
|||
#define MQTT_INDEX_SEPARATOR false // [SetOption64] Enable "_" instead of "-" as sensor index separator
|
||||
#define MQTT_TUYA_RECEIVED false // [SetOption66] Enable TuyaMcuReceived messages over Mqtt
|
||||
#define MQTT_TLS_ENABLED false // [SetOption103] Enable TLS mode (requires TLS version)
|
||||
#define MQTT_TLS_FINGERPRINT false // [SetOption132] Force TLS fingerprint validation instead of CA (requires TLS version)
|
||||
|
||||
// -- HTTP ----------------------------------------
|
||||
#define WEB_SERVER 2 // [WebServer] Web server (0 = Off, 1 = Start as User, 2 = Start as Admin)
|
||||
|
@ -430,9 +431,7 @@
|
|||
// -- MQTT - TLS - AWS IoT ------------------------
|
||||
// Using TLS starting with version v6.5.0.16 compilation will only work using Core 2.4.2 and 2.5.2. No longer supported: 2.3.0
|
||||
//#define USE_MQTT_TLS // Use TLS for MQTT connection (+34.5k code, +7.0k mem and +4.8k additional during connection handshake)
|
||||
// #define USE_MQTT_TLS_CA_CERT // Force full CA validation instead of fingerprints, slower, but simpler to use. (+2.2k code, +1.9k mem during connection handshake)
|
||||
// This includes the LetsEncrypt CA in tasmota_ca.ino for verifying server certificates
|
||||
// #define USE_MQTT_TLS_FORCE_EC_CIPHER // Force Elliptic Curve cipher (higher security) required by some servers (automatically enabled with USE_MQTT_AWS_IOT) (+11.4k code, +0.4k mem)
|
||||
// #define USE_MQTT_TLS_CA_CERT // [DEPRECATED] Now TLS supports dual mode using SetOption132 - this flag is now ignored
|
||||
// #define USE_MQTT_AWS_IOT_LIGHT // Enable MQTT for AWS IoT in light mode, with user/password instead of private certificate
|
||||
// #define USE_MQTT_AWS_IOT // [Deprecated] Enable MQTT for AWS IoT - requires a private key (+11.9k code, +0.4k mem)
|
||||
// Note: you need to generate a private key + certificate per device and update 'tasmota/tasmota_aws_iot.cpp'
|
||||
|
@ -453,7 +452,6 @@
|
|||
// -- Telegram Protocol ---------------------------
|
||||
//#define USE_TELEGRAM // Support for Telegram protocol (+49k code, +7.0k mem and +4.8k additional during connection handshake)
|
||||
#define USE_TELEGRAM_FINGERPRINT "\xB2\x72\x47\xA6\x69\x8C\x3C\x69\xF9\x58\x6C\xF3\x60\x02\xFB\x83\xFA\x8B\x1F\x23" // Telegram api.telegram.org TLS public key fingerpring
|
||||
// #define USE_MQTT_TLS_CA_CERT // Use certificate instead of fingerprint
|
||||
|
||||
// -- KNX IP Protocol -----------------------------
|
||||
//#define USE_KNX // Enable KNX IP Protocol Support (+9.4k code, +3k7 mem)
|
||||
|
@ -1117,10 +1115,6 @@
|
|||
|
||||
#if defined(USE_MQTT_TLS) || defined(USE_SENDMAIL) || defined(USE_TELEGRAM) || defined(USE_WEBCLIENT_HTTPS) || defined(USE_ALEXA_AVS)
|
||||
#define USE_TLS // flag indicates we need to include TLS code
|
||||
|
||||
#if defined(USE_MQTT_AWS_IOT) || defined(USE_TELEGRAM) || defined(USE_WEBCLIENT_HTTPS)
|
||||
#define USE_MQTT_TLS_FORCE_EC_CIPHER // AWS IoT and TELEGRAM require EC Cipher
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // _MY_USER_CONFIG_H_
|
||||
|
|
|
@ -161,7 +161,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu
|
|||
uint32_t energy_phase : 1; // bit 15 (v9.5.0.9) - SetOption129 - (Energy) Show phase information
|
||||
uint32_t show_heap_with_timestamp : 1; // bit 16 (v9.5.0.9) - SetOption130 - (Debug) Show heap with logging timestamp
|
||||
uint32_t tuya_allow_dimmer_0 : 1; // bit 17 (v10.0.0.3) - SetOption131 - (Tuya) Allow save dimmer = 0 receved by MCU
|
||||
uint32_t spare18 : 1; // bit 18
|
||||
uint32_t tls_use_fingerprint : 1; // bit 18 (v10.0.0.4) - SetOption132 - (TLS) use fingerprint validation instead of CA based
|
||||
uint32_t spare19 : 1; // bit 19
|
||||
uint32_t spare20 : 1; // bit 20
|
||||
uint32_t spare21 : 1; // bit 21
|
||||
|
|
|
@ -1458,6 +1458,14 @@ void SettingsDelta(void) {
|
|||
if (Settings->version < 0x0A000003) {
|
||||
if (0 == Settings->param[P_ARP_GRATUITOUS]) {
|
||||
Settings->param[P_ARP_GRATUITOUS] = WIFI_ARP_INTERVAL;
|
||||
#ifdef USE_TLS
|
||||
for (uint32_t i = 0; i < 20; i++) {
|
||||
if (Settings->mqtt_fingerprint[0][i]) {
|
||||
Settings->flag5.tls_use_fingerprint = true; // if the fingerprint1 is non null we expect it to be actually used
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1068,6 +1068,9 @@ void CmndSetoptionBase(bool indexed) {
|
|||
TasmotaGlobal.restart_flag = 2;
|
||||
}
|
||||
break;
|
||||
case 18: // SetOption132 - TLS Fingerprint
|
||||
TasmotaGlobal.restart_flag = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
// Please use fingerprint validation instead
|
||||
// However, the CA are available below for future use if it appears to be useful
|
||||
|
||||
#if defined(USE_TLS) && defined(USE_MQTT_TLS_CA_CERT)
|
||||
#if defined(USE_TLS)
|
||||
|
||||
/*********************************************************************************************\
|
||||
* LetsEncrypt R3 certificate, RSA 2048 bits SHA 256, valid until 20250915
|
||||
|
@ -232,4 +232,4 @@ const br_x509_trust_anchor GoDaddyCAG2_TA PROGMEM = {
|
|||
}
|
||||
};
|
||||
|
||||
#endif // defined(USE_TLS) && defined(USE_MQTT_TLS_CA_CERT)
|
||||
#endif // defined(USE_TLS)
|
||||
|
|
|
@ -497,9 +497,6 @@
|
|||
// -- MQTT - TLS - AWS IoT ------------------------
|
||||
#ifdef USE_ZBBRIDGE_TLS // Enable TLS for ZbBridge
|
||||
#define USE_MQTT_TLS // Use TLS for MQTT connection (+34.5k code, +7.0k mem and +4.8k additional during connection handshake)
|
||||
#define USE_MQTT_TLS_CA_CERT // Force full CA validation instead of fingerprints, slower, but simpler to use. (+2.2k code, +1.9k mem during connection handshake)
|
||||
// This includes the LetsEncrypt CA in tasmota_ca.ino for verifying server certificates
|
||||
#define USE_MQTT_TLS_FORCE_EC_CIPHER // Force Elliptic Curve cipher (higher security) required by some servers (automatically enabled with USE_MQTT_AWS_IOT) (+11.4k code, +0.4k mem)
|
||||
#define USE_MQTT_AWS_IOT_LIGHT // Enable MQTT for AWS IoT in light mode, with user/password instead of private certificate
|
||||
#define USE_TLS // flag indicates we need to include TLS code
|
||||
#endif // USE_ZBBRIDGE_TLS
|
||||
|
|
|
@ -48,11 +48,11 @@ const char kMqttCommands[] PROGMEM = "|" // No prefix
|
|||
// SetOption synonyms
|
||||
D_SO_MQTTJSONONLY "|"
|
||||
#ifdef USE_MQTT_TLS
|
||||
D_SO_MQTTTLS "|"
|
||||
D_SO_MQTTTLS "|" D_SO_MQTTTLS_FINGERPRINT "|"
|
||||
#endif
|
||||
D_SO_MQTTNORETAIN "|" D_SO_MQTTDETACHRELAY "|"
|
||||
// regular commands
|
||||
#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT)
|
||||
#if defined(USE_MQTT_TLS)
|
||||
D_CMND_MQTTFINGERPRINT "|"
|
||||
#endif
|
||||
D_CMND_MQTTUSER "|" D_CMND_MQTTPASSWORD "|" D_CMND_MQTTKEEPALIVE "|" D_CMND_MQTTTIMEOUT "|" D_CMND_MQTTWIFITIMEOUT "|"
|
||||
|
@ -70,18 +70,13 @@ const char kMqttCommands[] PROGMEM = "|" // No prefix
|
|||
SO_SYNONYMS(kMqttSynonyms,
|
||||
90,
|
||||
#ifdef USE_MQTT_TLS
|
||||
103,
|
||||
103, 132,
|
||||
#endif
|
||||
104, 114
|
||||
);
|
||||
|
||||
// const uint8_t kMqttSynonyms[] PROGMEM = {
|
||||
// 4, // number of synonyms
|
||||
// 90, 103, 104, 114,
|
||||
// };
|
||||
|
||||
void (* const MqttCommand[])(void) PROGMEM = {
|
||||
#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT)
|
||||
#if defined(USE_MQTT_TLS)
|
||||
&CmndMqttFingerprint,
|
||||
#endif
|
||||
&CmndMqttUser, &CmndMqttPassword, &CmndMqttKeepAlive, &CmndMqttTimeout, &CmndMqttWifiTimeout,
|
||||
|
@ -231,9 +226,9 @@ void MqttInit(void) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_MQTT_TLS_CA_CERT
|
||||
if (!Settings->flag5.tls_use_fingerprint) {
|
||||
tlsClient->setTrustAnchor(Tasmota_TA, nitems(Tasmota_TA));
|
||||
#endif // USE_MQTT_TLS_CA_CERT
|
||||
}
|
||||
|
||||
MqttClient.setClient(*tlsClient);
|
||||
} else {
|
||||
|
@ -953,7 +948,7 @@ void MqttConnected(void) {
|
|||
void MqttReconnect(void) {
|
||||
char stopic[TOPSZ];
|
||||
|
||||
Mqtt.allowed = Settings->flag.mqtt_enabled; // SetOption3 - Enable MQTT
|
||||
Mqtt.allowed = Settings->flag.mqtt_enabled && (TasmotaGlobal.restart_flag == 0); // SetOption3 - Enable MQTT, and don't connect if restart in process
|
||||
if (Mqtt.allowed) {
|
||||
#if defined(USE_MQTT_AZURE_DPS_SCOPEID) && defined(USE_MQTT_AZURE_DPS_PRESHAREDKEY)
|
||||
ProvisionAzureDPS();
|
||||
|
@ -1043,11 +1038,11 @@ void MqttReconnect(void) {
|
|||
MqttClient.setServer(SettingsText(SET_MQTT_HOST), Settings->mqtt_port);
|
||||
|
||||
uint32_t mqtt_connect_time = millis();
|
||||
#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT)
|
||||
bool allow_all_fingerprints;
|
||||
bool learn_fingerprint1;
|
||||
bool learn_fingerprint2;
|
||||
if (Mqtt.mqtt_tls) {
|
||||
#if defined(USE_MQTT_TLS)
|
||||
bool allow_all_fingerprints = false;
|
||||
bool learn_fingerprint1 = false;
|
||||
bool learn_fingerprint2 = false;
|
||||
if (Mqtt.mqtt_tls && Settings->flag5.tls_use_fingerprint) {
|
||||
allow_all_fingerprints = false;
|
||||
learn_fingerprint1 = is_fingerprint_mono_value(Settings->mqtt_fingerprint[0], 0x00);
|
||||
learn_fingerprint2 = is_fingerprint_mono_value(Settings->mqtt_fingerprint[1], 0x00);
|
||||
|
@ -1094,7 +1089,8 @@ void MqttReconnect(void) {
|
|||
if (!tlsClient->getMFLNStatus()) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "MFLN not supported by TLS server"));
|
||||
}
|
||||
#ifndef USE_MQTT_TLS_CA_CERT // don't bother with fingerprints if using CA validation
|
||||
|
||||
if (Settings->flag5.tls_use_fingerprint) { // CA validation
|
||||
const uint8_t *recv_fingerprint = tlsClient->getRecvPubKeyFingerprint();
|
||||
// create a printable version of the fingerprint received
|
||||
char buf_fingerprint[64];
|
||||
|
@ -1122,7 +1118,8 @@ void MqttReconnect(void) {
|
|||
|
||||
SettingsSaveAll(); // save settings
|
||||
}
|
||||
#endif // !USE_MQTT_TLS_CA_CERT
|
||||
}
|
||||
|
||||
}
|
||||
#endif // USE_MQTT_TLS
|
||||
MqttConnected();
|
||||
|
@ -1275,7 +1272,7 @@ bool KeyTopicActive(uint32_t key) {
|
|||
* Commands
|
||||
\*********************************************************************************************/
|
||||
|
||||
#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT)
|
||||
#if defined(USE_MQTT_TLS)
|
||||
void CmndMqttFingerprint(void) {
|
||||
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) {
|
||||
char fingerprint[60];
|
||||
|
|
|
@ -47,13 +47,8 @@
|
|||
#define TELEGRAM_SEND_RETRY 4 // Retries
|
||||
#define TELEGRAM_MAX_MESSAGES 2
|
||||
|
||||
#ifdef USE_MQTT_TLS_CA_CERT
|
||||
static const uint32_t tls_rx_size = 2048; // since Telegram CA is bigger than 1024 bytes, we need to increase rx buffer
|
||||
static const uint32_t tls_tx_size = 1024;
|
||||
#else
|
||||
static const uint32_t tls_rx_size = 1024;
|
||||
static const uint32_t tls_tx_size = 1024;
|
||||
#endif
|
||||
static const uint32_t tls_rx_size = 2048; // since Telegram CA is bigger than 1024 bytes, we need to increase rx buffer
|
||||
static const uint32_t tls_tx_size = 1024;
|
||||
|
||||
#include "WiFiClientSecureLightBearSSL.h"
|
||||
BearSSL::WiFiClientSecure_light *telegramClient = nullptr;
|
||||
|
@ -87,11 +82,13 @@ bool TelegramInit(void) {
|
|||
if (strlen(SettingsText(SET_TELEGRAM_TOKEN))) {
|
||||
if (!telegramClient) {
|
||||
telegramClient = new BearSSL::WiFiClientSecure_light(tls_rx_size, tls_tx_size);
|
||||
#ifdef USE_MQTT_TLS_CA_CERT
|
||||
telegramClient->setTrustAnchor(&GoDaddyCAG2_TA, 1);
|
||||
#else
|
||||
|
||||
if (Settings.flag5.tls_use_fingerprint) {
|
||||
telegramClient->setPubKeyFingerprint(Telegram_Fingerprint, Telegram_Fingerprint, false); // check server fingerprint
|
||||
#endif
|
||||
} else {
|
||||
telegramClient->setTrustAnchor(&GoDaddyCAG2_TA, 1);
|
||||
}
|
||||
|
||||
Telegram.message_count = 0; // Number of received messages
|
||||
Telegram.next_update_id = 0; // Code of last read Message
|
||||
Telegram.message[0].text = "";
|
||||
|
|
Loading…
Reference in New Issue