From 3c78626b126e378adefeb42da98ae9fd22717cba Mon Sep 17 00:00:00 2001 From: ksaye Date: Thu, 29 Apr 2021 12:53:46 -0500 Subject: [PATCH 1/4] Adding support for SAS Token Generation Azure IoT Hub uses a Sha256 has as opposed to a password. This change enables the admin to add the 'Preshared Key' and lets Tasmota calculate the SAS token, vs the original changes where the end user had to calculate the SAS Token. --- tasmota/xdrv_02_mqtt.ino | 64 +++++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino index 8fcb330a4..e7dcecbcb 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -24,6 +24,8 @@ #endif #ifdef USE_MQTT_AZURE_IOT +#include +#include #include #undef MQTT_PORT #define MQTT_PORT 8883 @@ -222,6 +224,51 @@ void MqttInit(void) { MqttClient.setSocketTimeout(Settings.mqtt_socket_timeout); } +String azurePreSharedKeytoSASToken(char *iotHubFQDN, const char *deviceId, const char *preSharedKey, int sasTTL = 86400){ + int ttl = time(NULL) + sasTTL; + String dataToSignString = urlEncodeBase64(String(iotHubFQDN) + "/devices/" + String(deviceId)) + "\n" + String(ttl); + char dataToSign[dataToSignString.length() + 1]; + dataToSignString.toCharArray(dataToSign, dataToSignString.length() + 1); + + unsigned char decodedPSK[32]; + unsigned char encryptedSignature[100]; + unsigned char encodedSignature[100]; + br_sha256_context sha256_context; + br_hmac_key_context hmac_key_context; + br_hmac_context hmac_context; + + // need to base64 decode the Preshared key and the length + int base64_decoded_device_length = decode_base64((unsigned char*)preSharedKey, decodedPSK); + + // create the sha256 hmac and hash the data + br_sha256_init(&sha256_context); + br_hmac_key_init(&hmac_key_context, sha256_context.vtable, decodedPSK, base64_decoded_device_length); + br_hmac_init(&hmac_context, &hmac_key_context, 32); + br_hmac_update(&hmac_context, dataToSign, sizeof(dataToSign)-1); + br_hmac_out(&hmac_context, encryptedSignature); + + // base64 decode the HMAC to a char + encode_base64(encryptedSignature, br_hmac_size(&hmac_context), encodedSignature); + + // creating the real SAS Token + String realSASToken = "SharedAccessSignature "; + realSASToken += "sr=" + urlEncodeBase64(String(iotHubFQDN) + "/devices/" + String(deviceId)); + realSASToken += "&sig=" + urlEncodeBase64(String((char*)encodedSignature)); + realSASToken += "&se=" + String(ttl); + + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT "SASToken is '%s'"), realSASToken.c_str()); + + return realSASToken; +} + +String urlEncodeBase64(String stringToEncode){ + // correctly URL encoding the 64 characters of Base64 and the '=' sign + stringToEncode.replace("+", "%2B"); + stringToEncode.replace("=", "%3D"); + stringToEncode.replace("/", "%2F"); + return stringToEncode; +} + bool MqttIsConnected(void) { return MqttClient.connected(); } @@ -259,8 +306,7 @@ bool MqttPublishLib(const char* topic, bool retained) { bool result; #ifdef USE_MQTT_AZURE_IOT - String sourceTopicString = String(topic); - sourceTopicString.replace("/", "%2F"); + String sourceTopicString = urlEncodeBase64(String(topic)); String topicString = "devices/" + String(SettingsText(SET_MQTT_CLIENT)); topicString+= "/messages/events/topic=" + sourceTopicString; @@ -268,9 +314,9 @@ bool MqttPublishLib(const char* topic, bool retained) { JsonParserObject message_object = mqtt_message.getRootObject(); if (message_object.isValid()) { // only sending valid JSON, yet this is optional result = MqttClient.publish(topicString.c_str(), TasmotaGlobal.mqtt_data, retained); - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "Sending '%s'"), TasmotaGlobal.mqtt_data); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT "Sending '%s'"), TasmotaGlobal.mqtt_data); } else { - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "Invalid JSON, '%s' for topic '%s', not sending to Azure IoT Hub"), TasmotaGlobal.mqtt_data, topic); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT "Invalid JSON, '%s' for topic '%s', not sending to Azure IoT Hub"), TasmotaGlobal.mqtt_data, topic); result = true; } #else @@ -747,12 +793,14 @@ void MqttReconnect(void) { #endif #ifdef USE_MQTT_AZURE_IOT - if (String(mqtt_pwd).indexOf("SharedAccessSignature") == -1) { - AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_MQTT "Azure IoT incorrect SAS Token format. Refer to https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-mqtt-support")); - return; + String azureMqtt_password = SettingsText(SET_MQTT_PWD); + if (azureMqtt_password.indexOf("SharedAccessSignature") == -1) { + // assuming a PreSharedKey was provided, calculating a SAS Token into azureMqtt_password + azureMqtt_password = azurePreSharedKeytoSASToken(SettingsText(SET_MQTT_HOST), SettingsText(SET_MQTT_CLIENT), SettingsText(SET_MQTT_PWD)); } + String azureMqtt_userString = String(SettingsText(SET_MQTT_HOST)) + "/" + String(SettingsText(SET_MQTT_CLIENT)); + "/?api-version=2018-06-30"; - if (MqttClient.connect(TasmotaGlobal.mqtt_client, azureMqtt_userString.c_str(), mqtt_pwd, stopic, 1, lwt_retain, TasmotaGlobal.mqtt_data, MQTT_CLEAN_SESSION)) { + if (MqttClient.connect(TasmotaGlobal.mqtt_client, azureMqtt_userString.c_str(), azureMqtt_password.c_str(), stopic, 1, lwt_retain, TasmotaGlobal.mqtt_data, MQTT_CLEAN_SESSION)) { #else if (MqttClient.connect(TasmotaGlobal.mqtt_client, mqtt_user, mqtt_pwd, stopic, 1, lwt_retain, TasmotaGlobal.mqtt_data, MQTT_CLEAN_SESSION)) { #endif // USE_MQTT_AZURE_IOT From 5b90754af61c3ebeecbb097c753210c27218c70a Mon Sep 17 00:00:00 2001 From: ksaye Date: Thu, 29 Apr 2021 13:13:58 -0500 Subject: [PATCH 2/4] Moving the SSL and Base64 to global --- tasmota/xdrv_02_mqtt.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino index e7dcecbcb..51d19f365 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -23,9 +23,9 @@ #define MQTT_WIFI_CLIENT_TIMEOUT 200 // Wifi TCP connection timeout (default is 5000 mSec) #endif -#ifdef USE_MQTT_AZURE_IOT #include #include +#ifdef USE_MQTT_AZURE_IOT #include #undef MQTT_PORT #define MQTT_PORT 8883 From 8aa006625a0974055cc697ae932a48f15928ca26 Mon Sep 17 00:00:00 2001 From: ksaye Date: Thu, 29 Apr 2021 14:45:45 -0500 Subject: [PATCH 3/4] Moving includes back to local Moving the includes back under the '#ifdef USE_MQTT_AZURE_IOT', so the build process will complete for all builds. --- tasmota/xdrv_02_mqtt.ino | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino index 51d19f365..aee1a5dd7 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -23,9 +23,9 @@ #define MQTT_WIFI_CLIENT_TIMEOUT 200 // Wifi TCP connection timeout (default is 5000 mSec) #endif +#ifdef USE_MQTT_AZURE_IOT #include #include -#ifdef USE_MQTT_AZURE_IOT #include #undef MQTT_PORT #define MQTT_PORT 8883 @@ -224,6 +224,7 @@ void MqttInit(void) { MqttClient.setSocketTimeout(Settings.mqtt_socket_timeout); } +#ifdef USE_MQTT_AZURE_IOT String azurePreSharedKeytoSASToken(char *iotHubFQDN, const char *deviceId, const char *preSharedKey, int sasTTL = 86400){ int ttl = time(NULL) + sasTTL; String dataToSignString = urlEncodeBase64(String(iotHubFQDN) + "/devices/" + String(deviceId)) + "\n" + String(ttl); @@ -268,6 +269,7 @@ String urlEncodeBase64(String stringToEncode){ stringToEncode.replace("/", "%2F"); return stringToEncode; } +#endif // USE_MQTT_AZURE_IOT bool MqttIsConnected(void) { return MqttClient.connected(); From 442a7f9ba725fd45c21ca806d1112582c7e0916c Mon Sep 17 00:00:00 2001 From: Adrian Scillato <39969427+ascillato2@users.noreply.github.com> Date: Thu, 29 Apr 2021 23:39:08 -0300 Subject: [PATCH 4/4] Use t_bearssl.h instead of bearssl.h --- tasmota/xdrv_02_mqtt.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino index aee1a5dd7..3d474a684 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -24,7 +24,7 @@ #endif #ifdef USE_MQTT_AZURE_IOT -#include +#include #include #include #undef MQTT_PORT