Browse Source

MQTT rewrite with SSL fixes (#1751, #1829)

* MQTT rewrite with SSL fixes

- Added Arduino MQTT library support (actively maintained)
- Added support for BearSSL (core >= 2.5)
- BearSSL validation: insecure, fingerprinting and CA validation
- AxTLS validation: insecure and fingerprinting
- Support MFLN in order to reduce heap usage

* Better header incl, fix building w/ no NTP_SUPPORT

* Clean up code, use DEBUG_MSG_P

* Fix compile error
master
Niek van der Maas 4 years ago
committed by Max Prokhorov
parent
commit
b48f8c1440
10 changed files with 336 additions and 156 deletions
  1. +7
    -2
      code/espurna/config/dependencies.h
  2. +31
    -11
      code/espurna/config/general.h
  3. +7
    -0
      code/espurna/config/prototypes.h
  4. +6
    -0
      code/espurna/config/types.h
  5. +183
    -90
      code/espurna/mqtt.ino
  6. +94
    -0
      code/espurna/static/letsencrypt_isrgroot_pem.h
  7. +0
    -46
      code/espurna/static/letsencrypt_isrgrootx1_pem.h
  8. +1
    -1
      code/espurna/thinkspeak.ino
  9. +6
    -6
      code/espurna/web.ino
  10. +1
    -0
      code/platformio.ini

+ 7
- 2
code/espurna/config/dependencies.h View File

@ -5,6 +5,11 @@
// Configuration settings are in the general.h file
//------------------------------------------------------------------------------
#if defined(ASYNC_TCP_SSL_ENABLED) && SECURE_CLIENT == SECURE_CLIENT_NONE
#undef SECURE_CLIENT
#define SECURE_CLIENT SECURE_CLIENT_AXTLS
#endif
#if DEBUG_TELNET_SUPPORT
#undef TELNET_SUPPORT
#define TELNET_SUPPORT 1
@ -55,10 +60,10 @@
#define MQTT_SUPPORT 1 // If Home Assistant enabled enable MQTT
#endif
#ifndef ASYNC_TCP_SSL_ENABLED
#if SECURE_CLIENT != SECURE_CLIENT_AXTLS
#if THINGSPEAK_USE_SSL && THINGSPEAK_USE_ASYNC
#undef THINGSPEAK_SUPPORT
#define THINGSPEAK_SUPPORT 0 // Thingspeak in ASYNC mode requires ASYNC_TCP_SSL_ENABLED
#define THINGSPEAK_SUPPORT 0 // Thingspeak in ASYNC mode requires SECURE_CLIENT_AXTLS
#endif
#endif


+ 31
- 11
code/espurna/config/general.h View File

@ -567,7 +567,7 @@
#endif
// This is not working at the moment!!
// Requires ASYNC_TCP_SSL_ENABLED to 1 and ESP8266 Arduino Core 2.4.0
// Requires SECURE_CLIENT = SECURE_CLIENT_AXTLS and ESP8266 Arduino Core 2.4.0
#ifndef WEB_SSL_ENABLED
#define WEB_SSL_ENABLED 0 // Use HTTPS web interface
#endif
@ -839,26 +839,28 @@
#endif
#ifndef MQTT_USE_ASYNC
#define MQTT_USE_ASYNC 1 // Use AysncMQTTClient (1) or PubSubClient (0)
#ifndef MQTT_LIBRARY
#define MQTT_LIBRARY MQTT_ASYNC // Choose between: MQTT_ASYNC (AysncMQTTClient), MQTT_PUBSUB (PubSubClient), MQTT_ARDUINO (Arduino-MQTT)
#endif
// MQTT OVER SSL
// Using MQTT over SSL works pretty well but generates problems with the web interface.
// It could be a good idea to use it in conjuntion with WEB_SUPPORT=0.
// Requires ASYNC_TCP_SSL_ENABLED to 1 and ESP8266 Arduino Core 2.4.0.
// Requires SECURE_CLIENT = SECURE_CLIENT_AXTLS or SECURE_CLIENT_BEARSSL and ESP8266 Arduino Core >= 2.4.0.
//
// You can use SSL with MQTT_USE_ASYNC=1 (AsyncMqttClient library)
// You can use SSL with MQTT_LIBRARY=ASYNC (AsyncMqttClient library)
// but you might experience hiccups on the web interface, so my recommendation is:
// WEB_SUPPORT=0
//
// If you use SSL with MQTT_USE_ASYNC=0 (PubSubClient library)
// If you use SSL with MQTT_LIBRARY=PUBSUB (PubSubClient library) or MQTT_LIBRARY=ARDUINO (Arduino-MQTT library)
// you will have to disable all the modules that use ESPAsyncTCP, that is:
// ALEXA_SUPPORT=0, INFLUXDB_SUPPORT=0, TELNET_SUPPORT=0, THINGSPEAK_SUPPORT=0 and WEB_SUPPORT=0
// ALEXA_SUPPORT=0, INFLUXDB_SUPPORT=0, TELNET_SUPPORT=0, THINGSPEAK_SUPPORT=0, DEBUG_TELNET_SUPPORT=0 and WEB_SUPPORT=0
//
// You will need the fingerprint for your MQTT server, example for CloudMQTT:
// $ echo -n | openssl s_client -connect m11.cloudmqtt.com:24055 > cloudmqtt.pem
// $ openssl x509 -noout -in cloudmqtt.pem -fingerprint -sha1
// You will need the fingerprint of your MQTT server, in order to prevent MITS attacks.
// To get a certificate fingerprint, run the following command:
// $ echo -n | openssl s_client -connect mqtt.googleapis.com:8883 2>&1 | openssl x509 -noout -fingerprint -sha1 | cut -d\= -f2
// Note that this fingerprint changes with e.g. LetsEncrypt renewals or when the CSR changes.
// It's also possible to leave the fingerprint empty, the certificate is then always trusted.
#ifndef MQTT_SSL_ENABLED
#define MQTT_SSL_ENABLED 0 // By default MQTT over SSL will not be enabled
@ -868,6 +870,20 @@
#define MQTT_SSL_FINGERPRINT "" // SSL fingerprint of the server
#endif
#ifndef MQTT_SECURE_CLIENT_CHECK
#define MQTT_SECURE_CLIENT_CHECK SECURE_CLIENT_CHECK // Use global verification setting by default
#endif
#ifndef MQTT_SECURE_CLIENT_MFLN
#define MQTT_SECURE_CLIENT_MFLN SECURE_CLIENT_MFLN // Use global MFLN setting by default
#endif
#ifndef MQTT_SECURE_CLIENT_INCLUDE_CA
#define MQTT_SECURE_CLIENT_INCLUDE_CA 0 // Use user-provided CA. Only PROGMEM PEM option is supported.
// When enabled, current implementation includes "static/mqtt_secure_client_ca.h" with
// const char _mqtt_client_ca[] PROGMEM = "...PEM data...";
// By default, using LetsEncrypt X3 root in "static/letsencrypt_isrgroot_pem.h"
#endif
#ifndef MQTT_ENABLED
#define MQTT_ENABLED 0 // Do not enable MQTT connection by default
@ -986,7 +1002,9 @@
#define MQTT_TOPIC_DATETIME "datetime"
#define MQTT_TOPIC_FREEHEAP "freeheap"
#define MQTT_TOPIC_VCC "vcc"
#ifndef MQTT_TOPIC_STATUS
#define MQTT_TOPIC_STATUS "status"
#endif
#define MQTT_TOPIC_MAC "mac"
#define MQTT_TOPIC_RSSI "rssi"
#define MQTT_TOPIC_MESSAGE_ID "id"
@ -1031,7 +1049,9 @@
#define MQTT_STATUS_ONLINE "1" // Value for the device ON message
#ifndef MQTT_STATUS_OFFLINE
#define MQTT_STATUS_OFFLINE "0" // Value for the device OFF message (will)
#endif
#define MQTT_ACTION_RESET "reboot" // RESET MQTT topic particle
@ -1283,7 +1303,7 @@
// THINGSPEAK OVER SSL
// Using THINGSPEAK over SSL works well but generates problems with the web interface,
// so you should compile it with WEB_SUPPORT to 0.
// When THINGSPEAK_USE_ASYNC is 1, requires ASYNC_TCP_SSL_ENABLED to 1 and ESP8266 Arduino Core 2.4.0.
// When THINGSPEAK_USE_ASYNC is 1, requires SECURE_CLIENT = SECURE_CLIENT_AXTLS and ESP8266 Arduino Core >= 2.4.0.
#define THINGSPEAK_USE_SSL 0 // Use secure connection
#define THINGSPEAK_FINGERPRINT "78 60 18 44 81 35 BF DF 77 84 D4 0A 22 0D 9B 4E 6C DC 57 2C"


+ 7
- 0
code/espurna/config/prototypes.h View File

@ -172,6 +172,13 @@ using mqtt_callback_f = std::function<void(unsigned int, const char *, char *)>;
String mqttMagnitude(char * topic);
#endif
#if MQTT_SECURE_CLIENT_INCLUDE_CA
#include "static/mqtt_secure_client_ca.h" // Assumes this header file defines a _mqtt_client_ca[] PROGMEM = "...PEM data..."
#else
#include "static/letsencrypt_isrgroot_pem.h" // Default to LetsEncrypt X3 certificate
#define _mqtt_client_ca _ssl_letsencrypt_isrg_x3_ca
#endif // MQTT_SECURE_CLIENT_INCLUDE_CA
// -----------------------------------------------------------------------------
// OTA
// -----------------------------------------------------------------------------


+ 6
- 0
code/espurna/config/types.h View File

@ -152,6 +152,11 @@
#define MQTT_DISCONNECT_EVENT 1
#define MQTT_MESSAGE_EVENT 2
#define MQTT_ASYNC 0
#define MQTT_ARDUINO 1
#define MQTT_PUBSUB 2
//------------------------------------------------------------------------------
// LED
//------------------------------------------------------------------------------
@ -353,6 +358,7 @@
//------------------------------------------------------------------------------
// Telnet server
//------------------------------------------------------------------------------
#define TELNET_SERVER_ASYNC 0
#define TELNET_SERVER_WIFISERVER 1


+ 183
- 90
code/espurna/mqtt.ino View File

@ -15,24 +15,38 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#include <vector>
#include <utility>
#include <Ticker.h>
#include <TimeLib.h>
#if MQTT_LIBRARY == MQTT_ASYNC // AsyncMqttClient
#include <AsyncMqttClient.h>
AsyncMqttClient _mqtt;
#else // Arduino-MQTT or PubSubClient
WiFiClient _mqtt_client;
bool _mqtt_connected = false;
#include "WiFiClientSecure.h"
#if SECURE_CLIENT == SECURE_CLIENT_AXTLS
using namespace axTLS;
#elif SECURE_CLIENT == SECURE_CLIENT_BEARSSL
using namespace BearSSL;
BearSSL::X509List *_ca_list = nullptr;
#endif
#if MQTT_USE_ASYNC // Using AsyncMqttClient
#include <AsyncMqttClient.h>
AsyncMqttClient _mqtt;
#else // Using PubSubClient
#include <PubSubClient.h>
PubSubClient _mqtt;
bool _mqtt_connected = false;
WiFiClient _mqtt_client;
#if ASYNC_TCP_SSL_ENABLED
WiFiClientSecure _mqtt_client_secure;
#endif // ASYNC_TCP_SSL_ENABLED
WiFiClientSecure _mqtt_client_secure;
#endif // MQTT_USE_ASYNC
#if MQTT_LIBRARY == MQTT_ARDUINO // Using Arduino-MQTT
#include <MQTTClient.h>
#ifdef MQTT_MAX_PACKET_SIZE
MQTTClient _mqtt(MQTT_MAX_PACKET_SIZE);
#else
MQTTClient _mqtt();
#endif
#else // Using PubSubClient
#include <PubSubClient.h>
PubSubClient _mqtt;
#endif
#endif
bool _mqtt_enabled = MQTT_ENABLED;
bool _mqtt_use_json = false;
@ -92,109 +106,176 @@ void _mqttConnect() {
DEBUG_MSG_P(PSTR("[MQTT] Connecting to broker at %s:%u\n"), _mqtt_server.c_str(), _mqtt_port);
#if MQTT_USE_ASYNC
_mqtt_connecting = true;
DEBUG_MSG_P(PSTR("[MQTT] Client ID: %s\n"), _mqtt_clientid.c_str());
DEBUG_MSG_P(PSTR("[MQTT] QoS: %d\n"), _mqtt_qos);
DEBUG_MSG_P(PSTR("[MQTT] Retain flag: %d\n"), _mqtt_retain ? 1 : 0);
DEBUG_MSG_P(PSTR("[MQTT] Keepalive time: %ds\n"), _mqtt_keepalive);
DEBUG_MSG_P(PSTR("[MQTT] Will topic: %s\n"), _mqtt_will.c_str());
_mqtt_connecting = true;
#if MQTT_LIBRARY == MQTT_ASYNC
_mqtt.setServer(_mqtt_server.c_str(), _mqtt_port);
_mqtt.setClientId(_mqtt_clientid.c_str());
_mqtt.setKeepAlive(_mqtt_keepalive);
_mqtt.setCleanSession(false);
_mqtt.setWill(_mqtt_will.c_str(), _mqtt_qos, _mqtt_retain, "0");
_mqtt.setWill(_mqtt_will.c_str(), _mqtt_qos, _mqtt_retain, MQTT_STATUS_OFFLINE);
if (_mqtt_user.length() && _mqtt_pass.length()) {
DEBUG_MSG_P(PSTR("[MQTT] Connecting as user %s\n"), _mqtt_user.c_str());
_mqtt.setCredentials(_mqtt_user.c_str(), _mqtt_pass.c_str());
}
#if ASYNC_TCP_SSL_ENABLED
#if SECURE_CLIENT != SECURE_CLIENT_NONE
bool secure = getSetting("mqttUseSSL", MQTT_SSL_ENABLED).toInt() == 1;
_mqtt.setSecure(secure);
if (secure) {
DEBUG_MSG_P(PSTR("[MQTT] Using SSL\n"));
unsigned char fp[20] = {0};
if (sslFingerPrintArray(getSetting("mqttFP", MQTT_SSL_FINGERPRINT).c_str(), fp)) {
_mqtt.addServerFingerprint(fp);
int check = getSetting("mqttScCheck", MQTT_SECURE_CLIENT_CHECK).toInt();
if (check == SECURE_CLIENT_CHECK_FINGERPRINT) {
DEBUG_MSG_P(PSTR("[MQTT] Using fingerprint verification, trying to connect\n"));
unsigned char fp[20] = {0};
if (sslFingerPrintArray(getSetting("mqttFP", MQTT_SSL_FINGERPRINT).c_str(), fp)) {
_mqtt.addServerFingerprint(fp);
} else {
DEBUG_MSG_P(PSTR("[MQTT] Wrong fingerprint, cannot connect\n"));
_mqtt_last_connection = millis();
return;
}
} else if (check == SECURE_CLIENT_CHECK_CA) {
DEBUG_MSG_P(PSTR("[MQTT] CA verification is not supported with AsyncMqttClient, cannot connect\n"));
_mqtt_last_connection = millis();
return;
} else {
DEBUG_MSG_P(PSTR("[MQTT] Wrong fingerprint\n"));
DEBUG_MSG_P(PSTR("[MQTT] !!! SSL connection will not be validated !!!\n"));
}
}
#endif // ASYNC_TCP_SSL_ENABLED
DEBUG_MSG_P(PSTR("[MQTT] Client ID: %s\n"), _mqtt_clientid.c_str());
DEBUG_MSG_P(PSTR("[MQTT] QoS: %d\n"), _mqtt_qos);
DEBUG_MSG_P(PSTR("[MQTT] Retain flag: %d\n"), _mqtt_retain ? 1 : 0);
DEBUG_MSG_P(PSTR("[MQTT] Keepalive time: %ds\n"), _mqtt_keepalive);
DEBUG_MSG_P(PSTR("[MQTT] Will topic: %s\n"), _mqtt_will.c_str());
#endif // SECURE_CLIENT != SECURE_CLIENT_NONE
_mqtt.connect();
#else // not MQTT_USE_ASYNC
#else // Using PubSubClient or Arduino-MQTT
bool response = true;
bool verified = true; // Connection verified
bool secure = false; // Whether to use SSL
#if ASYNC_TCP_SSL_ENABLED
#if SECURE_CLIENT != SECURE_CLIENT_NONE
bool secure = getSetting("mqttUseSSL", MQTT_SSL_ENABLED).toInt() == 1;
secure = getSetting("mqttUseSSL", MQTT_SSL_ENABLED).toInt() == 1;
if (secure) {
DEBUG_MSG_P(PSTR("[MQTT] Using SSL\n"));
if (_mqtt_client_secure.connect(_mqtt_server.c_str(), _mqtt_port)) {
// Use MFLN if configured and on BearSSL
#if SECURE_CLIENT == SECURE_CLIENT_BEARSSL
uint16_t requested_mfln = getSetting("mqttScMFLN", MQTT_SECURE_CLIENT_MFLN).toInt();
if (requested_mfln) {
bool supported = _mqtt_client_secure.probeMaxFragmentLength(_mqtt_server.c_str(), _mqtt_port, requested_mfln);
DEBUG_MSG_P(PSTR("[MQTT] MFLN buffer size %u supported: %s\n"), requested_mfln, supported ? "YES" : "NO");
if (supported) {
_mqtt_client_secure.setBufferSizes(requested_mfln, requested_mfln);
}
}
#endif
// Default verification: CA in case of BearSSL, fingerprint otherwise
int check = getSetting("mqttScCheck", MQTT_SECURE_CLIENT_CHECK).toInt();
if (check == SECURE_CLIENT_CHECK_FINGERPRINT) {
DEBUG_MSG_P(PSTR("[MQTT] Using fingerprint verification, trying to connect\n"));
char fp[60] = {0};
if (sslFingerPrintChar(getSetting("mqttFP", MQTT_SSL_FINGERPRINT).c_str(), fp)) {
if (_mqtt_client_secure.verify(fp, _mqtt_server.c_str())) {
_mqtt.setClient(_mqtt_client_secure);
// BearSSL needs to have the fingerprint set prior to connecting
#if SECURE_CLIENT == SECURE_CLIENT_BEARSSL
_mqtt_client_secure.setFingerprint(fp); // Always returns true
#endif
if (_mqtt_client_secure.connect(_mqtt_server.c_str(), _mqtt_port)) {
// AxTLS does the fingerprint check *after* connecting
#if SECURE_CLIENT == SECURE_CLIENT_AXTLS
if (!_mqtt_client_secure.verify(fp, _mqtt_server.c_str())) {
DEBUG_MSG_P(PSTR("[MQTT] Fingerprint did not match\n"));
verified = false;
}
#endif
} else {
DEBUG_MSG_P(PSTR("[MQTT] Invalid fingerprint\n"));
response = false;
DEBUG_MSG_P(PSTR("[MQTT] Client connection failed, incorrect fingerprint?\n"));
verified = false;
}
_mqtt_client_secure.stop();
yield();
} else {
DEBUG_MSG_P(PSTR("[MQTT] Wrong fingerprint\n"));
response = false;
DEBUG_MSG_P(PSTR("[MQTT] Invalid fingerprint, cannot connect\n"));
verified = false;
}
} else if (check == SECURE_CLIENT_CHECK_CA) {
#if SECURE_CLIENT == SECURE_CLIENT_BEARSSL
#ifndef _mqtt_client_ca
DEBUG_MSG_P(PSTR("[MQTT] No CA certificate defined, cannot connect\n"));
verified = false;
#else
DEBUG_MSG_P(PSTR("[MQTT] Using CA verification, trying to connect\n"));
// We need to allocate using new in order to keep the list in memory
_ca_list = new BearSSL::X509List(_mqtt_client_ca);
// If NTP is not synced yet, the connect() call may fail.
// This is not an issue, MQTT will reconnect after MQTT_RECONNECT_DELAY_MIN
#if NTP_SUPPORT
_mqtt_client_secure.setX509Time(ntpLocal2UTC(now()));
#else
_mqtt_client_secure.setX509Time(now());
#endif
_mqtt_client_secure.setTrustAnchors(_ca_list);
if (!_mqtt_client_secure.connect(_mqtt_server.c_str(), _mqtt_port)) {
DEBUG_MSG_P(PSTR("[MQTT] CA verification failed - possible reasons are an incorrect certificate or unsynced clock\n"));
verified = false;
}
#endif // defined _mqtt_client_ca
#else
DEBUG_MSG_P(PSTR("[MQTT] CA verification is not supported with AxTLS client, cannot connect\n"));
verified = false;
#endif
} else {
DEBUG_MSG_P(PSTR("[MQTT] Client connection failed\n"));
response = false;
}
DEBUG_MSG_P(PSTR("[MQTT] !!! SSL connection will not be validated !!!\n"));
} else {
_mqtt.setClient(_mqtt_client);
#if SECURE_CLIENT == SECURE_CLIENT_BEARSSL
_mqtt_client_secure.setInsecure();
#endif
}
}
#else // not ASYNC_TCP_SSL_ENABLED
_mqtt.setClient(_mqtt_client);
#endif // SECURE_CLIENT != SECURE_CLIENT_NONE
#endif // ASYNC_TCP_SSL_ENABLED
if (response) {
_mqtt.setServer(_mqtt_server.c_str(), _mqtt_port);
if (_mqtt_user.length() && _mqtt_pass.length()) {
DEBUG_MSG_P(PSTR("[MQTT] Connecting as user %s\n"), _mqtt_user.c_str());
response = _mqtt.connect(_mqtt_clientid.c_str(), _mqtt_user.c_str(), _mqtt_pass.c_str(), _mqtt_will.c_str(), _mqtt_qos, _mqtt_retain, "0");
} else {
response = _mqtt.connect(_mqtt_clientid.c_str(), _mqtt_will.c_str(), _mqtt_qos, _mqtt_retain, "0");
}
DEBUG_MSG_P(PSTR("[MQTT] Client ID: %s\n"), _mqtt_clientid.c_str());
DEBUG_MSG_P(PSTR("[MQTT] QoS: %d\n"), _mqtt_qos);
DEBUG_MSG_P(PSTR("[MQTT] Retain flag: %d\n"), _mqtt_retain ? 1 : 0);
DEBUG_MSG_P(PSTR("[MQTT] Keepalive time: %ds\n"), _mqtt_keepalive);
DEBUG_MSG_P(PSTR("[MQTT] Will topic: %s\n"), _mqtt_will.c_str());
if (verified) {
#if MQTT_LIBRARY == MQTT_ARDUINO // Arduino-MQTT
_mqtt.begin(_mqtt_server.c_str(), _mqtt_port, (secure ? _mqtt_client_secure : _mqtt_client));
_mqtt.setWill(_mqtt_will.c_str(), MQTT_STATUS_OFFLINE, _mqtt_qos, _mqtt_retain);
verified = _mqtt.connect(_mqtt_clientid.c_str(), _mqtt_user.c_str(), _mqtt_pass.c_str());
#else // PubSubClient
_mqtt.setClient(secure ? _mqtt_client_secure : _mqtt_client);
_mqtt.setServer(_mqtt_server.c_str(), _mqtt_port);
if (_mqtt_user.length() && _mqtt_pass.length()) {
DEBUG_MSG_P(PSTR("[MQTT] Connecting as user %s\n"), _mqtt_user.c_str());
verified = _mqtt.connect(_mqtt_clientid.c_str(), _mqtt_user.c_str(), _mqtt_pass.c_str(), _mqtt_will.c_str(), _mqtt_qos, _mqtt_retain, MQTT_STATUS_OFFLINE);
} else {
verified = _mqtt.connect(_mqtt_clientid.c_str(), _mqtt_will.c_str(), _mqtt_qos, _mqtt_retain, MQTT_STATUS_OFFLINE);
}
#endif
}
if (response) {
if (verified) {
_mqttOnConnect();
} else {
DEBUG_MSG_P(PSTR("[MQTT] Connection failed\n"));
_mqtt_last_connection = millis();
mqttDisconnect(); // Clean up
_mqtt_last_connection = millis();
}
#endif // MQTT_USE_ASYNC
#endif // MQTT_LIBRARY == MQTT_ASYNC
}
@ -318,9 +399,9 @@ void _mqttBackwards() {
}
void _mqttInfo() {
DEBUG_MSG_P(PSTR("[MQTT] Async %s, SSL %s, Autoconnect %s\n"),
MQTT_USE_ASYNC ? "ENABLED" : "DISABLED",
ASYNC_TCP_SSL_ENABLED ? "ENABLED" : "DISABLED",
DEBUG_MSG_P(PSTR("[MQTT] Library %s, SSL %s, Autoconnect %s\n"),
(MQTT_LIBRARY == MQTT_ASYNC ? "AsyncMqttClient" : (MQTT_LIBRARY == MQTT_ARDUINO ? "Arduino-MQTT" : "PubSubClient")),
SECURE_CLIENT == SECURE_CLIENT_NONE ? "DISABLED" : "ENABLED",
MQTT_AUTOCONNECT ? "ENABLED" : "DISABLED"
);
DEBUG_MSG_P(PSTR("[MQTT] Client %s, %s\n"),
@ -367,7 +448,7 @@ void _mqttWebSocketOnConnected(JsonObject& root) {
root["mqttKeep"] = _mqtt_keepalive;
root["mqttRetain"] = _mqtt_retain;
root["mqttQoS"] = _mqtt_qos;
#if ASYNC_TCP_SSL_ENABLED
#if SECURE_CLIENT != SECURE_CLIENT_NONE
root["mqttUseSSL"] = getSetting("mqttUseSSL", MQTT_SSL_ENABLED).toInt() == 1;
root["mqttFP"] = getSetting("mqttFP", MQTT_SSL_FINGERPRINT);
#endif
@ -552,10 +633,13 @@ String mqttTopic(const char * magnitude, unsigned int index, bool is_set) {
void mqttSendRaw(const char * topic, const char * message, bool retain) {
if (_mqtt.connected()) {
#if MQTT_USE_ASYNC
#if MQTT_LIBRARY == MQTT_ASYNC // AsyncMqttClient
unsigned int packetId = _mqtt.publish(topic, _mqtt_qos, retain, message);
DEBUG_MSG_P(PSTR("[MQTT] Sending %s => %s (PID %d)\n"), topic, message, packetId);
#else
#elif MQTT_LIBRARY == MQTT_ARDUINO // Arduino-MQTT
_mqtt.publish(topic, message, retain, _mqtt_qos);
DEBUG_MSG_P(PSTR("[MQTT] Sending %s => %s\n"), topic, message);
#else // PubSubClient
_mqtt.publish(topic, message, retain);
DEBUG_MSG_P(PSTR("[MQTT] Sending %s => %s\n"), topic, message);
#endif
@ -720,10 +804,10 @@ int8_t mqttEnqueue(const char * topic, const char * message) {
void mqttSubscribeRaw(const char * topic) {
if (_mqtt.connected() && (strlen(topic) > 0)) {
#if MQTT_USE_ASYNC
#if MQTT_LIBRARY == MQTT_ASYNC // AsyncMqttClient
unsigned int packetId = _mqtt.subscribe(topic, _mqtt_qos);
DEBUG_MSG_P(PSTR("[MQTT] Subscribing to %s (PID %d)\n"), topic, packetId);
#else
#else // Arduino-MQTT or PubSubClient
_mqtt.subscribe(topic, _mqtt_qos);
DEBUG_MSG_P(PSTR("[MQTT] Subscribing to %s\n"), topic);
#endif
@ -736,10 +820,10 @@ void mqttSubscribe(const char * topic) {
void mqttUnsubscribeRaw(const char * topic) {
if (_mqtt.connected() && (strlen(topic) > 0)) {
#if MQTT_USE_ASYNC
#if MQTT_LIBRARY == MQTT_ASYNC // AsyncMqttClient
unsigned int packetId = _mqtt.unsubscribe(topic);
DEBUG_MSG_P(PSTR("[MQTT] Unsubscribing to %s (PID %d)\n"), topic, packetId);
#else
#else // Arduino-MQTT or PubSubClient
_mqtt.unsubscribe(topic);
DEBUG_MSG_P(PSTR("[MQTT] Unsubscribing to %s\n"), topic);
#endif
@ -769,6 +853,9 @@ void mqttDisconnect() {
DEBUG_MSG_P(PSTR("[MQTT] Disconnecting\n"));
_mqtt.disconnect();
}
#if SECURE_CLIENT == SECURE_CLIENT_BEARSSL
delete _ca_list;
#endif
}
bool mqttForward() {
@ -802,7 +889,7 @@ void mqttSetup() {
_mqttBackwards();
_mqttInfo();
#if MQTT_USE_ASYNC
#if MQTT_LIBRARY == MQTT_ASYNC // AsyncMqttClient
_mqtt.onConnect([](bool sessionPresent) {
_mqttOnConnect();
@ -823,7 +910,7 @@ void mqttSetup() {
if (reason == AsyncMqttClientDisconnectReason::MQTT_NOT_AUTHORIZED) {
DEBUG_MSG_P(PSTR("[MQTT] Not authorized\n"));
}
#if ASYNC_TCP_SSL_ENABLED
#if SECURE_CLIENT == SECURE_CLIENT_AXTLS
if (reason == AsyncMqttClientDisconnectReason::TLS_BAD_FINGERPRINT) {
DEBUG_MSG_P(PSTR("[MQTT] Bad fingerprint\n"));
}
@ -840,13 +927,19 @@ void mqttSetup() {
DEBUG_MSG_P(PSTR("[MQTT] Publish ACK for PID %d\n"), packetId);
});
#else // not MQTT_USE_ASYNC
#elif MQTT_LIBRARY == MQTT_ARDUINO // Arduino-MQTT
_mqtt.onMessageAdvanced([](MQTTClient *client, char topic[], char payload[], int length) {
_mqttOnMessage(topic, payload, length);
});
#else // PubSubClient
_mqtt.setCallback([](char* topic, byte* payload, unsigned int length) {
_mqttOnMessage(topic, (char *) payload, length);
});
#endif // MQTT_USE_ASYNC
#endif // MQTT_LIBRARY == MQTT_ASYNC
_mqttConfigure();
mqttRegister(_mqttCallback);
@ -877,11 +970,11 @@ void mqttLoop() {
if (WiFi.status() != WL_CONNECTED) return;
#if MQTT_USE_ASYNC
#if MQTT_LIBRARY == MQTT_ASYNC
_mqttConnect();
#else // not MQTT_USE_ASYNC
#else // MQTT_LIBRARY != MQTT_ASYNC
if (_mqtt.connected()) {
@ -898,7 +991,7 @@ void mqttLoop() {
}
#endif
#endif // MQTT_LIBRARY == MQTT_ASYNC
}


+ 94
- 0
code/espurna/static/letsencrypt_isrgroot_pem.h View File

@ -0,0 +1,94 @@
// ISRG Root X1 (self-signed)
// from https://letsencrypt.org/certs/isrgrootx1.pem.txt
// Note: LetsEncrypt will only start using this root certificate to sign
// certificates after July 8, 2020. Any certificate issued before this date
// uses the X3 intermediate certificate down below.
// See: https://letsencrypt.org/2019/04/15/transitioning-to-isrg-root.html
// Issuer: C = US, O = Internet Security Research Group, CN = ISRG Root X1
// Validity
// Not Before: Jun 4 11:04:38 2015 GMT
// Not After : Jun 4 11:04:38 2035 GMT
// Subject: C = US, O = Internet Security Research Group, CN = ISRG Root X1
#pragma once
#include <pgmspace.h>
const char PROGMEM _ssl_letsencrypt_isrg_x1_ca[] = R"EOF(
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----
)EOF";
// Lets Encrypt Authority X3 (Signed by ISRG Root X1)
// from https://letsencrypt.org/certs/letsencryptauthorityx3.pem.txt
// Issuer: C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
// Validity
// Not Before: Oct 6 15:43:55 2016 GMT
// Not After : Oct 6 15:43:55 2021 GMT
const char PROGMEM _ssl_letsencrypt_isrg_x3_ca[] = R"EOF(
-----BEGIN CERTIFICATE-----
MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1
WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrX
NSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf
89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHl
Npi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7Dc
Gu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgz
uEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMB
AAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBU
BgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIB
FiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSo
SmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js
LnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEF
BQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsG
AQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYD
VR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIB
ABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGx
A/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRM
UM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2
DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1
eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOu
OsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vw
p7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY
2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0
ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKR
PB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5b
rUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt
-----END CERTIFICATE-----
)EOF";

+ 0
- 46
code/espurna/static/letsencrypt_isrgrootx1_pem.h View File

@ -1,46 +0,0 @@
// ISRG Root X1 (self-signed)
// from https://letsencrypt.org/certs/isrgrootx1.pem.txt
// Issuer: C = US, O = Internet Security Research Group, CN = ISRG Root X1
// Validity
// Not Before: Jun 4 11:04:38 2015 GMT
// Not After : Jun 4 11:04:38 2035 GMT
// Subject: C = US, O = Internet Security Research Group, CN = ISRG Root X1
#pragma once
#include <pgmspace.h>
const char PROGMEM _ssl_letsencrypt_isrg_x1_ca[]= R"EOF(
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----
)EOF";

+ 1
- 1
code/espurna/thinkspeak.ino View File

@ -229,7 +229,7 @@ void _tspkPost() {
_tspk_client_ts = millis();
#if ASYNC_TCP_SSL_ENABLED
#if SECURE_CLIENT == SECURE_CLIENT_AXTLS
bool connected = _tspk_client->connect(THINGSPEAK_HOST, THINGSPEAK_PORT, THINGSPEAK_USE_SSL);
#else
bool connected = _tspk_client->connect(THINGSPEAK_HOST, THINGSPEAK_PORT);


+ 6
- 6
code/espurna/web.ino View File

@ -37,10 +37,10 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#endif // WEB_EMBEDDED
#if ASYNC_TCP_SSL_ENABLED & WEB_SSL_ENABLED
#if SECURE_CLIENT == SECURE_CLIENT_AXTLS & WEB_SSL_ENABLED
#include "static/server.cer.h"
#include "static/server.key.h"
#endif // ASYNC_TCP_SSL_ENABLED & WEB_SSL_ENABLED
#endif // SECURE_CLIENT == SECURE_CLIENT_AXTLS & WEB_SSL_ENABLED
// -----------------------------------------------------------------------------
@ -192,7 +192,7 @@ void _onHome(AsyncWebServerRequest *request) {
} else {
#if ASYNC_TCP_SSL_ENABLED
#if SECURE_CLIENT == SECURE_CLIENT_AXTLS
// Chunked response, we calculate the chunks based on free heap (in multiples of 32)
// This is necessary when a TLS connection is open since it sucks too much memory
@ -233,7 +233,7 @@ void _onHome(AsyncWebServerRequest *request) {
}
#endif
#if ASYNC_TCP_SSL_ENABLED & WEB_SSL_ENABLED
#if SECURE_CLIENT == SECURE_CLIENT_AXTLS & WEB_SSL_ENABLED
int _onCertificate(void * arg, const char *filename, uint8_t **buf) {
@ -444,7 +444,7 @@ void webRequestRegister(web_request_callback_f callback) {
}
unsigned int webPort() {
#if ASYNC_TCP_SSL_ENABLED & WEB_SSL_ENABLED
#if SECURE_CLIENT == SECURE_CLIENT_AXTLS & WEB_SSL_ENABLED
return 443;
#else
return getSetting("webPort", WEB_PORT).toInt();
@ -495,7 +495,7 @@ void webSetup() {
_server->onNotFound(_onRequest);
// Run server
#if ASYNC_TCP_SSL_ENABLED & WEB_SSL_ENABLED
#if SECURE_CLIENT == SECURE_CLIENT_AXTLS & WEB_SSL_ENABLED
_server->onSslFileRequest(_onCertificate, NULL);
_server->beginSecure("server.cer", "server.key", NULL);
#else


+ 1
- 0
code/platformio.ini View File

@ -114,6 +114,7 @@ lib_deps =
https://github.com/xoseperez/justwifi.git#2.0.2
https://github.com/madpilot/mDNSResolver#4cfcda1
https://github.com/xoseperez/my92xx#3.0.1
MQTT
https://bitbucket.org/xoseperez/nofuss.git#0.3.0
https://github.com/xoseperez/NtpClient.git#0942ebc
OneWire


Loading…
Cancel
Save