Skip to content

Commit

Permalink
add WireGuard implementation to increase security
Browse files Browse the repository at this point in the history
WireGuard is really ideal for those IoT devices with limited resources.
  • Loading branch information
perexg committed Dec 1, 2023
1 parent 3789d44 commit 2c94573
Show file tree
Hide file tree
Showing 16 changed files with 384 additions and 21 deletions.
2 changes: 1 addition & 1 deletion data/en.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"en":{"language":"English","home":{"title":"Main Menu","btn":"Main Menu","nav":"Home"},"save":"Save Settings","user":"Username","pass":"Password","hasp":{"title":"HASP Design","btn":"HASP Design","theme":"UI Theme","color1":"Primary color","color2":"Secondary color","pages":"Start Layout","font":"Default Font","startpage":"Startup Page","startdim":"Startup Dim"},"screenshot":{"title":"Screenshot","btn":"Screenshot","nav":"Screenshot","prev":"Prev Page","next":"Next Page","refresh":"Refresh"},"info":{"title":"Information","btn":"Information","nav":"Information"},"config":{"title":"Configuration","btn":"Configuration","nav":"Settings"},"ota":{"title":"Firmware Update","btn":"Firmware Update","nav":"Firmware","submit":"Update Firmware","file":"Firmware File","url":"Firmware URL","redirect":"Follow Redirects","never":"Never","strict":"Strict","always":"Always"},"editor":{"title":"File Editor","btn":"File Editor","nav":"File Editor"},"reset":{"title":"Factory Reset","btn":"Factory Reset","warning":"Warning","message":"This process will reset all settings to the default values. The internal flash will be erased and the device is restarted. You may need to connect to the WiFi AP displayed on the panel to reconfigure the device before accessing it again.","fileloss":"ALL FILES WILL BE LOST!"},"reboot":{"title":"Rebooting...","btn":"Restart","nav":"Reboot","message":"The device is rebooting."},"about":{"credits":"Based on the previous work of the following open source developers:","copyright":"Copyright ","rights":"All rights reserved.","clause1":"Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:","clause2":"The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.","clause3":"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.","mit":"MIT License","bsd":"BSD License","freebsd":"FreeBSD License","apache2":"Apache2 License"},"wifi":{"title":"Wifi Settings","btn":"Wifi Settings","ssid":"SSID"},"mqtt":{"title":"MQTT Settings","btn":"MQTT Settings","name":"Hostname","group":"Groupname","host":"Broker","port":"Port","node_t":"Node Topic","group_t":"Group Topic","broadcast_t":"Broadcast Topic","hass_t":"HA LWT Topic"},"http":{"title":"HTTP Settings","btn":"HTTP Settings"},"ftp":{"title":"FTP Settings","btn":"FTP Settings","port":"FTP Port","pasv":"Passive Port"},"gui":{"title":"Display Settings","btn":"Display Settings","antiburn":"Antiburn","calibrate":"Calibrate"},"gpio":"GPIO Settings","debug":{"title":"Debug Settings","btn":"Debug Settings","baud":"Baudrate","tele":"Tele Period","ansi":"Use ANSI codes","host":"Syslog Server","port":"Syslog Port","ietf":"IETF (RFC 5424)","bsd":"BSD (RFC 3164)","log":"Facility"},"time":{"title":"Time Settings","btn":"Time Settings","region":"Region","zone":"Timezone","tz":"Timezone","ntp":"NTP Servers"},"region":{"etc":"Etcetera ","continents":"Continents ","af":"Africa ","as":"Asia ","au":"Australia ","aq":"Antarctica ","eu":"Europe ","na":"North America ","sa":"South America ","islands":"Islands ","at":"Atlantic Ocean ","in":"Indian Ocean ","pa":"Pacific Ocean "}}}
{"en":{"language":"English","home":{"title":"Main Menu","btn":"Main Menu","nav":"Home"},"save":"Save Settings","user":"Username","pass":"Password","hasp":{"title":"HASP Design","btn":"HASP Design","theme":"UI Theme","color1":"Primary color","color2":"Secondary color","pages":"Start Layout","font":"Default Font","startpage":"Startup Page","startdim":"Startup Dim"},"screenshot":{"title":"Screenshot","btn":"Screenshot","nav":"Screenshot","prev":"Prev Page","next":"Next Page","refresh":"Refresh"},"info":{"title":"Information","btn":"Information","nav":"Information"},"config":{"title":"Configuration","btn":"Configuration","nav":"Settings"},"ota":{"title":"Firmware Update","btn":"Firmware Update","nav":"Firmware","submit":"Update Firmware","file":"Firmware File","url":"Firmware URL","redirect":"Follow Redirects","never":"Never","strict":"Strict","always":"Always"},"editor":{"title":"File Editor","btn":"File Editor","nav":"File Editor"},"reset":{"title":"Factory Reset","btn":"Factory Reset","warning":"Warning","message":"This process will reset all settings to the default values. The internal flash will be erased and the device is restarted. You may need to connect to the WiFi AP displayed on the panel to reconfigure the device before accessing it again.","fileloss":"ALL FILES WILL BE LOST!"},"reboot":{"title":"Rebooting...","btn":"Restart","nav":"Reboot","message":"The device is rebooting."},"about":{"credits":"Based on the previous work of the following open source developers:","copyright":"Copyright ","rights":"All rights reserved.","clause1":"Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:","clause2":"The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.","clause3":"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.","mit":"MIT License","bsd":"BSD License","freebsd":"FreeBSD License","apache2":"Apache2 License"},"wifi":{"title":"Wifi Settings","btn":"Wifi Settings","ssid":"SSID"},"wg":{"title":"WireGuard Settings","btn":"WireGuard Settings","vpnip":"VPN IP","privkey":"Private Key","host":"Remote IP","port":"Remote Port","pubkey":"Remote Public Key"},"mqtt":{"title":"MQTT Settings","btn":"MQTT Settings","name":"Hostname","group":"Groupname","host":"Broker","port":"Port","node_t":"Node Topic","group_t":"Group Topic","broadcast_t":"Broadcast Topic","hass_t":"HA LWT Topic"},"http":{"title":"HTTP Settings","btn":"HTTP Settings"},"ftp":{"title":"FTP Settings","btn":"FTP Settings","port":"FTP Port","pasv":"Passive Port"},"gui":{"title":"Display Settings","btn":"Display Settings","antiburn":"Antiburn","calibrate":"Calibrate"},"gpio":"GPIO Settings","debug":{"title":"Debug Settings","btn":"Debug Settings","baud":"Baudrate","tele":"Tele Period","ansi":"Use ANSI codes","host":"Syslog Server","port":"Syslog Port","ietf":"IETF (RFC 5424)","bsd":"BSD (RFC 3164)","log":"Facility"},"time":{"title":"Time Settings","btn":"Time Settings","region":"Region","zone":"Timezone","tz":"Timezone","ntp":"NTP Servers"},"region":{"etc":"Etcetera ","continents":"Continents ","af":"Africa ","as":"Asia ","au":"Australia ","aq":"Antarctica ","eu":"Europe ","na":"North America ","sa":"South America ","islands":"Islands ","at":"Atlantic Ocean ","in":"Indian Ocean ","pa":"Pacific Ocean "}}}
2 changes: 1 addition & 1 deletion data/main.js

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions include/hasp_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@
#define HASP_USE_MQTT (HASP_HAS_NETWORK)
#endif

#ifndef HASP_USE_WIREGUARD
#define HASP_USE_WIREGUARD (HASP_HAS_NETWORK)
#endif

#ifndef HASP_USE_BROADCAST
#define HASP_USE_BROADCAST 1
#endif
Expand Down Expand Up @@ -232,6 +236,10 @@ static WiFiSpiClass WiFi;
#endif
#endif // HASP_USE_WIFI

#if HASP_USE_WIREGUARD > 0
#include "sys/net/hasp_wireguard.h"
#endif

#if HASP_USE_ETHERNET > 0
#if defined(ARDUINO_ARCH_ESP32)
#include "sys/net/hasp_ethernet_esp32.h"
Expand Down
8 changes: 8 additions & 0 deletions src/hasp/hasp_dispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,14 @@ void dispatch_config(const char* topic, const char* payload, uint8_t source)
else
timeGetConfig(settings);
}
#if HASP_USE_WIREGUARD > 0
else if(strcasecmp_P(topic, PSTR(FP_WG)) == 0) {
if(update)
wgSetConfig(settings);
else
wgGetConfig(settings);
}
#endif
#if HASP_USE_MQTT > 0
else if(strcasecmp_P(topic, PSTR(FP_MQTT)) == 0) {
if(update)
Expand Down
37 changes: 25 additions & 12 deletions src/hasp/hasp_nvs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include "hasplib.h"
#include "hasp_nvs.h"

#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))

bool nvs_user_begin(Preferences& preferences, const char* key, bool readonly)
{
if(preferences.begin(key, false, "config")) {
Expand All @@ -22,16 +24,16 @@ bool nvs_user_begin(Preferences& preferences, const char* key, bool readonly)

bool nvs_clear_user_config()
{
const char* name[] = {FP_TIME, FP_OTA, FP_HTTP, FP_FTP, FP_MQTT, FP_WIFI};
const char* name[] = {FP_TIME, FP_OTA, FP_HTTP, FP_FTP, FP_MQTT, FP_WIFI, FP_WG};
Preferences preferences;
bool state = true;

for(int i = 0; i < sizeof(name) / sizeof(name[0]); i++) {
for(int i = 0; i < ARRAY_SIZE(name); i++) {
if(preferences.begin(name[i], false) && !preferences.clear()) state = false;
preferences.end();
}

for(int i = 0; i < sizeof(name) / sizeof(name[0]); i++) {
for(int i = 0; i < ARRAY_SIZE(name); i++) {
if(preferences.begin(name[i], false, "config") && !preferences.clear()) state = false;
preferences.end();
}
Expand Down Expand Up @@ -226,19 +228,30 @@ void nvs_setup()
nvs_stats.free_entries, nvs_stats.total_entries);

{ // TODO: remove migratrion of keys from default NVS partition to CONFIG partition
const char* name[8] = {FP_TIME, FP_OTA, FP_HTTP, FP_FTP, FP_MQTT, FP_WIFI};
const struct {
const char* name;
const char* config;
} sec[] = {
{ .name = FP_TIME, .config = FP_CONFIG_PASS },
{ .name = FP_OTA, .config = FP_CONFIG_PASS },
{ .name = FP_HTTP, .config = FP_CONFIG_PASS },
{ .name = FP_FTP, .config = FP_CONFIG_PASS },
{ .name = FP_MQTT, .config = FP_CONFIG_PASS },
{ .name = FP_WIFI, .config = FP_CONFIG_PASS },
{ .name = FP_WG, .config = FP_CONFIG_PRIVATE_KEY }
};
Preferences oldPrefs, newPrefs;

for(int i = 0; i < 6; i++) {
if(oldPrefs.begin(name[i], false) && newPrefs.begin(name[i], false, "config")) {
LOG_INFO(TAG_NVS, "opened %s", name[i]);
String password = oldPrefs.getString(FP_CONFIG_PASS, D_PASSWORD_MASK);
for(int i = 0; i < ARRAY_SIZE(sec); i++) {
if(oldPrefs.begin(sec[i].name, false) && newPrefs.begin(sec[i].name, false, "config")) {
LOG_INFO(TAG_NVS, "opened %s", sec[i].name);
String password = oldPrefs.getString(sec[i].config, D_PASSWORD_MASK);
if(password != D_PASSWORD_MASK) {
LOG_INFO(TAG_NVS, "found %s %s => %s", name[i], D_PASSWORD_MASK, password.c_str());
size_t len = newPrefs.putString(FP_CONFIG_PASS, password);
LOG_INFO(TAG_NVS, "found %s %s => %s", sec[i].name, D_PASSWORD_MASK, password.c_str());
size_t len = newPrefs.putString(sec[i].config, password);
if(len == password.length()) {
oldPrefs.remove(FP_CONFIG_PASS);
LOG_INFO(TAG_NVS, "Moved %s key %s to new NVS partition", name[i], FP_CONFIG_PASS);
oldPrefs.remove(sec[i].config);
LOG_INFO(TAG_NVS, "Moved %s key %s to new NVS partition", sec[i].name, sec[i].config);
}
}
}
Expand Down
51 changes: 45 additions & 6 deletions src/hasp_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,19 @@ bool configSet(lv_color_t& value, const JsonVariant& setting, const __FlashStrin
}
return false;
}
bool configSet(char *value, size_t size, const JsonVariant& setting, const __FlashStringHelper* fstr_name)
{
if(!setting.isNull()) {
const char *val = setting;
if(strcmp(value, val) != 0) {
confDebugSet(fstr_name);
strncpy(value, val, size - 1);
value[size - 1] = '\0';
return true;
}
}
return false;
}

bool configSet(bool& value, const JsonVariant& setting, const char* fstr_name)
{
Expand Down Expand Up @@ -198,28 +211,30 @@ void configSetupDebug(JsonDocument& settings)
debugStart(); // Debug started, now we can use it; HASP header sent
}

void configStorePasswords(JsonDocument& settings, String& wifiPass, String& mqttPass, String& httpPass)
void configStorePasswords(JsonDocument& settings, String& wifiPass, String& mqttPass, String& httpPass, String &wgPrivKey)
{
const char* pass = ("pass");

wifiPass = settings[FPSTR(FP_WIFI)][pass].as<String>();
mqttPass = settings[FPSTR(FP_MQTT)][pass].as<String>();
httpPass = settings[FPSTR(FP_HTTP)][pass].as<String>();
wgPrivKey = settings[FPSTR(FP_WG)][FPSTR(FP_CONFIG_PRIVATE_KEY)].as<String>();
}

void configRestorePasswords(JsonDocument& settings, String& wifiPass, String& mqttPass, String& httpPass)
void configRestorePasswords(JsonDocument& settings, String& wifiPass, String& mqttPass, String& httpPass, String& wgPrivKey)
{
const char* pass = ("pass");

if(!settings[FPSTR(FP_WIFI)][pass].isNull()) settings[FPSTR(FP_WIFI)][pass] = wifiPass;
if(!settings[FPSTR(FP_MQTT)][pass].isNull()) settings[FPSTR(FP_MQTT)][pass] = mqttPass;
if(!settings[FPSTR(FP_HTTP)][pass].isNull()) settings[FPSTR(FP_HTTP)][pass] = httpPass;
if(!settings[FPSTR(FP_WG)][FPSTR(FP_CONFIG_PRIVATE_KEY)].isNull()) settings[FPSTR(FP_WG)][FPSTR(FP_CONFIG_PRIVATE_KEY)] = wgPrivKey;
}

void configMaskPasswords(JsonDocument& settings)
{
String passmask = F(D_PASSWORD_MASK);
configRestorePasswords(settings, passmask, passmask, passmask);
configRestorePasswords(settings, passmask, passmask, passmask, passmask);
}

DeserializationError configParseFile(String& configFile, JsonDocument& settings)
Expand Down Expand Up @@ -254,7 +269,7 @@ DeserializationError configRead(JsonDocument& settings, bool setupdebug)
#if HASP_USE_SPIFFS > 0 || HASP_USE_LITTLEFS > 0
error = configParseFile(configFile, settings);
if(!error) {
String output, wifiPass, mqttPass, httpPass;
String output, wifiPass, mqttPass, httpPass, wgPrivKey;

/* Load Debug params */
if(setupdebug) {
Expand All @@ -266,14 +281,14 @@ DeserializationError configRead(JsonDocument& settings, bool setupdebug)
}

LOG_TRACE(TAG_CONF, F(D_FILE_LOADING), configFile.c_str());
configStorePasswords(settings, wifiPass, mqttPass, httpPass);
configStorePasswords(settings, wifiPass, mqttPass, httpPass, wgPrivKey);

// Output settings in log with masked passwords
configMaskPasswords(settings);
serializeJson(settings, output);
LOG_VERBOSE(TAG_CONF, output.c_str());

configRestorePasswords(settings, wifiPass, mqttPass, httpPass);
configRestorePasswords(settings, wifiPass, mqttPass, httpPass, wgPrivKey);
LOG_INFO(TAG_CONF, F(D_FILE_LOADED), configFile.c_str());

// if(setupdebug) debugSetup();
Expand Down Expand Up @@ -382,6 +397,17 @@ void configWrite()
}
#endif

#if HASP_USE_WIREGUARD > 0
module = FPSTR(FP_WG);
if(settings[module].as<JsonObject>().isNull()) settings.createNestedObject(module);
changed = wgGetConfig(settings[module]);
if(changed) {
LOG_VERBOSE(TAG_WG, settingsChanged.c_str());
configOutput(settings[module], TAG_WG);
writefile = true;
}
#endif

#if HASP_USE_MQTT > 0
module = FPSTR(FP_MQTT);
if(settings[module].as<JsonObject>().isNull()) settings.createNestedObject(module);
Expand Down Expand Up @@ -549,6 +575,11 @@ void configSetup()
wifiSetConfig(settings[FPSTR(FP_WIFI)]);
#endif

#if HASP_USE_WIREGUARD > 0
LOG_INFO(TAG_WG, F("Loading WireGuard settings"));
wgSetConfig(settings[FPSTR(FP_WG)]);
#endif

#if HASP_USE_MQTT > 0
LOG_INFO(TAG_MQTT, F("Loading MQTT settings"));
mqttSetConfig(settings[FPSTR(FP_MQTT)]);
Expand Down Expand Up @@ -624,6 +655,14 @@ void configOutput(const JsonObject& settings, uint8_t tag)
output.replace(password, passmask);
}

if(!settings[FPSTR(FP_WG)][FPSTR(FP_CONFIG_PRIVATE_KEY)].isNull()) {
password = F("\"privkey\":\"");
password += settings[FPSTR(FP_WG)][FPSTR(FP_CONFIG_PRIVATE_KEY)].as<String>();
password += F("\"");
passmask = F("\"privkey\":\"" D_PASSWORD_MASK "\"");
output.replace(password, passmask);
}

LOG_VERBOSE(tag, output.c_str());
}

Expand Down
5 changes: 5 additions & 0 deletions src/hasp_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ bool configSet(uint8_t& value, const JsonVariant& setting, const __FlashStringHe
bool configSet(uint16_t& value, const JsonVariant& setting, const __FlashStringHelper* fstr_name);
bool configSet(int32_t& value, const JsonVariant& setting, const __FlashStringHelper* fstr_name);
bool configSet(lv_color_t& value, const JsonVariant& setting, const __FlashStringHelper* fstr_name);
bool configSet(char *value, size_t size, const JsonVariant& setting, const __FlashStringHelper* fstr_name);
bool configSet(bool& value, const JsonVariant& setting, const char* fstr_name);
bool configSet(int8_t& value, const JsonVariant& setting, const char* fstr_name);
bool configSet(uint8_t& value, const JsonVariant& setting, const char* fstr_name);
Expand Down Expand Up @@ -71,6 +72,9 @@ const char FP_CONFIG_BROADCAST_TOPIC[] PROGMEM = "broadcast_t";
const char FP_CONFIG_BAUD[] PROGMEM = "baud";
const char FP_CONFIG_LOG[] PROGMEM = "log";
const char FP_CONFIG_PROTOCOL[] PROGMEM = "proto";
const char FP_CONFIG_VPN_IP[] PROGMEM = "vpnip";
const char FP_CONFIG_PRIVATE_KEY[] PROGMEM = "privkey";
const char FP_CONFIG_PUBLIC_KEY[] PROGMEM = "pubkey";
const char FP_GUI_ROTATION[] PROGMEM = "rotate";
const char FP_GUI_INVERT[] PROGMEM = "invert";
const char FP_GUI_TICKPERIOD[] PROGMEM = "tick";
Expand All @@ -89,6 +93,7 @@ const char FP_GPIO_CONFIG[] PROGMEM = "config";
const char FP_HASP_CONFIG_FILE[] PROGMEM = "/config.json";

const char FP_WIFI[] PROGMEM = "wifi";
const char FP_WG[] PROGMEM = "wg";
const char FP_MQTT[] PROGMEM = "mqtt";
const char FP_HTTP[] PROGMEM = "http";
const char FP_FTP[] PROGMEM = "ftp";
Expand Down
4 changes: 4 additions & 0 deletions src/hasp_debug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,10 @@ void debug_get_tag(uint8_t tag, char* buffer)
memcpy_P(buffer, PSTR("CUST"), 5);
break;

case TAG_WG:
memcpy_P(buffer, PSTR("WG "), 5);
break;

default:
memcpy_P(buffer, PSTR("----"), 5);
break;
Expand Down
1 change: 1 addition & 0 deletions src/hasp_debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ enum {
TAG_FTP = 68,
TAG_TIME = 69,
TAG_NETW = 70,
TAG_WG = 71,

TAG_LVGL = 90,
TAG_LVFS = 91,
Expand Down
Loading

0 comments on commit 2c94573

Please sign in to comment.