NVS support for credential storage

pull/123/head
Hieromon Ikasamo 6 years ago
parent 1f04689ac3
commit 0ad6253ec6
  1. 97
      src/AutoConnectCredential.cpp
  2. 71
      src/AutoConnectCredential.h

@ -129,7 +129,6 @@ int8_t AutoConnectCredential::load(const char* ssid, struct station_config* conf
_dp = AC_HEADERSIZE; _dp = AC_HEADERSIZE;
if (_entries) { if (_entries) {
Serial.printf("load: length series, AC_HEADERSIZE(%d), _containSize(%d)\n", AC_HEADERSIZE, _containSize);
_eeprom->begin(AC_HEADERSIZE + _containSize); _eeprom->begin(AC_HEADERSIZE + _containSize);
for (uint8_t i = 0; i < _entries; i++) { for (uint8_t i = 0; i < _entries; i++) {
_retrieveEntry(reinterpret_cast<char*>(config->ssid), reinterpret_cast<char*>(config->password), config->bssid); _retrieveEntry(reinterpret_cast<char*>(config->ssid), reinterpret_cast<char*>(config->password), config->bssid);
@ -315,12 +314,24 @@ void AutoConnectCredential::_retrieveEntry(char* ssid, char* password, uint8_t*
* Free area are filled with FF, which is reused as an area for insertion. * Free area are filled with FF, which is reused as an area for insertion.
*/ */
AutoConnectCredential::AutoConnectCredential() { AutoConnectCredential::AutoConnectCredential() {
_allocateEntry();
}
AutoConnectCredential::AutoConnectCredential(uint16_t offset) {
// In ESP32, always use from the beginning of the Preferences area.
// The offset parameter is invalid but preserved for backward compatibility.
(void)(offset);
_allocateEntry();
}
void AutoConnectCredential::_allocateEntry(void) {
_pref.reset(new Preferences); _pref.reset(new Preferences);
_entries = _import(); _entries = _import();
} }
AutoConnectCredential::~AutoConnectCredential() { AutoConnectCredential::~AutoConnectCredential() {
_credit.clear(); _credit.clear();
_pref->end();
_pref.reset(); _pref.reset();
} }
@ -335,10 +346,8 @@ bool AutoConnectCredential::del(const char* ssid) {
if (it != _credit.end()) { if (it != _credit.end()) {
_credit.erase(it); _credit.erase(it);
_entries = _credit.size(); _entries = _credit.size();
Serial.printf("%s deleted.\n", ssid);
return true; return true;
} }
Serial.printf("%s could not deleted, not found.\n", ssid);
return false; return false;
} }
@ -356,7 +365,7 @@ int8_t AutoConnectCredential::load(const char* ssid, struct station_config* conf
if (it != _credit.end()) { if (it != _credit.end()) {
_obtain(it, config); _obtain(it, config);
// Detemine the number in entries // Determine the number in entries
int8_t en = 0; int8_t en = 0;
for (decltype(_credit)::iterator se = _credit.begin(), e = _credit.end(); se != e; ++se) { for (decltype(_credit)::iterator se = _credit.begin(), e = _credit.end(); se != e; ++se) {
if (it == se) if (it == se)
@ -398,7 +407,6 @@ bool AutoConnectCredential::load(int8_t entry, struct station_config* config) {
*/ */
bool AutoConnectCredential::save(const struct station_config* config) { bool AutoConnectCredential::save(const struct station_config* config) {
if (_add(config)) { if (_add(config)) {
Serial.printf("%s added.\n", config->ssid);
return _commit() > 0 ? true : false; return _commit() > 0 ? true : false;
} }
return false; return false;
@ -424,8 +432,8 @@ bool AutoConnectCredential::_add(const station_config_t* config) {
// Insert // Insert
AC_CREDTBODY_t credtBody; AC_CREDTBODY_t credtBody;
credtBody.password = String(reinterpret_cast<const char*>(config->password)); credtBody.password = String(reinterpret_cast<const char*>(config->password));
memcpy(credtBody.bssid, config->bssid, sizeof(((AC_CREDTBODY_t*)0)->bssid)); memcpy(credtBody.bssid, config->bssid, sizeof(AC_CREDTBODY_t::bssid));
std::pair<std::map<String, AC_CREDTBODY_t>::iterator, bool> rc = _credit.insert(std::make_pair(ssid, credtBody)); std::pair<AC_CREDT_t::iterator, bool> rc = _credit.insert(std::make_pair(ssid, credtBody));
_entries = _credit.size(); _entries = _credit.size();
return rc.second; return rc.second;
} }
@ -442,16 +450,14 @@ size_t AutoConnectCredential::_commit(void) {
size_t psz = 0; size_t psz = 0;
// Calculate the container size for saving to NVS. // Calculate the container size for saving to NVS.
// Calculate the serialization size for each entry and add the size // Calculate the serialization size for each entry and add the size of 'e' with the size of 'ss' to it.
// of 'e' with the size of 'ss' to it.
for (const auto& credt : _credit) { for (const auto& credt : _credit) {
ssid = credt.first; ssid = credt.first;
credtBody = credt.second; credtBody = credt.second;
sz += ssid.length() + sizeof('\0') + credtBody.password.length() + sizeof('\0') + sizeof(((AC_CREDTBODY_t*)0)->bssid); sz += ssid.length() + sizeof('\0') + credtBody.password.length() + sizeof('\0') + sizeof(AC_CREDTBODY_t::bssid);
} }
_entries = _credit.size(); _entries = _credit.size();
// When the entry is not empty, the size of container terminator as // When the entry is not empty, the size of container terminator as '\0' must be added.
// '\0' must be added.
_containSize = sz + (_entries ? sizeof('\0') : 0); _containSize = sz + (_entries ? sizeof('\0') : 0);
// Add size of 'e' and 'ss' field. // Add size of 'e' and 'ss' field.
psz = _containSize + sizeof(uint8_t) + sizeof(uint16_t); psz = _containSize + sizeof(uint8_t) + sizeof(uint16_t);
@ -459,29 +465,42 @@ size_t AutoConnectCredential::_commit(void) {
// Dump container to serialization pool and write it back to NVS. // Dump container to serialization pool and write it back to NVS.
uint8_t* credtPool = (uint8_t*)malloc(psz); uint8_t* credtPool = (uint8_t*)malloc(psz);
if (credtPool) { if (credtPool) {
credtPool[0] = _entries; credtPool[0] = _entries; // 'e'
credtPool[1] = (uint8_t)((uint16_t)psz & 0x00ff); credtPool[1] = (uint8_t)((uint16_t)psz & 0x00ff); // 'ss' low byte
credtPool[2] = (uint8_t)((uint16_t)psz >> 8); credtPool[2] = (uint8_t)((uint16_t)psz >> 8); // 'ss' high byte
// Starts dump of credential entries
uint16_t dp = 3; uint16_t dp = 3;
for (const auto& credt : _credit) { for (const auto& credt : _credit) {
ssid = credt.first; ssid = credt.first; // Retrieve SSID
credtBody = credt.second; credtBody = credt.second; // Retrieve an entry
// SSID
size_t itemLen = ssid.length() + sizeof('\0'); size_t itemLen = ssid.length() + sizeof('\0');
ssid.toCharArray(reinterpret_cast<char*>(&credtPool[dp]), itemLen); ssid.toCharArray(reinterpret_cast<char*>(&credtPool[dp]), itemLen);
// Password
dp += itemLen; dp += itemLen;
itemLen = credtBody.password.length() + sizeof('\0'); itemLen = credtBody.password.length() + sizeof('\0');
credtBody.password.toCharArray(reinterpret_cast<char*>(&credtPool[dp]), itemLen); credtBody.password.toCharArray(reinterpret_cast<char*>(&credtPool[dp]), itemLen);
// BSSID
dp += itemLen; dp += itemLen;
memcpy(&credtPool[dp], credtBody.bssid, sizeof(((station_config_t*)0)->bssid)); memcpy(&credtPool[dp], credtBody.bssid, sizeof(station_config_t::bssid));
dp += sizeof(((station_config_t*)0)->bssid); dp += sizeof(station_config_t::bssid);
} }
credtPool[dp] = '\0'; credtPool[dp] = '\0'; // Terminates a container
if (_pref->begin(AC_CREDENTIAL_NVSNAME)) { // Write back to the nvs
if (_pref->begin(AC_CREDENTIAL_NVSNAME, false)) {
sz = _pref->putBytes(AC_CREDENTIAL_NVSKEY, credtPool, psz); sz = _pref->putBytes(AC_CREDENTIAL_NVSKEY, credtPool, psz);
_pref->end(); _pref->end();
} }
#ifdef AC_DBG
else
AC_DBG("Preferences begin failed to save " AC_CREDENTIAL_NVSKEY "\n");
#endif
free(credtPool); free(credtPool);
} }
#ifdef AC_DBG
else
AC_DBG("Preferences pool %d(B) allocation failed\n", psz);
#endif
return sz; return sz;
} }
@ -491,40 +510,60 @@ size_t AutoConnectCredential::_commit(void) {
*/ */
uint8_t AutoConnectCredential::_import(void) { uint8_t AutoConnectCredential::_import(void) {
uint8_t cn = 0; uint8_t cn = 0;
if (_pref->begin(AC_CREDENTIAL_NVSNAME)) { if (_pref->begin(AC_CREDENTIAL_NVSNAME, true)) {
size_t psz = _pref->getBytesLength(AC_CREDENTIAL_NVSKEY); size_t psz = _pref->getBytesLength(AC_CREDENTIAL_NVSKEY);
if (psz) { if (psz) {
uint8_t* credtPool = (uint8_t*)malloc(psz); uint8_t* credtPool = (uint8_t*)malloc(psz);
if (credtPool) { if (credtPool) {
_pref->getBytes(AC_CREDENTIAL_NVSKEY, static_cast<void*>(credtPool), psz); _pref->getBytes(AC_CREDENTIAL_NVSKEY, static_cast<void*>(credtPool), psz);
_credit.clear(); _credit.clear();
cn = credtPool[0]; cn = credtPool[0]; // Retrieve 'e'
_containSize = credtPool[1] + (uint16_t)(credtPool[2] << 8); _containSize = credtPool[1] + (uint16_t)(credtPool[2] << 8); // Retrieve 'ss'
uint16_t dp = sizeof(uint8_t) + sizeof(uint16_t); uint16_t dp = sizeof(uint8_t) + sizeof(uint16_t);
// Starts import
while (dp < psz - sizeof('\0')) { while (dp < psz - sizeof('\0')) {
AC_CREDTBODY_t credtBody; AC_CREDTBODY_t credtBody;
// SSID
String ssid = String(reinterpret_cast<const char*>(&credtPool[dp])); String ssid = String(reinterpret_cast<const char*>(&credtPool[dp]));
// Password
dp += ssid.length() + sizeof('\0'); dp += ssid.length() + sizeof('\0');
credtBody.password = String(reinterpret_cast<const char*>(&credtPool[dp])); credtBody.password = String(reinterpret_cast<const char*>(&credtPool[dp]));
// BSSID
dp += credtBody.password.length() + sizeof('\0'); dp += credtBody.password.length() + sizeof('\0');
for (uint8_t ep = 0; ep < sizeof(((AC_CREDTBODY_t*)0)->bssid); ep++) for (uint8_t ep = 0; ep < sizeof(AC_CREDTBODY_t::bssid); ep++)
credtBody.bssid[ep] = credtPool[dp++]; credtBody.bssid[ep] = credtPool[dp++];
// Make an entry
_credit.insert(std::make_pair(ssid, credtBody)); _credit.insert(std::make_pair(ssid, credtBody));
} }
free(credtPool); free(credtPool);
} }
#ifdef AC_DBG
else
AC_DBG("Preferences pool %d(B) allocation failed\n", psz);
#endif
} }
_pref->end(); _pref->end();
} }
#ifdef AC_DBG
else
AC_DBG("Preferences begin failed to import " AC_CREDENTIAL_NVSKEY "\n");
#endif
return cn; return cn;
} }
void AutoConnectCredential::_obtain(std::map<String, AC_CREDTBODY_t>::iterator const& it, station_config_t* config) { /**
* Obtains an entry pointed to by the specified iterator from the
* dictionary as the std::map that maintains the credentials into the
* station_config structure.
* @param it An iterator to an entry
* @param config the station_config structure storing SSID and password.
*/
void AutoConnectCredential::_obtain(AC_CREDT_t::iterator const& it, station_config_t* config) {
String ssid = it->first; String ssid = it->first;
AC_CREDTBODY_t& credtBody = it->second; AC_CREDTBODY_t& credtBody = it->second;
ssid.toCharArray(reinterpret_cast<char*>(config->ssid), sizeof(((station_config_t*)0)->ssid)); ssid.toCharArray(reinterpret_cast<char*>(config->ssid), sizeof(station_config_t::ssid));
credtBody.password.toCharArray(reinterpret_cast<char*>(config->password), sizeof(((station_config_t*)0)->password)); credtBody.password.toCharArray(reinterpret_cast<char*>(config->password), sizeof(station_config_t::password));
memcpy(config->bssid, credtBody.bssid, sizeof(((station_config_t*)0)->bssid)); memcpy(config->bssid, credtBody.bssid, sizeof(station_config_t::bssid));
} }
#endif #endif

@ -22,25 +22,20 @@ extern "C" {
#include <map> #include <map>
#include <esp_wifi.h> #include <esp_wifi.h>
#include <Preferences.h> #include <Preferences.h>
struct station_config {
uint8_t ssid[32];
uint8_t password[64];
uint8_t bssid_set;
uint8_t bssid[6];
wifi_fast_scan_threshold_t threshold;
};
#endif #endif
/** Credential storage area offset specifier in EEPROM. /**
* By defining AC_IDENTIFIER_OFFSET macro in the user sketch, the credential * Credential storage area offset specifier in EEPROM.
* storage area can be shifted in EEPROM. * By defining AC_IDENTIFIER_OFFSET macro in the user sketch, the credential
* storage area can be shifted in EEPROM.
*/ */
#ifndef AC_IDENTIFIER_OFFSET #ifndef AC_IDENTIFIER_OFFSET
#define AC_IDENTIFIER_OFFSET 0 #define AC_IDENTIFIER_OFFSET 0
#endif #endif
/** /**
* * Storage identifier for AutoConnect credentials. It is global constant
* and reserved.
*/ */
#ifndef AC_IDENTIFIER #ifndef AC_IDENTIFIER
#define AC_IDENTIFIER "AC_CREDT" #define AC_IDENTIFIER "AC_CREDT"
@ -48,7 +43,7 @@ struct station_config {
class AutoConnectCredentialBase { class AutoConnectCredentialBase {
public: public:
explicit AutoConnectCredentialBase() {} explicit AutoConnectCredentialBase() : _entries(0), _containSize(0) {}
virtual ~AutoConnectCredentialBase() {} virtual ~AutoConnectCredentialBase() {}
virtual uint8_t entries(void) { return _entries; } virtual uint8_t entries(void) { return _entries; }
virtual bool del(const char* ssid) = 0; virtual bool del(const char* ssid) = 0;
@ -57,6 +52,8 @@ class AutoConnectCredentialBase {
virtual bool save(const struct station_config* config) = 0; virtual bool save(const struct station_config* config) = 0;
protected: protected:
virtual void _allocateEntry(void) = 0; /**< Initialize storage for credentials. */
uint8_t _entries; /**< Count of the available entry */ uint8_t _entries; /**< Count of the available entry */
uint16_t _containSize; /**< Container size */ uint16_t _containSize; /**< Container size */
}; };
@ -69,14 +66,16 @@ class AutoConnectCredential : public AutoConnectCredentialBase {
AutoConnectCredential(); AutoConnectCredential();
explicit AutoConnectCredential(uint16_t offset); explicit AutoConnectCredential(uint16_t offset);
~AutoConnectCredential(); ~AutoConnectCredential();
bool del(const char* ssid) override; bool del(const char* ssid) override;
int8_t load(const char* ssid, struct station_config* config) override; int8_t load(const char* ssid, struct station_config* config) override;
bool load(int8_t entry, struct station_config* config) override; bool load(int8_t entry, struct station_config* config) override;
bool save(const struct station_config* config) override; bool save(const struct station_config* config) override;
protected:
void _allocateEntry(void) override; /**< Initialize storage for credentials. */
private: private:
void _allocateEntry(void); /**< Initialize storage for credentials. */ void _retrieveEntry(char* ssid, char* password, uint8_t* bssid); /**< Read an available entry. */
void _retrieveEntry(char* ssid, char* password, uint8_t* bssid); /**< Read an available entry. */
int _dp; /**< The current address in EEPROM */ int _dp; /**< The current address in EEPROM */
int _ep; /**< The current entry address in EEPROM */ int _ep; /**< The current entry address in EEPROM */
@ -89,31 +88,43 @@ class AutoConnectCredential : public AutoConnectCredentialBase {
#define AC_CREDENTIAL_NVSNAME AC_IDENTIFIER #define AC_CREDENTIAL_NVSNAME AC_IDENTIFIER
#define AC_CREDENTIAL_NVSKEY AC_CREDENTIAL_NVSNAME #define AC_CREDENTIAL_NVSKEY AC_CREDENTIAL_NVSNAME
struct station_config {
uint8_t ssid[32];
uint8_t password[64];
uint8_t bssid_set;
uint8_t bssid[6];
wifi_fast_scan_threshold_t threshold;
};
/** AutoConnectCredential class using Preferences for ESP32 */ /** AutoConnectCredential class using Preferences for ESP32 */
class AutoConnectCredential : public AutoConnectCredentialBase { class AutoConnectCredential : public AutoConnectCredentialBase {
public: public:
AutoConnectCredential(); AutoConnectCredential();
explicit AutoConnectCredential(uint16_t offset); explicit AutoConnectCredential(uint16_t offset);
~AutoConnectCredential(); ~AutoConnectCredential();
bool del(const char* ssid) override; bool del(const char* ssid) override;
int8_t load(const char* ssid, struct station_config* config) override; int8_t load(const char* ssid, struct station_config* config) override;
bool load(int8_t entry, struct station_config* config) override; bool load(int8_t entry, struct station_config* config) override;
bool save(const struct station_config* config) override; bool save(const struct station_config* config) override;
protected:
void _allocateEntry(void) override; /**< Initialize storage for credentials. */
private: private:
typedef station_config station_config_t;
typedef struct { typedef struct {
String password; String password;
uint8_t bssid[6]; uint8_t bssid[6];
} AC_CREDTBODY_t; } AC_CREDTBODY_t; /**< Credential entry */
typedef std::map<String, AC_CREDTBODY_t> AC_CREDT_t;
typedef station_config station_config_t;
bool _add(const station_config_t* config); bool _add(const station_config_t* config); /**< Add an entry */
size_t _commit(void); size_t _commit(void); /**< Write back to the nvs */
uint8_t _import(void); uint8_t _import(void); /**< Import from the nvs */
void _obtain(std::map<String, AC_CREDTBODY_t>::iterator const& it, station_config_t* config); void _obtain(AC_CREDT_t::iterator const& it, station_config_t* config); /**< Obtain an entry from iterator */
std::unique_ptr<Preferences> _pref; AC_CREDT_t _credit; /**< Dictionary to maintain the credentials */
std::map<String, AC_CREDTBODY_t> _credit; std::unique_ptr<Preferences> _pref; /**< Preferences class instance to access the nvs */
}; };
#endif #endif

Loading…
Cancel
Save