From 24ba2242a409411b93cee07e8e69fa14574ab24a Mon Sep 17 00:00:00 2001 From: Thomas Kolb Date: Tue, 26 Nov 2019 22:03:44 +0100 Subject: [PATCH] Load sensitive data from the SPIFFS Sensitive data are WiFi Logins and authentication data. This is done in preparation for the OTA update, where the firmware image will be transferred unencrypted and therefore passwords could be extracted from a dumped image. --- .gitignore | 4 +-- README.md | 15 ++++++++--- data/etc/.keep | 0 include/Config.h | 36 +++++++++++++++++++++++++++ include/Crypto_Config.h.example | 8 ------ include/WLAN_Logins.h.example | 13 ---------- scripts/authtest.py | 13 +++++----- src/ChallengeResponse.cpp | 4 +-- src/Config.cpp | 44 +++++++++++++++++++++++++++++++++ src/WebServer.cpp | 4 +-- src/main.cpp | 11 ++++++--- 11 files changed, 112 insertions(+), 40 deletions(-) create mode 100644 data/etc/.keep create mode 100644 include/Config.h delete mode 100644 include/Crypto_Config.h.example delete mode 100644 include/WLAN_Logins.h.example create mode 100644 src/Config.cpp diff --git a/.gitignore b/.gitignore index 4c60841..567c20a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,5 @@ .vscode/launch.json .vscode/ipch -include/WLAN_Logins.h -include/Crypto_Config.h +data/etc/* +!data/etc/.* diff --git a/README.md b/README.md index 1ac9059..58eccf3 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,16 @@ ## Setup notes -On the files below, remove the `.example` part of the name and adjust to your needs: +Authentication data for WiFi connections and OTA firmware updates is stored in the SPIFFS. You must create the following files: -- `include/WLAN_Logins.h.example` -- `include/Crypto_Config.h.example` +### `data/etc/wlan` + +This file contains WiFi logins. Each entry consists of two lines: The first line contains the SSID to connect to, the second the password. + +### `data/etc/auth` + +This file configures the authentication at the device. For now, this is used for OTA updates. + +The file contains two lines: First the password, second the salt. + +The salt must be known to any client performing OTA updates and can be stored in a local configuration file. The password should never be stored in any config file. diff --git a/data/etc/.keep b/data/etc/.keep new file mode 100644 index 0000000..e69de29 diff --git a/include/Config.h b/include/Config.h new file mode 100644 index 0000000..78f5f09 --- /dev/null +++ b/include/Config.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +class Config +{ + public: + struct WLAN + { + std::string ssid; + std::string password; + }; + + typedef std::vector WLANList; + + static Config &instance() + { + static Config theConfig; + return theConfig; + } + + void load(void); + + const WLANList& getWLANList(void) { return m_wlans; } + + const std::string& getCRPassword(void) { return m_crPassword; } + const std::string& getCRSalt(void) { return m_crSalt; } + + private: + Config(); + + WLANList m_wlans; + std::string m_crPassword; + std::string m_crSalt; +}; \ No newline at end of file diff --git a/include/Crypto_Config.h.example b/include/Crypto_Config.h.example deleted file mode 100644 index 031e505..0000000 --- a/include/Crypto_Config.h.example +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -namespace crypto -{ - // replace these with your own values! - static const char * const HTTP_PASSWORD = "secure!1"; - static const char * const SALT = "1234567890abcdefghijklmnopqrstuv"; -} \ No newline at end of file diff --git a/include/WLAN_Logins.h.example b/include/WLAN_Logins.h.example deleted file mode 100644 index c48d3b4..0000000 --- a/include/WLAN_Logins.h.example +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -struct NetInfo { - const char *ssid; - const char *password; -}; - -const std::array NETWORKS { - NetInfo{"SomeNetwork", "ThePassword"}, - NetInfo{"SomeOtherNetwork", "TheOtherPassword"} -}; diff --git a/scripts/authtest.py b/scripts/authtest.py index 3f9918e..b7dc333 100755 --- a/scripts/authtest.py +++ b/scripts/authtest.py @@ -9,11 +9,12 @@ import re IP = sys.argv[1] # read the salt from the header file -with open("../include/Crypto_Config.h", "r") as header: - for line in header: - if "SALT" in line: - mo = re.search(r'"(.*)"', line) - SALT = mo.groups()[0] +with open("../data/etc/auth", "r") as authFile: + lineno = 0 + for line in authFile: + if lineno == 1: + SALT = line.strip() + lineno += 1 print(f'SALT = "{SALT}"') @@ -35,4 +36,4 @@ m.update(responsestr.encode('utf-8')) response = m.hexdigest() result = requests.get(f"http://{IP}/authtest", {"response": response}) -print(result.text) \ No newline at end of file +print(result.text) diff --git a/src/ChallengeResponse.cpp b/src/ChallengeResponse.cpp index 3ca8ee8..47775ff 100644 --- a/src/ChallengeResponse.cpp +++ b/src/ChallengeResponse.cpp @@ -6,7 +6,7 @@ #include "ChallengeResponse.h" -#include "Crypto_Config.h" +#include "Config.h" ChallengeResponse::ChallengeResponse(const std::string &pw) : m_passwd(pw), m_expireTime(0) @@ -21,7 +21,7 @@ bool ChallengeResponse::verify(const std::string &hash) } std::ostringstream refResponse; - refResponse << m_passwd << ":" << m_currentNonce << ":" << crypto::SALT; + refResponse << m_passwd << ":" << m_currentNonce << ":" << Config::instance().getCRSalt(); // calculate hash of reference response uint8_t sha256sum[32]; diff --git a/src/Config.cpp b/src/Config.cpp new file mode 100644 index 0000000..88bf4e9 --- /dev/null +++ b/src/Config.cpp @@ -0,0 +1,44 @@ +#include + +#include "Config.h" + +Config::Config() +{} + +void Config::load(void) +{ + // load WLANs + File wlanFile = SPIFFS.open("/etc/wlan", "r"); + + while(wlanFile.available()) { + String ssid = wlanFile.readStringUntil('\n'); + + if(!wlanFile.available()) { + Serial.println("/etc/wlan terminated early. Last entry ignored."); + break; + } + + String passwd = wlanFile.readStringUntil('\n'); + + m_wlans.emplace_back(WLAN{ssid.c_str(), passwd.c_str()}); + } + + wlanFile.close(); + + // load Challenge-Response data + File authFile = SPIFFS.open("/etc/auth", "r"); + + if(authFile.available()) { + String passwd = authFile.readStringUntil('\n'); + m_crPassword = passwd.c_str(); + + if(!authFile.available()) { + m_crSalt = ""; + } else { + String salt = authFile.readStringUntil('\n'); + m_crSalt = salt.c_str(); + } + } + + authFile.close(); +} \ No newline at end of file diff --git a/src/WebServer.cpp b/src/WebServer.cpp index b040a5d..27596c7 100644 --- a/src/WebServer.cpp +++ b/src/WebServer.cpp @@ -5,12 +5,12 @@ #include "WebServer.h" -#include "Crypto_Config.h" +#include "Config.h" #include "Fader.h" WebServer::WebServer(void) - : m_cr(crypto::HTTP_PASSWORD), m_fader(NULL) + : m_cr(Config::instance().getCRPassword()), m_fader(NULL) { m_server = new httpsserver::HTTPServer(); } diff --git a/src/main.cpp b/src/main.cpp index 96ae08a..1bad3c0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,10 +5,10 @@ #include #include -#include "WLAN_Logins.h" #include "WebServer.h" #include "Fader.h" #include "UDPProto.h" +#include "Config.h" #include #include @@ -193,6 +193,9 @@ void setup() } } + // load configuration (especially crypto data) + Config::instance().load(); + digitalWrite(2, HIGH); Serial.println(); @@ -210,11 +213,11 @@ void setup() NULL); /* Task handle to keep track of created task */ // Connect the WiFi network (or start an AP if that doesn't work) - for (auto &net : NETWORKS) + for (auto &net : Config::instance().getWLANList()) { Serial.print("Adding network "); - Serial.println(net.ssid); - wiFiMulti.addAP(net.ssid, net.password); + Serial.println(net.ssid.c_str()); + wiFiMulti.addAP(net.ssid.c_str(), net.password.c_str()); } wifi_setup();