Add handling for updateDatabase() in call context

This commit is contained in:
Michael Geramb 2023-11-12 19:38:19 +01:00
parent 3cd8f8469a
commit 1569019fab
5 changed files with 128 additions and 62 deletions

View File

@ -26,23 +26,18 @@
********************************************************************************/ ********************************************************************************/
#include <Arduino.h> #include <Arduino.h>
#include "SendEncryptedContext.h" #include "CallContext.h"
#include "HomeSpan.h" #include "HomeSpan.h"
#include "HAP.h" #include "HAP.h"
#include "Settings.h" #include "Settings.h"
#include <sodium.h> #include <sodium.h>
SendEncryptedContext::SendEncryptedContext(HAPClient& hapClient) CallContext::CallContext()
: hapClient(hapClient), : stringBuffer(1024, "CallContext stringBuffer")
stringBuffer(FRAME_SIZE + 1, "SendEncryptedContext dataBuffer"),
sendBuffer(FRAME_SIZE + 2 + 16, "SendEncryptedContext sendBuffer") // FRAME_SIZE + AAD + Auth
{ {
LOG2("\n>>>>>>>>>> ");
LOG2(hapClient.client.remoteIP());
LOG2(" >>>>>>>>>>\n");
} }
void SendEncryptedContext::printf(const char* format, ...) void CallContext::printf(const char* format, ...)
{ {
va_list ap; va_list ap;
va_start(ap, format); va_start(ap, format);
@ -53,28 +48,19 @@ void SendEncryptedContext::printf(const char* format, ...)
va_end(ap); va_end(ap);
} }
void SendEncryptedContext::print(const char* text)
{
auto len = strlen(text);
auto buffer = reserveStringBuffer(len);
memcpy(buffer, text, len);
LOG2(buffer);
}
char* SendEncryptedContext::reserveStringBuffer(int stringLength) char* CallContext::reserveStringBuffer(int stringLength)
{ {
int requiredNewBufferLength = stringLength + 1; // reserve buffer for characters and string terminator '\0' int requiredNewBufferLength = stringLength + 1; // reserve buffer for characters and string terminator '\0'
if (stringBuffer.len() - usedStringLength < requiredNewBufferLength) if (stringBuffer.len() - usedStringLength < requiredNewBufferLength)
{ {
// to less memory, send first exitsting buffer // to less memory, send first exitsting buffer
if (usedStringLength > 0) if (usedStringLength > 0)
{ flush();
sendEncrypted(stringBuffer.get(), usedStringLength);
usedStringLength = 0;
}
// Check if still more memory is needed // Check if still more memory is needed
if (stringBuffer.len() < requiredNewBufferLength) if (stringBuffer.len() < requiredNewBufferLength)
stringBuffer = TempBuffer<char>(requiredNewBufferLength, "SendEncryptedContext::reserveMemory"); stringBuffer = TempBuffer<char>(requiredNewBufferLength, "SendEncryptedCallContext::reserveMemory");
} }
int alreadyInUse = usedStringLength; int alreadyInUse = usedStringLength;
usedStringLength += stringLength; usedStringLength += stringLength;
@ -83,11 +69,35 @@ char* SendEncryptedContext::reserveStringBuffer(int stringLength)
return result; return result;
} }
void SendEncryptedContext::sendEncrypted(const char* buffer, int length) void CallContext::flush()
{
handlePage(stringBuffer.get(), usedStringLength);
usedStringLength = 0;
}
void CallContext::print(const char* text)
{
auto len = strlen(text);
auto buffer = reserveStringBuffer(len);
memcpy(buffer, text, len);
LOG2(buffer);
}
SendEncryptedCallContext::SendEncryptedCallContext(HAPClient& hapClient)
: hapClient(hapClient),
sendBuffer(FRAME_SIZE + 2 + 16, "SendEncryptedCallContext sendBuffer") // FRAME_SIZE + AAD + Auth
{
LOG2("\n>>>>>>>>>> ");
LOG2(hapClient.client.remoteIP());
LOG2(" >>>>>>>>>>\n");
}
void SendEncryptedCallContext::handlePage(const char* buffer, int length)
{ {
if (length == 0) if (length == 0)
return; return;
sentLength += length;
LOG2(buffer); LOG2(buffer);
for(int i=0;i<length;i+=FRAME_SIZE){ // encrypt FRAME_SIZE number of bytes in dataBuf in sequential frames for(int i=0;i<length;i+=FRAME_SIZE){ // encrypt FRAME_SIZE number of bytes in dataBuf in sequential frames
@ -108,9 +118,30 @@ void SendEncryptedContext::sendEncrypted(const char* buffer, int length)
} }
} }
SendEncryptedContext::~SendEncryptedContext() SendEncryptedCallContext::~SendEncryptedCallContext()
{ {
if (usedStringLength > 0) flush();
sendEncrypted(stringBuffer.get(), usedStringLength);
LOG2("-------- SENT ENCRYPTED! --------\n"); LOG2("-------- SENT ENCRYPTED! --------\n");
} }
CalcHashCallContext::CalcHashCallContext()
: CallContext()
{
memset(&tHash, 0, HASH_SIZE);
}
void CalcHashCallContext::handlePage(const char* buffer, int length)
{
uint8_t newHash[HASH_SIZE];
// create SHA-384 hash of JSON and xor with previous part (can be any hash - just looking for a unique key)
mbedtls_sha512_ret((uint8_t *)buffer,length, newHash,1);
for (int i=0; i<48; i++)
tHash[i] = tHash[i] ^ newHash[i];
}
const uint8_t* CalcHashCallContext::getHashCode()
{
flush();
return tHash;
}

View File

@ -30,27 +30,57 @@
struct HAPClient; struct HAPClient;
/////////////////////////////////////////////////////////////////////////////////
// CallContext class
// Store strings off a call and call handlePage after the page limit was reached
///////////////////////////////////////////////// class CallContext
// SendEncryptedContext class
// Write encrypted data as response to a web call
class SendEncryptedContext
{ {
const int FRAME_SIZE=1024; // number of bytes to use in each ChaCha20-Poly1305 encrypted frame when sending encrypted JSON content to Client private:
TempBuffer<char> stringBuffer; TempBuffer<char> stringBuffer;
int usedStringLength = 0; int usedStringLength = 0;
int sentLength = 0;
HAPClient& hapClient;
TempBuffer<uint8_t> sendBuffer;
public: public:
SendEncryptedContext(HAPClient& hapClient); CallContext();
~SendEncryptedContext();
void printf(const char* format, ...); void printf(const char* format, ...);
void print(const char* text); void print(const char* text);
char* reserveStringBuffer(int stringLength); char* reserveStringBuffer(int stringLength);
private: void flush();
void sendEncrypted(const char* buffer, int length); protected:
virtual void handlePage(const char* buffer, int length) = 0;
};
/////////////////////////////////////////////////////////////////////////////////
// SendEncryptedCallContext class
// Sends the pages encrypted to the network
class SendEncryptedCallContext : public CallContext
{
static const int FRAME_SIZE=1024; // number of bytes to use in each ChaCha20-Poly1305 encrypted frame when sending encrypted JSON content to Client
HAPClient& hapClient;
TempBuffer<uint8_t> sendBuffer;
public:
SendEncryptedCallContext(HAPClient& hapClient);
~SendEncryptedCallContext();
private:
virtual void handlePage(const char* buffer, int length) override;
};
/////////////////////////////////////////////////////////////////////////////////
// CalcHashCallContext class
// Calc an hash code out of the provided strings
class CalcHashCallContext : public CallContext
{
public:
static const int HASH_SIZE=48;
private:
uint8_t tHash[CalcHashCallContext::HASH_SIZE];
public:
CalcHashCallContext();
const uint8_t* getHashCode();
protected:
void handlePage(const char* buffer, int length) override;
}; };

View File

@ -876,9 +876,9 @@ int HAPClient::getAccessoriesURL(){
// free(body); // free(body);
SendEncryptedContext context(*this); SendEncryptedCallContext callContext(*this);
context.printf("HTTP/1.1 200 OK\r\nContent-Type: application/hap+json\r\nContent-Length: %d\r\n\r\n",nBytes); callContext.printf("HTTP/1.1 200 OK\r\nContent-Type: application/hap+json\r\nContent-Length: %d\r\n\r\n",nBytes);
homeSpan.sprintfAttributes(NULL, GET_VALUE|GET_META|GET_PERMS|GET_TYPE|GET_DESC, &context); homeSpan.sprintfAttributes(NULL, GET_VALUE|GET_META|GET_PERMS|GET_TYPE|GET_DESC, &callContext);
return(1); return(1);
} // getAccessories } // getAccessories

View File

@ -39,7 +39,7 @@
#include "HomeSpan.h" #include "HomeSpan.h"
#include "HAP.h" #include "HAP.h"
#include "SendEncryptedContext.h" #include "CallContext.h"
const __attribute__((section(".rodata_custom_desc"))) SpanPartition spanPartition = {HOMESPAN_MAGIC_COOKIE,0}; const __attribute__((section(".rodata_custom_desc"))) SpanPartition spanPartition = {HOMESPAN_MAGIC_COOKIE,0};
@ -1228,33 +1228,33 @@ Span& Span::setWifiCredentials(const char *ssid, const char *pwd){
/////////////////////////////// ///////////////////////////////
int Span::sprintfAttributes(char *cBuf, int flags, SendEncryptedContext* sendEncryptedContext){ int Span::sprintfAttributes(char *cBuf, int flags, CallContext* callContext){
int nBytes=0; int nBytes=0;
nBytes+=snprintf(cBuf,cBuf?64:0,"{\"accessories\":["); nBytes+=snprintf(cBuf,cBuf?64:0,"{\"accessories\":[");
if (sendEncryptedContext != NULL) if (callContext != NULL)
sendEncryptedContext->print("{\"accessories\":["); callContext->print("{\"accessories\":[");
for(int i=0;i<Accessories.size();i++){ for(int i=0;i<Accessories.size();i++){
SpanAccessory* accessory=Accessories[i]; SpanAccessory* accessory=Accessories[i];
int accessoryBytes=accessory->sprintfAttributes(cBuf?(cBuf+nBytes):NULL,flags); int accessoryBytes=accessory->sprintfAttributes(cBuf?(cBuf+nBytes):NULL,flags);
nBytes+= accessoryBytes; nBytes+= accessoryBytes;
if (sendEncryptedContext != NULL) if (callContext != NULL)
{ {
char* buffer = sendEncryptedContext->reserveStringBuffer(accessoryBytes); char* buffer = callContext->reserveStringBuffer(accessoryBytes);
accessory->sprintfAttributes(buffer, flags); accessory->sprintfAttributes(buffer, flags);
} }
if(i+1<Accessories.size()) if(i+1<Accessories.size())
{ {
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,","); nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,",");
if (sendEncryptedContext != NULL) if (callContext != NULL)
sendEncryptedContext->print(","); callContext->print(",");
} }
} }
nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,"]}"); nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,"]}");
if (sendEncryptedContext != NULL) if (callContext != NULL)
sendEncryptedContext->print("]}"); callContext->print("]}");
return(nBytes); return(nBytes);
} }
@ -1606,15 +1606,20 @@ int Span::sprintfAttributes(char **ids, int numIDs, int flags, char *cBuf){
boolean Span::updateDatabase(boolean updateMDNS){ boolean Span::updateDatabase(boolean updateMDNS){
uint8_t tHash[48]; // uint8_t tHash[48];
TempBuffer <char> tBuf(sprintfAttributes(NULL,GET_META|GET_PERMS|GET_TYPE|GET_DESC)+1, "Span::updateDatabase"); // TempBuffer <char> tBuf(sprintfAttributes(NULL,GET_META|GET_PERMS|GET_TYPE|GET_DESC)+1, "Span::updateDatabase");
sprintfAttributes(tBuf.get(),GET_META|GET_PERMS|GET_TYPE|GET_DESC); // sprintfAttributes(tBuf.get(),GET_META|GET_PERMS|GET_TYPE|GET_DESC);
mbedtls_sha512_ret((uint8_t *)tBuf.get(),tBuf.len(),tHash,1); // create SHA-384 hash of JSON (can be any hash - just looking for a unique key) // mbedtls_sha512_ret((uint8_t *)tBuf.get(),tBuf.len(),tHash,1); // create SHA-384 hash of JSON (can be any hash - just looking for a unique key)
CalcHashCallContext callContext = CalcHashCallContext();
sprintfAttributes(NULL ,GET_META|GET_PERMS|GET_TYPE|GET_DESC, &callContext);
const uint8_t* hash = callContext.getHashCode();
boolean changed=false; boolean changed=false;
if(memcmp(tHash,hapConfig.hashCode,48)){ // if hash code of current HAP database does not match stored hash code if(memcmp(hash,hapConfig.hashCode,CalcHashCallContext::HASH_SIZE)){ // if hash code of current HAP database does not match stored hash code
memcpy(hapConfig.hashCode,tHash,48); // update stored hash code memcpy(hapConfig.hashCode,hash,CalcHashCallContext::HASH_SIZE); // update stored hash code
hapConfig.configNumber++; // increment configuration number hapConfig.configNumber++; // increment configuration number
if(hapConfig.configNumber==65536) // reached max value if(hapConfig.configNumber==65536) // reached max value
hapConfig.configNumber=1; // reset to 1 hapConfig.configNumber=1; // reset to 1

View File

@ -48,7 +48,7 @@
#include "extras/Pixel.h" #include "extras/Pixel.h"
#include "Settings.h" #include "Settings.h"
#include "Utils.h" #include "Utils.h"
#include "SendEncryptedContext.h" #include "CallContext.h"
#include "Network.h" #include "Network.h"
#include "HAPConstants.h" #include "HAPConstants.h"
#include "HapQR.h" #include "HapQR.h"
@ -269,7 +269,7 @@ class Span{
void resetStatus(); // resets statusLED and calls statusCallback based on current HomeSpan status void resetStatus(); // resets statusLED and calls statusCallback based on current HomeSpan status
void reboot(); // reboots device void reboot(); // reboots device
int sprintfAttributes(char *cBuf, int flags=GET_VALUE|GET_META|GET_PERMS|GET_TYPE|GET_DESC, SendEncryptedContext* sendEncryptedContext = nullptr); // prints Attributes JSON database into buf, unless buf=NULL; return number of characters printed, excluding null terminator int sprintfAttributes(char *cBuf, int flags=GET_VALUE|GET_META|GET_PERMS|GET_TYPE|GET_DESC, CallContext* callContext = nullptr); // prints Attributes JSON database into buf, unless buf=NULL; return number of characters printed, excluding null terminator
void prettyPrint(char *buf, int nsp=2, int minLogLevel=0); // print arbitrary JSON from buf to serial monitor, formatted with indentions of 'nsp' spaces, subject to specified minimum log level void prettyPrint(char *buf, int nsp=2, int minLogLevel=0); // print arbitrary JSON from buf to serial monitor, formatted with indentions of 'nsp' spaces, subject to specified minimum log level
SpanCharacteristic *find(uint32_t aid, int iid); // return Characteristic with matching aid and iid (else NULL if not found) SpanCharacteristic *find(uint32_t aid, int iid); // return Characteristic with matching aid and iid (else NULL if not found)