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.
master
Thomas Kolb 3 years ago
parent 8a1a17bb07
commit 24ba2242a4
  1. 4
      .gitignore
  2. 15
      README.md
  3. 0
      data/etc/.keep
  4. 36
      include/Config.h
  5. 8
      include/Crypto_Config.h.example
  6. 13
      include/WLAN_Logins.h.example
  7. 13
      scripts/authtest.py
  8. 4
      src/ChallengeResponse.cpp
  9. 44
      src/Config.cpp
  10. 4
      src/WebServer.cpp
  11. 11
      src/main.cpp

4
.gitignore vendored

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

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

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

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

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

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

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

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

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

Loading…
Cancel
Save