diff --git a/code/espurna/config/dependencies.h b/code/espurna/config/dependencies.h index 1eb49f4a..61f4edff 100644 --- a/code/espurna/config/dependencies.h +++ b/code/espurna/config/dependencies.h @@ -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 diff --git a/code/espurna/config/general.h b/code/espurna/config/general.h index 861165e1..df522033 100644 --- a/code/espurna/config/general.h +++ b/code/espurna/config/general.h @@ -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" diff --git a/code/espurna/config/prototypes.h b/code/espurna/config/prototypes.h index 48ea9f4b..322d64c3 100644 --- a/code/espurna/config/prototypes.h +++ b/code/espurna/config/prototypes.h @@ -172,6 +172,13 @@ using mqtt_callback_f = std::function; 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 // ----------------------------------------------------------------------------- diff --git a/code/espurna/config/types.h b/code/espurna/config/types.h index 2a2ca77e..adc20dd1 100644 --- a/code/espurna/config/types.h +++ b/code/espurna/config/types.h @@ -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 diff --git a/code/espurna/mqtt.ino b/code/espurna/mqtt.ino index 474160cf..91fe1741 100644 --- a/code/espurna/mqtt.ino +++ b/code/espurna/mqtt.ino @@ -15,24 +15,38 @@ Copyright (C) 2016-2019 by Xose Pérez #include #include #include +#include + +#if MQTT_LIBRARY == MQTT_ASYNC // AsyncMqttClient + #include + 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 _mqtt; - -#else // Using PubSubClient - -#include -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 + #ifdef MQTT_MAX_PACKET_SIZE + MQTTClient _mqtt(MQTT_MAX_PACKET_SIZE); + #else + MQTTClient _mqtt(); + #endif + #else // Using PubSubClient + #include + 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 } diff --git a/code/espurna/static/letsencrypt_isrgroot_pem.h b/code/espurna/static/letsencrypt_isrgroot_pem.h new file mode 100644 index 00000000..fe627af6 --- /dev/null +++ b/code/espurna/static/letsencrypt_isrgroot_pem.h @@ -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 + +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"; + + +// Let’s 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"; \ No newline at end of file diff --git a/code/espurna/static/letsencrypt_isrgrootx1_pem.h b/code/espurna/static/letsencrypt_isrgrootx1_pem.h deleted file mode 100644 index 18d979a2..00000000 --- a/code/espurna/static/letsencrypt_isrgrootx1_pem.h +++ /dev/null @@ -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 - -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"; diff --git a/code/espurna/thinkspeak.ino b/code/espurna/thinkspeak.ino index 0ab90307..c53cb303 100644 --- a/code/espurna/thinkspeak.ino +++ b/code/espurna/thinkspeak.ino @@ -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); diff --git a/code/espurna/web.ino b/code/espurna/web.ino index c3e6682f..3c80fd2c 100644 --- a/code/espurna/web.ino +++ b/code/espurna/web.ino @@ -37,10 +37,10 @@ Copyright (C) 2016-2019 by Xose Pérez #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 diff --git a/code/platformio.ini b/code/platformio.ini index 48f69276..85b643d6 100644 --- a/code/platformio.ini +++ b/code/platformio.ini @@ -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