Adding Custom Magic Cookie to OTA Partition

Will prevent uploading a non-HomeSpan sketch via OTA to an existing HomeSpan device.
This commit is contained in:
Gregg 2022-03-12 08:24:01 -06:00
parent 7ddbfd55cc
commit 2f1044b013
5 changed files with 149 additions and 36 deletions

View File

@ -41,6 +41,7 @@ void HAPClient::init(){
nvs_open("SRP",NVS_READWRITE,&srpNVS); // open SRP 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("HAP",NVS_READWRITE,&hapNVS); // open HAP data namespace in NVS
nvs_open("OTA",NVS_READWRITE,&otaNVS); // open OTA 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 if(!nvs_get_str(otaNVS,"OTADATA",NULL,&len)){ // if found OTA data in NVS
nvs_get_str(otaNVS,"OTADATA",homeSpan.otaPwd,&len); // retrieve data nvs_get_str(otaNVS,"OTADATA",homeSpan.otaPwd,&len); // retrieve data
@ -1750,6 +1751,7 @@ TLV<kTLVType,10> HAPClient::tlv8;
nvs_handle HAPClient::hapNVS; nvs_handle HAPClient::hapNVS;
nvs_handle HAPClient::srpNVS; nvs_handle HAPClient::srpNVS;
nvs_handle HAPClient::otaNVS; nvs_handle HAPClient::otaNVS;
nvs_handle HAPClient::stateNVS;
uint8_t HAPClient::httpBuf[MAX_HTTP+1]; uint8_t HAPClient::httpBuf[MAX_HTTP+1];
HKDF HAPClient::hkdf; HKDF HAPClient::hkdf;
pairState HAPClient::pairStatus; pairState HAPClient::pairStatus;

View File

@ -81,6 +81,7 @@ struct HAPClient {
static nvs_handle hapNVS; // handle for non-volatile-storage of HAP data 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 srpNVS; // handle for non-volatile-storage of SRP data
static nvs_handle otaNVS; // handle for non-volatile-storage of OTA 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 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 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 static pairState pairStatus; // tracks pair-setup status

View File

@ -29,22 +29,22 @@
#include <nvs_flash.h> #include <nvs_flash.h>
#include <sodium.h> #include <sodium.h>
#include <WiFi.h> #include <WiFi.h>
#include <ArduinoOTA.h>
#include <esp_ota_ops.h>
#include <driver/ledc.h> #include <driver/ledc.h>
#include <mbedtls/version.h> #include <mbedtls/version.h>
#include <esp_task_wdt.h> #include <esp_task_wdt.h>
#include <esp_sntp.h> #include <esp_sntp.h>
#include <esp_ota_ops.h>
#include "HomeSpan.h" #include "HomeSpan.h"
#include "HAP.h" #include "HAP.h"
const __attribute__((section(".rodata_custom_desc"))) SpanPartition spanPartition = {HOMESPAN_MAGIC_COOKIE};
using namespace Utils; using namespace Utils;
HAPClient **hap; // HAP Client structure containing HTTP client connections, parsing routines, and state variables (global-scoped variable) 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) 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) 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 // // Span //
@ -135,11 +135,35 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa
Serial.print(__DATE__); Serial.print(__DATE__);
Serial.print(" "); Serial.print(" ");
Serial.print(__TIME__); 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("\n\nDevice Name: ");
Serial.print(displayName); Serial.print(displayName);
Serial.print("\n\n"); Serial.print("\n\n");
} // begin } // begin
/////////////////////////////// ///////////////////////////////
@ -524,42 +548,48 @@ void Span::checkConnect(){
if(otaAuth) if(otaAuth)
ArduinoOTA.setPasswordHash(otaPwd); ArduinoOTA.setPasswordHash(otaPwd);
ArduinoOTA.onStart(spanOTA.start).onEnd(spanOTA.end).onProgress(spanOTA.progress).onError(spanOTA.error);
ArduinoOTA // ArduinoOTA
.onStart([]() { // .onStart([]() {
Serial.printf("\n*** Current Partition: %s\n*** New Partition: %s\n*** OTA Starting..", // 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); // esp_ota_get_running_partition()->label,esp_ota_get_next_update_partition(NULL)->label);
otaPercent=-10; // otaPercent=-10;
homeSpan.statusLED.start(LED_OTA_STARTED); // homeSpan.statusLED.start(LED_OTA_STARTED);
}) // })
.onEnd([]() { // .onEnd([]() {
Serial.printf(" DONE! Rebooting...\n"); // Serial.printf(" DONE! Rebooting...\n");
homeSpan.statusLED.off(); // 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));
.onProgress([](unsigned int progress, unsigned int total) { // Serial.printf("Found: %s\n",newDesc);
int percent=progress*100/total; // homeSpan.statusLED.off();
if(percent/10 != otaPercent/10){ // })
otaPercent=percent; // .onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("%d%%..",progress*100/total); // int percent=progress*100/total;
} // if(percent/10 != otaPercent/10){
}) // otaPercent=percent;
.onError([](ota_error_t error) { // Serial.printf("%d%%..",progress*100/total);
Serial.printf("*** OTA Error[%u]: ", error); // if(progress*100/total>20){
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed\n"); // Serial.println("BAD!!\n");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed\n"); // Update.abort();
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"); // })
}); // .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(); ArduinoOTA.begin();
Serial.print("Starting OTA Server: "); Serial.print("Starting OTA Server: ");
Serial.print(displayName); Serial.print(displayName);
Serial.print(" at "); Serial.print(" at ");
Serial.print(WiFi.localIP()); 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("\nAuthorization Password: ");
Serial.print(otaAuth?"Enabled\n\n":"DISABLED!\n\n"); Serial.print(otaAuth?"Enabled\n\n":"DISABLED!\n\n");
} else { } else {
@ -1996,3 +2026,58 @@ void SpanWebLog::addLog(const char *fmt, ...){
if(homeSpan.logLevel>0) if(homeSpan.logLevel>0)
Serial.printf("WEBLOG: %s\n",log[index].message); 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;

View File

@ -37,6 +37,7 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <nvs.h> #include <nvs.h>
#include <ArduinoOTA.h>
#include "Settings.h" #include "Settings.h"
#include "Utils.h" #include "Utils.h"
@ -71,13 +72,19 @@ struct SpanRange;
struct SpanBuf; struct SpanBuf;
struct SpanButton; struct SpanButton;
struct SpanUserCommand; struct SpanUserCommand;
struct SpanWebLog;
extern Span homeSpan; 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) 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 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{ struct Span{
const char *displayName; // display name for this device - broadcast as part of Bonjour MDNS 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 boolean otaEnabled=false; // enables Over-the-Air ("OTA") updates
char otaPwd[33]; // MD5 Hash of OTA password, represented as a string of hexidecimal characters 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 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 (*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) 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 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 Network network; // configures WiFi and Setup Code via either serial monitor or temporary Access Point
SpanWebLog webLog; // optional web status/log 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 SpanConfig hapConfig; // track configuration changes to the HAP Accessory database; used to increment the configuration number (c#) when changes found
vector<SpanAccessory *> Accessories; // vector of pointers to all Accessories vector<SpanAccessory *> Accessories; // vector of pointers to all Accessories
vector<SpanService *> Loops; // vector of pointer to all Services that have over-ridden loop() methods vector<SpanService *> Loops; // vector of pointer to all Services that have over-ridden loop() methods

View File

@ -83,6 +83,11 @@
#define DEFAULT_WEBLOG_URL "status" // change with optional fourth argument in homeSpan.enableWebLog() #define DEFAULT_WEBLOG_URL "status" // change with optional fourth argument in homeSpan.enableWebLog()
/////////////////////////////////////////////////////
// OTA PARTITION INFO //
#define HOMESPAN_MAGIC_COOKIE "HomeSpanMagicCookie"
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
// STATUS LED SETTINGS // // STATUS LED SETTINGS //