From 29470e4dd9bd84f5563d8de2789ee61bdf095ce0 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 7 Feb 2021 09:58:52 -0600 Subject: [PATCH] Completed OTA logic OTA pasword now stored in NVS. Use 'O' command to change from default. Note password is stored as MD5 hash, and is therefore unrecoverable. Changes to password DO NOT take effect until next reboot. Password CAN be changed even if OTA has not been enabled for sketch. Blank passwords are not allowed. Stored password can only be erased with 'E' command. --- src/HAP.cpp | 15 ++++++++++++++- src/HAP.h | 1 + src/HomeSpan.cpp | 34 +++++++++++++++++++++++++++++++--- src/HomeSpan.h | 3 ++- src/Settings.h | 5 +++-- 5 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/HAP.cpp b/src/HAP.cpp index 32c854d..c29658b 100644 --- a/src/HAP.cpp +++ b/src/HAP.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "HAP.h" #include "HomeSpan.h" @@ -43,10 +44,21 @@ void HAPClient::init(){ nvs_open("WIFI",NVS_READWRITE,&wifiNVS); // open WIFI data namespace in NVS nvs_open("SRP",NVS_READWRITE,&srpNVS); // open SRP data namespace in NVS nvs_open("HAP",NVS_READWRITE,&hapNVS); // open HAP data namespace in NVS + nvs_open("OTA",NVS_READWRITE,&otaNVS); // open OTA data namespace in NVS if(!nvs_get_blob(wifiNVS,"WIFIDATA",NULL,&len)) // if found WiFi data in NVS nvs_get_blob(wifiNVS,"WIFIDATA",&homeSpan.network.wifiData,&len); // retrieve data - + + if(!nvs_get_str(otaNVS,"OTADATA",NULL,&len)){ // if found OTA data in NVS + nvs_get_str(otaNVS,"OTADATA",homeSpan.otaPwd,&len); // retrieve data + } else { + MD5Builder otaPwdHash; + otaPwdHash.begin(); + otaPwdHash.add(DEFAULT_OTA_PASSWORD); + otaPwdHash.calculate(); + otaPwdHash.getChars(homeSpan.otaPwd); + } + struct { // temporary structure to hold SRP verification code and salt stored in NVS uint8_t salt[16]; uint8_t verifyCode[384]; @@ -1643,6 +1655,7 @@ TLV HAPClient::tlv8; nvs_handle HAPClient::hapNVS; nvs_handle HAPClient::wifiNVS; nvs_handle HAPClient::srpNVS; +nvs_handle HAPClient::otaNVS; uint8_t HAPClient::httpBuf[MAX_HTTP+1]; HKDF HAPClient::hkdf; pairState HAPClient::pairStatus; diff --git a/src/HAP.h b/src/HAP.h index 9730215..070a170 100644 --- a/src/HAP.h +++ b/src/HAP.h @@ -82,6 +82,7 @@ struct HAPClient { static nvs_handle hapNVS; // handle for non-volatile-storage of HAP data static nvs_handle wifiNVS; // handle for non-volatile-storage of WiFi data static nvs_handle srpNVS; // handle for non-volatile-storage of SRP data + static nvs_handle otaNVS; // handle for non-volatile-storage of OTA data static uint8_t httpBuf[MAX_HTTP+1]; // buffer to store HTTP messages (+1 to leave room for storing an extra 'overflow' character) static HKDF hkdf; // generates (and stores) HKDF-SHA-512 32-byte keys derived from an inputKey of arbitrary length, a salt string, and an info string static pairState pairStatus; // tracks pair-setup status diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index d3b300d..7516093 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -434,7 +434,8 @@ void Span::checkConnect(){ if(otaEnabled){ ArduinoOTA.setHostname(hostName); - + ArduinoOTA.setPasswordHash(otaPwd); + ArduinoOTA .onStart([]() { String type; @@ -539,7 +540,7 @@ void Span::processSerialCommand(const char *c){ Serial.print("\n"); } - Serial.print("\n*** End Status ***\n"); + Serial.print("\n*** End Status ***\n\n"); } break; @@ -565,7 +566,7 @@ void Span::processSerialCommand(const char *c){ if(strlen(s)==4 && strlen(tBuf)==4){ sprintf(qrID,"%s",tBuf); - Serial.print("\n\nChanging default Setup ID for QR Code to : '"); + Serial.print("\nChanging default Setup ID for QR Code to: '"); Serial.print(qrID); Serial.print("'. Will take effect after next restart.\n\n"); nvs_set_str(HAPClient::hapNVS,"SETUPID",qrID); // update data @@ -578,6 +579,32 @@ void Span::processSerialCommand(const char *c){ } break; + case 'O': { + + char textPwd[33]="\0"; + + while(!strlen(textPwd)){ + Serial.print("\n>>> OTA Password (32 characters max): "); + readSerial(textPwd,32); + Serial.print(mask(textPwd,2)); + Serial.print("\n"); + } + + MD5Builder otaPwdHash; + otaPwdHash.begin(); + otaPwdHash.add(textPwd); + otaPwdHash.calculate(); + otaPwdHash.getChars(otaPwd); + nvs_set_str(HAPClient::otaNVS,"OTADATA",otaPwd); // update data + nvs_commit(HAPClient::otaNVS); + + Serial.print("\nPassword change will take effect after next restart.\n"); + if(!otaEnabled) + Serial.print("Note: OTA has not been enabled in this sketch.\n"); + Serial.print("\n"); + } + break; + case 'S': { char buf[128]; @@ -791,6 +818,7 @@ void Span::processSerialCommand(const char *c){ Serial.print(" X - delete WiFi Credentials and restart\n"); Serial.print(" S - change the HomeKit Pairing Setup Code to \n"); Serial.print(" Q - change the HomeKit Setup ID for QR Codes to \n"); + Serial.print(" O - change the OTA password\n"); Serial.print(" A - start the HomeSpan Setup Access Point\n"); Serial.print("\n"); Serial.print(" U - unpair device by deleting all Controller data\n"); diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 7a8777f..c84e6eb 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -100,7 +100,8 @@ struct Span{ unsigned long comModeLife=DEFAULT_COMMAND_TIMEOUT*1000; // length of time (in milliseconds) to keep Command Mode alive before resuming normal operations uint16_t tcpPortNum=DEFAULT_TCP_PORT; // port for TCP communications between HomeKit and HomeSpan char qrID[5]=""; // Setup ID used for pairing with QR Code - boolean otaEnabled=false; // enables Over-the-Air updates + boolean otaEnabled=false; // enables Over-the-Air ("OTA") updates + char otaPwd[33]; // MD5 Hash of OTA password, represented as a string of hexidecimal characters WiFiServer *hapServer; // pointer to the HAP Server connection Blinker statusLED; // indicates HomeSpan status diff --git a/src/Settings.h b/src/Settings.h index fb1417b..8cfff31 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -66,8 +66,9 @@ #define DEFAULT_CONTROL_PIN 21 // change with homeSpan.setControlPin(pin) #define DEFAULT_STATUS_PIN 13 // change with homeSpan.setStatusPin(pin) -#define DEFAULT_AP_SSID "HomeSpan-Setup" // change with homeSpan.setApSSID(pwd) +#define DEFAULT_AP_SSID "HomeSpan-Setup" // change with homeSpan.setApSSID(ssid) #define DEFAULT_AP_PASSWORD "homespan" // change with homeSpan.setApPassword(pwd) +#define DEFAULT_OTA_PASSWORD "homespan-ota" // change with 'O' command #define DEFAULT_AP_TIMEOUT 300 // change with homeSpan.setApTimeout(nSeconds) #define DEFAULT_COMMAND_TIMEOUT 120 // change with homeSpan.setCommandTimeout(nSeconds) @@ -87,7 +88,7 @@ #define LED_WIFI_CONNECTING 2000 // slow flashing #define LED_AP_STARTED 100,0.5,2,300 // rapid double-blink #define LED_AP_CONNECTED 300,0.5,2,400 // medium double-blink -#define LED_OTA_STARTED 300,0.5,5,500 // rapid 5-blink +#define LED_OTA_STARTED 300,0.5,3,400 // medium triple-blink ///////////////////////////////////////////////////// // Message Log Level Control Macros //