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.
This commit is contained in:
Thomas Kolb 2019-11-26 22:03:44 +01:00
parent 8a1a17bb07
commit 24ba2242a4
11 changed files with 112 additions and 40 deletions

4
.gitignore vendored
View File

@ -4,5 +4,5 @@
.vscode/launch.json
.vscode/ipch
include/WLAN_Logins.h
include/Crypto_Config.h
data/etc/*
!data/etc/.*

View File

@ -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.

0
data/etc/.keep Normal file
View File

36
include/Config.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include <vector>
#include <string>
class Config
{
public:
struct WLAN
{
std::string ssid;
std::string password;
};
typedef std::vector<WLAN> 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;
};

View File

@ -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";
}

View File

@ -1,13 +0,0 @@
#pragma once
#include <array>
struct NetInfo {
const char *ssid;
const char *password;
};
const std::array<NetInfo, 2> NETWORKS {
NetInfo{"SomeNetwork", "ThePassword"},
NetInfo{"SomeOtherNetwork", "TheOtherPassword"}
};

View File

@ -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)
print(result.text)

View File

@ -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];

44
src/Config.cpp Normal file
View File

@ -0,0 +1,44 @@
#include <SPIFFS.h>
#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();
}

View File

@ -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();
}

View File

@ -5,10 +5,10 @@
#include <WiFiMulti.h>
#include <SPIFFS.h>
#include "WLAN_Logins.h"
#include "WebServer.h"
#include "Fader.h"
#include "UDPProto.h"
#include "Config.h"
#include <esp32_digital_led_lib.h>
#include <esp32_digital_led_funcs.h>
@ -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();