Skip to content

Commit

Permalink
TLS support + fingerprint attribute in config file ( #399 )
Browse files Browse the repository at this point in the history
Credit @adriancuzman
  • Loading branch information
timpur committed Jan 1, 2018
1 parent e64784b commit 00843f3
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 6 deletions.
5 changes: 4 additions & 1 deletion docs/configuration/json-configuration-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ Below is the format of the JSON configuration you will have to provide:
"base_topic": "devices/",
"auth": true,
"username": "user",
"password": "pass"
"password": "pass",
"ssl": true,
"ssl_fingerprint": "a27992d3420c89f293d351378ba5f5675f74fe3c"
},
"ota": {
"enabled": true
Expand All @@ -46,6 +48,7 @@ Here are the rules:
- `bssid` and `channel` have to be defined together and these settings are independand of settings related to static IP
- to define static IP, `ip` (IP address), `mask` (netmask) and `gw` (gateway) settings have to be defined at the same time
- to define second DNS `dns2` the first one `dns1` has to be defined. Set DNS without `ip`, `mask` and `gw` does not affect the configuration (dns server will be provided by DHCP). It is not required to set DNS servers.
* `ssl_fingerprint` if not required if `ssl` is enabled.


Default values if not provided:
Expand Down
14 changes: 13 additions & 1 deletion src/Homie/Boot/BootNormal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ void BootNormal::setup() {
Interface::get().getMqttClient().onPublish(std::bind(&BootNormal::_onMqttPublish, this, std::placeholders::_1));

Interface::get().getMqttClient().setServer(Interface::get().getConfig().get().mqtt.server.host, Interface::get().getConfig().get().mqtt.server.port);

#if ASYNC_TCP_SSL_ENABLED
Interface::get().getLogger() << "SSL is: " << Interface::get().getConfig().get().mqtt.server.ssl.enabled << endl;
Interface::get().getMqttClient().setSecure(Interface::get().getConfig().get().mqtt.server.ssl.enabled);
if (Interface::get().getConfig().get().mqtt.server.ssl.enabled && Interface::get().getConfig().get().mqtt.server.ssl.hasFingerprint) {
char hexBuf[MAX_FINGERPRINT_SIZE * 2 + 1];
Helpers::byteArrayToHexString(Interface::get().getConfig().get().mqtt.server.ssl.fingerprint, hexBuf, MAX_FINGERPRINT_SIZE);
Interface::get().getLogger() << "Using fingerprint: " << hexBuf << endl;
Interface::get().getMqttClient().addServerFingerprint((const uint8_t*)Interface::get().getConfig().get().mqtt.server.ssl.fingerprint);
}
#endif

Interface::get().getMqttClient().setMaxTopicLength(MAX_MQTT_TOPIC_LENGTH);
_mqttClientId = std::unique_ptr<char[]>(new char[strlen(Interface::get().brand) + 1 + strlen(Interface::get().getConfig().get().deviceId) + 1]);
strcpy(_mqttClientId.get(), Interface::get().brand);
Expand Down Expand Up @@ -919,4 +931,4 @@ bool HomieInternals::BootNormal::__handleNodeProperty(char * topic, char * paylo
}

return false;
}
}
19 changes: 19 additions & 0 deletions src/Homie/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ bool Config::load() {
if (parsedJson["mqtt"].as<JsonObject&>().containsKey("port")) {
reqMqttPort = parsedJson["mqtt"]["port"];
}
bool reqMqttSsl = false;
if (parsedJson["mqtt"].as<JsonObject&>().containsKey("ssl")) {
reqMqttSsl = parsedJson["mqtt"]["ssl"];
}
const char* reqMqttFingerprint = "";
if (parsedJson["mqtt"].as<JsonObject&>().containsKey("ssl_fingerprint")) {
reqMqttFingerprint = parsedJson["mqtt"]["ssl_fingerprint"];
}
const char* reqMqttBaseTopic = DEFAULT_MQTT_BASE_TOPIC;
if (parsedJson["mqtt"].as<JsonObject&>().containsKey("base_topic")) {
reqMqttBaseTopic = parsedJson["mqtt"]["base_topic"];
Expand Down Expand Up @@ -119,6 +127,11 @@ bool Config::load() {
strlcpy(_configStruct.wifi.dns1, reqWifiDns1, MAX_IP_STRING_LENGTH);
strlcpy(_configStruct.wifi.dns2, reqWifiDns2, MAX_IP_STRING_LENGTH);
strlcpy(_configStruct.mqtt.server.host, reqMqttHost, MAX_HOSTNAME_LENGTH);
_configStruct.mqtt.server.ssl.enabled = reqMqttSsl;
if (strcmp_P(reqMqttFingerprint, PSTR("")) != 0) {
_configStruct.mqtt.server.ssl.hasFingerprint = true;
Helpers::hexStringToByteArray(reqMqttFingerprint, _configStruct.mqtt.server.ssl.fingerprint, MAX_FINGERPRINT_SIZE);
}
_configStruct.mqtt.server.port = reqMqttPort;
strlcpy(_configStruct.mqtt.baseTopic, reqMqttBaseTopic, MAX_MQTT_BASE_TOPIC_LENGTH);
_configStruct.mqtt.auth = reqMqttAuth;
Expand Down Expand Up @@ -409,6 +422,12 @@ void Config::log() const {
Interface::get().getLogger() << F(" • MQTT: ") << endl;
Interface::get().getLogger() << F(" ◦ Host: ") << _configStruct.mqtt.server.host << endl;
Interface::get().getLogger() << F(" ◦ Port: ") << _configStruct.mqtt.server.port << endl;
Interface::get().getLogger() << F(" ◦ SSL enabled: ") << (_configStruct.mqtt.server.ssl.enabled ? "true" : "false") << endl;
if (_configStruct.mqtt.server.ssl.enabled && _configStruct.mqtt.server.ssl.hasFingerprint) {
char hexBuf[MAX_FINGERPRINT_SIZE * 2 + 1];
Helpers::byteArrayToHexString(Interface::get().getConfig().get().mqtt.server.ssl.fingerprint, hexBuf, MAX_FINGERPRINT_SIZE);
Interface::get().getLogger() << F(" ◦ Fingerprint: ") << hexBuf << endl;
}
Interface::get().getLogger() << F(" ◦ Base topic: ") << _configStruct.mqtt.baseTopic << endl;
Interface::get().getLogger() << F(" ◦ Auth? ") << (_configStruct.mqtt.auth ? F("yes") : F("no")) << endl;
if (_configStruct.mqtt.auth) {
Expand Down
5 changes: 5 additions & 0 deletions src/Homie/Datatypes/ConfigStruct.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ struct ConfigStruct {
struct Server {
char host[MAX_HOSTNAME_LENGTH];
uint16_t port;
struct {
bool enabled;
bool hasFingerprint;
uint8_t fingerprint[MAX_FINGERPRINT_SIZE];
} ssl;
} server;
char baseTopic[MAX_MQTT_BASE_TOPIC_LENGTH];
bool auth;
Expand Down
1 change: 1 addition & 0 deletions src/Homie/Limits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ namespace HomieInternals {
const uint8_t MAX_WIFI_SSID_LENGTH = 32 + 1;
const uint8_t MAX_WIFI_PASSWORD_LENGTH = 64 + 1;
const uint16_t MAX_HOSTNAME_LENGTH = 255 + 1;
const uint8_t MAX_FINGERPRINT_SIZE = 20;

const uint8_t MAX_MQTT_CREDS_LENGTH = 32 + 1;
const uint8_t MAX_MQTT_BASE_TOPIC_LENGTH = 48 + 1;
Expand Down
19 changes: 17 additions & 2 deletions src/Homie/Utils/Helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,25 @@ void Helpers::ipToString(const IPAddress& ip, char * str) {
snprintf(str, MAX_IP_STRING_LENGTH, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
}

void HomieInternals::Helpers::macToString(const uint8_t mac[MAX_MAC_LENGTH], char * str) {
void Helpers::macToString(const uint8_t mac[MAX_MAC_LENGTH], char * str) {
snprintf(str, MAX_MAC_STRING_LENGTH, "%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}

void HomieInternals::Helpers::macToFormattedString(const uint8_t mac[MAX_MAC_LENGTH], char * str) {
void Helpers::macToFormattedString(const uint8_t mac[MAX_MAC_LENGTH], char * str) {
snprintf(str, MAX_MAC_FORMATTED_STRING_LENGTH, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}

void Helpers::hexStringToByteArray(const char* hexStr, uint8_t* hexArray, uint8_t size) {
for (uint8_t i = 0; i < size; i++) {
char hex[3];
strncpy(hex, (hexStr + (i * 2)), 2);
hex[2] = '\0';
hexArray[i] = (uint8_t)strtol((const char*)&hex, nullptr, 16);
}
}

void Helpers::byteArrayToHexString(const uint8_t * hexArray, char* hexStr, uint8_t size) {
for (uint8_t i = 0; i < size; i++) {
snprintf((hexStr + (i * 2)), 3, "%02x", hexArray[i]);
}
}
6 changes: 4 additions & 2 deletions src/Homie/Utils/Helpers.hpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
#pragma once

#include "Arduino.h"
#include <memory>
#include <IPAddress.h>
#include "../../StreamingOperator.hpp"
#include "../Limits.hpp"
#include <memory>

namespace HomieInternals {
class Helpers {
public:
public:
static void abort(const String& message);
static uint8_t rssiToPercentage(int32_t rssi);
static void stringToBytes(const char* str, char sep, byte* bytes, int maxBytes, int base);
Expand All @@ -19,5 +19,7 @@ class Helpers {
static void ipToString(const IPAddress& ip, char* str);
static void macToString(const uint8_t mac[MAX_MAC_LENGTH], char * str);
static void macToFormattedString(const uint8_t mac[MAX_MAC_LENGTH], char * str);
static void hexStringToByteArray(const char* hexStr, uint8_t* hexArray, uint8_t size);
static void byteArrayToHexString(const uint8_t* hexArray, char* hexStr, uint8_t size);
};
} // namespace HomieInternals
17 changes: 17 additions & 0 deletions src/Homie/Utils/Validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,23 @@ ValidationResult Validation::_validateConfigMqtt(const JsonObject& object) {
result.reason = F("mqtt.port is not an integer");
return result;
}
if (object["mqtt"].as<JsonObject&>().containsKey("ssl")) {
if (!object["mqtt"]["ssl"].is<bool>()) {
result.reason = F("mqtt.ssl is not a bool");
return result;
}
}
if (object["mqtt"].as<JsonObject&>().containsKey("ssl_fingerprint")) {
if (!object["mqtt"]["ssl_fingerprint"].is<const char*>()) {
result.reason = F("mqtt.ssl_fingerprint is not a string");
return result;
}

if (strlen(object["mqtt"]["ssl_fingerprint"]) > MAX_FINGERPRINT_SIZE * 2) {
result.reason = F("mqtt.ssl_fingerprint is too long");
return result;
}
}
if (object["mqtt"].as<JsonObject&>().containsKey("base_topic")) {
if (!object["mqtt"]["base_topic"].is<const char*>()) {
result.reason = F("mqtt.base_topic is not a string");
Expand Down

0 comments on commit 00843f3

Please sign in to comment.