From 2f1044b0136992af609e74aaaa7430eb185759a7 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 12 Mar 2022 08:24:01 -0600 Subject: [PATCH] Adding Custom Magic Cookie to OTA Partition Will prevent uploading a non-HomeSpan sketch via OTA to an existing HomeSpan device. --- src/HAP.cpp | 2 + src/HAP.h | 1 + src/HomeSpan.cpp | 153 ++++++++++++++++++++++++++++++++++++----------- src/HomeSpan.h | 24 +++++++- src/Settings.h | 5 ++ 5 files changed, 149 insertions(+), 36 deletions(-) diff --git a/src/HAP.cpp b/src/HAP.cpp index dd8eb19..98c0015 100644 --- a/src/HAP.cpp +++ b/src/HAP.cpp @@ -41,6 +41,7 @@ void HAPClient::init(){ 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 + nvs_open("STATE",NVS_READWRITE,&stateNVS); // open STATE data namespace in NVS if(!nvs_get_str(otaNVS,"OTADATA",NULL,&len)){ // if found OTA data in NVS nvs_get_str(otaNVS,"OTADATA",homeSpan.otaPwd,&len); // retrieve data @@ -1750,6 +1751,7 @@ TLV HAPClient::tlv8; nvs_handle HAPClient::hapNVS; nvs_handle HAPClient::srpNVS; nvs_handle HAPClient::otaNVS; +nvs_handle HAPClient::stateNVS; uint8_t HAPClient::httpBuf[MAX_HTTP+1]; HKDF HAPClient::hkdf; pairState HAPClient::pairStatus; diff --git a/src/HAP.h b/src/HAP.h index ebeee29..2f35b47 100644 --- a/src/HAP.h +++ b/src/HAP.h @@ -81,6 +81,7 @@ struct HAPClient { static nvs_handle hapNVS; // handle for non-volatile-storage of HAP 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 nvs_handle stateNVS; // handle for non-volatile-storage of HomeSpan STATE 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 aec9753..5c8eb97 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -29,22 +29,22 @@ #include #include #include -#include -#include #include #include #include #include +#include #include "HomeSpan.h" #include "HAP.h" +const __attribute__((section(".rodata_custom_desc"))) SpanPartition spanPartition = {HOMESPAN_MAGIC_COOKIE}; + using namespace Utils; HAPClient **hap; // HAP Client structure containing HTTP client connections, parsing routines, and state variables (global-scoped variable) Span homeSpan; // HAP Attributes database and all related control functions for this Accessory (global-scoped variable) HapCharacteristics hapChars; // Instantiation of all HAP Characteristics (used to create SpanCharacteristics) -int otaPercent; // local variable to keep track of %progress when OTA is loading new sketch /////////////////////////////// // Span // @@ -135,11 +135,35 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa Serial.print(__DATE__); Serial.print(" "); Serial.print(__TIME__); - + + esp_ota_img_states_t otaState; + esp_ota_get_state_partition(esp_ota_get_running_partition(),&otaState); + Serial.printf("\nPartition: %s (%X)",esp_ota_get_running_partition()->label,otaState); + Serial.printf("\nMagic Cookie: %s",spanPartition.magicCookie); + + esp_app_desc_t appDesc; + esp_ota_get_partition_description(esp_ota_get_running_partition(),&appDesc); + + char newDesc[256]; + esp_partition_read(esp_ota_get_running_partition(), sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t), newDesc, sizeof(newDesc)); + + Serial.println(); + Serial.println(appDesc.version); + Serial.println(appDesc.project_name); + Serial.println(appDesc.time); + Serial.println(appDesc.date); + Serial.println(appDesc.idf_ver); + if(strlen(spanPartition.magicCookie)) + {Serial.println("HI\n"); + } + Serial.println(newDesc); + + Serial.print("\n\nDevice Name: "); Serial.print(displayName); Serial.print("\n\n"); - + + } // begin /////////////////////////////// @@ -524,42 +548,48 @@ void Span::checkConnect(){ if(otaAuth) ArduinoOTA.setPasswordHash(otaPwd); + + ArduinoOTA.onStart(spanOTA.start).onEnd(spanOTA.end).onProgress(spanOTA.progress).onError(spanOTA.error); - ArduinoOTA - .onStart([]() { - Serial.printf("\n*** Current Partition: %s\n*** New Partition: %s\n*** OTA Starting..", - esp_ota_get_running_partition()->label,esp_ota_get_next_update_partition(NULL)->label); - otaPercent=-10; - homeSpan.statusLED.start(LED_OTA_STARTED); - }) - .onEnd([]() { - Serial.printf(" DONE! Rebooting...\n"); - homeSpan.statusLED.off(); - }) - .onProgress([](unsigned int progress, unsigned int total) { - int percent=progress*100/total; - if(percent/10 != otaPercent/10){ - otaPercent=percent; - Serial.printf("%d%%..",progress*100/total); - } - }) - .onError([](ota_error_t error) { - Serial.printf("*** OTA Error[%u]: ", error); - if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed\n"); - else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed\n"); - else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed\n"); - else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed\n"); - else if (error == OTA_END_ERROR) Serial.println("End Failed\n"); - }); +// ArduinoOTA +// .onStart([]() { +// Serial.printf("\n*** Current Partition: %s\n*** New Partition: %s\n*** OTA Starting..", +// esp_ota_get_running_partition()->label,esp_ota_get_next_update_partition(NULL)->label); +// otaPercent=-10; +// homeSpan.statusLED.start(LED_OTA_STARTED); +// }) +// .onEnd([]() { +// Serial.printf(" DONE! Rebooting...\n"); +// char newDesc[256]; +// esp_partition_read(esp_ota_get_next_update_partition(NULL), sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t), newDesc, sizeof(newDesc)); +// Serial.printf("Found: %s\n",newDesc); +// homeSpan.statusLED.off(); +// }) +// .onProgress([](unsigned int progress, unsigned int total) { +// int percent=progress*100/total; +// if(percent/10 != otaPercent/10){ +// otaPercent=percent; +// Serial.printf("%d%%..",progress*100/total); +// if(progress*100/total>20){ +// Serial.println("BAD!!\n"); +// Update.abort(); +// } +// } +// }) +// .onError([](ota_error_t error) { +// Serial.printf("*** OTA Error[%u]: ", error); +// if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed\n"); +// else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed\n"); +// else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed\n"); +// else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed\n"); +// else if (error == OTA_END_ERROR) Serial.println("End Failed\n"); +// }); ArduinoOTA.begin(); Serial.print("Starting OTA Server: "); Serial.print(displayName); Serial.print(" at "); Serial.print(WiFi.localIP()); - esp_ota_img_states_t otaState; - esp_ota_get_state_partition(esp_ota_get_running_partition(),&otaState); -// Serial.printf("\nPartition: %s State: %X",esp_ota_get_running_partition()->label,otaState); Serial.print("\nAuthorization Password: "); Serial.print(otaAuth?"Enabled\n\n":"DISABLED!\n\n"); } else { @@ -1996,3 +2026,58 @@ void SpanWebLog::addLog(const char *fmt, ...){ if(homeSpan.logLevel>0) Serial.printf("WEBLOG: %s\n",log[index].message); } + +/////////////////////////////// +// SpanOTA // +/////////////////////////////// + +void SpanOTA::start(){ + Serial.printf("\n*** Current Partition: %s\n*** New Partition: %s\n*** OTA Starting..", + esp_ota_get_running_partition()->label,esp_ota_get_next_update_partition(NULL)->label); + otaPercent=0; + homeSpan.statusLED.start(LED_OTA_STARTED); +} + +/////////////////////////////// + +void SpanOTA::end(){ + Serial.printf(" DONE! Rebooting...\n"); +// char newDesc[256]; + SpanPartition newSpanPartition; + esp_partition_read(esp_ota_get_next_update_partition(NULL), sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t), &newSpanPartition, sizeof(newSpanPartition)); + Serial.printf("Found: %s\n",newSpanPartition.magicCookie); + homeSpan.statusLED.off(); +} + +/////////////////////////////// + +void SpanOTA::progress(uint32_t progress, uint32_t total){ + int percent=progress*100/total; + if(percent/10 != otaPercent/10){ + otaPercent=percent; + Serial.printf("%d%%..",progress*100/total); + if(progress*100/total>20){ + Serial.println("BAD!!\n"); +// Update.abort(); + } + } +} + +/////////////////////////////// + +void SpanOTA::error(ota_error_t err){ + Serial.printf("*** OTA Error[%u]: ", err); + if (err == OTA_AUTH_ERROR) Serial.println("Auth Failed\n"); + else if (err == OTA_BEGIN_ERROR) Serial.println("Begin Failed\n"); + else if (err == OTA_CONNECT_ERROR) Serial.println("Connect Failed\n"); + else if (err == OTA_RECEIVE_ERROR) Serial.println("Receive Failed\n"); + else if (err == OTA_END_ERROR) Serial.println("End Failed\n"); +} + +/////////////////////////////// + +int SpanOTA::otaPercent; +boolean SpanOTA::verified; + + + diff --git a/src/HomeSpan.h b/src/HomeSpan.h index e7d9469..e8314b2 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -37,6 +37,7 @@ #include #include #include +#include #include "Settings.h" #include "Utils.h" @@ -71,13 +72,19 @@ struct SpanRange; struct SpanBuf; struct SpanButton; struct SpanUserCommand; -struct SpanWebLog; extern Span homeSpan; /////////////////////////////// -struct SpanConfig { +struct SpanPartition{ + char magicCookie[32]; + uint8_t reserved[224]; +}; + +/////////////////////////////// + +struct SpanConfig{ int configNumber=0; // configuration number - broadcast as Bonjour "c#" (computed automatically) uint8_t hashCode[48]={0}; // SHA-384 hash of Span Database stored as a form of unique "signature" to know when to update the config number upon changes }; @@ -120,6 +127,17 @@ struct SpanWebLog{ // optional web status/log data /////////////////////////////// +struct SpanOTA{ // manages OTA process + static int otaPercent; + static boolean verified; + static void start(); + static void end(); + static void progress(uint32_t progress, uint32_t total); + static void error(ota_error_t err); +}; + +/////////////////////////////// + struct Span{ const char *displayName; // display name for this device - broadcast as part of Bonjour MDNS @@ -158,6 +176,7 @@ struct Span{ boolean otaEnabled=false; // enables Over-the-Air ("OTA") updates char otaPwd[33]; // MD5 Hash of OTA password, represented as a string of hexidecimal characters boolean otaAuth; // OTA requires password when set to true + boolean otaDownload=false; // set to true in NVS if last download was via OTA void (*wifiCallback)()=NULL; // optional callback function to invoke once WiFi connectivity is established void (*pairCallback)(boolean isPaired)=NULL; // optional callback function to invoke when pairing is established (true) or lost (false) boolean autoStartAPEnabled=false; // enables auto start-up of Access Point when WiFi Credentials not found @@ -169,6 +188,7 @@ struct Span{ Network network; // configures WiFi and Setup Code via either serial monitor or temporary Access Point SpanWebLog webLog; // optional web status/log + SpanOTA spanOTA; // manages OTA process SpanConfig hapConfig; // track configuration changes to the HAP Accessory database; used to increment the configuration number (c#) when changes found vector Accessories; // vector of pointers to all Accessories vector Loops; // vector of pointer to all Services that have over-ridden loop() methods diff --git a/src/Settings.h b/src/Settings.h index ec43965..b424dcb 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -83,6 +83,11 @@ #define DEFAULT_WEBLOG_URL "status" // change with optional fourth argument in homeSpan.enableWebLog() +///////////////////////////////////////////////////// +// OTA PARTITION INFO // + +#define HOMESPAN_MAGIC_COOKIE "HomeSpanMagicCookie" + ///////////////////////////////////////////////////// // STATUS LED SETTINGS //