diff --git a/src/HAP.cpp b/src/HAP.cpp index 4f4d645..bf2a963 100644 --- a/src/HAP.cpp +++ b/src/HAP.cpp @@ -851,7 +851,6 @@ int HAPClient::getAccessoriesURL(){ unauthorizedError(); return(0); } - LOG1("In Get Accessories #"); LOG1(conNum); LOG1(" ("); @@ -859,22 +858,27 @@ int HAPClient::getAccessoriesURL(){ LOG1(")...\n"); int nBytes = homeSpan.sprintfAttributes(NULL); // get size of HAP attributes JSON - TempBuffer jBuf(nBytes+1,"HAPClient::getAccessoriesURL"); - homeSpan.sprintfAttributes(jBuf.get()); // create JSON database (will need to re-cast to uint8_t* below) - char *body; - asprintf(&body,"HTTP/1.1 200 OK\r\nContent-Type: application/hap+json\r\nContent-Length: %d\r\n\r\n",nBytes); + // TempBuffer jBuf(nBytes+1,"HAPClient::getAccessoriesURL"); + // homeSpan.sprintfAttributes(jBuf.get()); // create JSON database (will need to re-cast to uint8_t* below) + + // char *body; + // asprintf(&body,"HTTP/1.1 200 OK\r\nContent-Type: application/hap+json\r\nContent-Length: %d\r\n\r\n",nBytes); - LOG2("\n>>>>>>>>>> "); - LOG2(client.remoteIP()); - LOG2(" >>>>>>>>>>\n"); - LOG2(body); - LOG2(jBuf.get()); - LOG2("\n"); + // LOG2("\n>>>>>>>>>> "); + // LOG2(client.remoteIP()); + // LOG2(" >>>>>>>>>>\n"); + // LOG2(body); + // LOG2(jBuf.get()); + // LOG2("\n"); - sendEncrypted(body,(uint8_t *)jBuf.get(),nBytes); - free(body); - + // sendEncrypted(body,(uint8_t *)jBuf.get(),nBytes); + // free(body); + + + SendEncryptedContext context(*this); + context.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); return(1); } // getAccessories diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index c510b59..2c03347 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -39,6 +39,7 @@ #include "HomeSpan.h" #include "HAP.h" +#include "SendEncryptedContext.h" const __attribute__((section(".rodata_custom_desc"))) SpanPartition spanPartition = {HOMESPAN_MAGIC_COOKIE,0}; @@ -1227,19 +1228,33 @@ Span& Span::setWifiCredentials(const char *ssid, const char *pwd){ /////////////////////////////// -int Span::sprintfAttributes(char *cBuf, int flags){ +int Span::sprintfAttributes(char *cBuf, int flags, SendEncryptedContext* sendEncryptedContext){ int nBytes=0; nBytes+=snprintf(cBuf,cBuf?64:0,"{\"accessories\":["); - + if (sendEncryptedContext != NULL) + sendEncryptedContext->print("{\"accessories\":["); + for(int i=0;isprintfAttributes(cBuf?(cBuf+nBytes):NULL,flags); - if(i+1sprintfAttributes(cBuf?(cBuf+nBytes):NULL,flags); + nBytes+= accessoryBytes; + if (sendEncryptedContext != NULL) + { + char* buffer = sendEncryptedContext->reserveStringBuffer(accessoryBytes); + accessory->sprintfAttributes(buffer, flags); } - + if(i+1print(","); + } + } nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?64:0,"]}"); + if (sendEncryptedContext != NULL) + sendEncryptedContext->print("]}"); return(nBytes); } diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 7522a56..f92d841 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -48,6 +48,7 @@ #include "extras/Pixel.h" #include "Settings.h" #include "Utils.h" +#include "SendEncryptedContext.h" #include "Network.h" #include "HAPConstants.h" #include "HapQR.h" @@ -268,7 +269,7 @@ class Span{ void resetStatus(); // resets statusLED and calls statusCallback based on current HomeSpan status void reboot(); // reboots device - int sprintfAttributes(char *cBuf, int flags=GET_VALUE|GET_META|GET_PERMS|GET_TYPE|GET_DESC); // 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, SendEncryptedContext* sendEncryptedContext = 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 SpanCharacteristic *find(uint32_t aid, int iid); // return Characteristic with matching aid and iid (else NULL if not found) diff --git a/src/SendEncryptedContext.cpp b/src/SendEncryptedContext.cpp new file mode 100644 index 0000000..f57d8d9 --- /dev/null +++ b/src/SendEncryptedContext.cpp @@ -0,0 +1,116 @@ +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020-2023 Michael Geramb + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + +#include +#include "SendEncryptedContext.h" +#include "HomeSpan.h" +#include "HAP.h" +#include "Settings.h" +#include + +SendEncryptedContext::SendEncryptedContext(HAPClient& hapClient) +: hapClient(hapClient), + 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, ...) +{ + va_list ap; + va_start(ap, format); + int len = vsnprintf(NULL, 0, format, ap); + char* buffer = reserveStringBuffer(len); + vsnprintf(buffer, len + 1, format, ap); // the reserved buffer is one byte larger because it contains the terminating '\0' + LOG2(buffer); + 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) +{ + int requiredNewBufferLength = stringLength + 1; // reserve buffer for characters and string terminator '\0' + if (stringBuffer.len() - usedStringLength < requiredNewBufferLength) + { + // to less memory, send first exitsting buffer + if (usedStringLength > 0) + { + sendEncrypted(stringBuffer.get(), usedStringLength); + usedStringLength = 0; + } + // Check if still more memory is needed + if (stringBuffer.len() < requiredNewBufferLength) + stringBuffer = TempBuffer(requiredNewBufferLength, "SendEncryptedContext::reserveMemory"); + } + int alreadyInUse = usedStringLength; + usedStringLength += stringLength; + char* result = stringBuffer.get() + alreadyInUse; + result[stringLength] = '\0'; // add string terminator + return result; +} + +void SendEncryptedContext::sendEncrypted(const char* buffer, int length) +{ + if (length == 0) + return; + sentLength += length; + LOG2(buffer); + + for(int i=0;iFRAME_SIZE) // maximum number of bytes to encrypt=FRAME_SIZE + n=FRAME_SIZE; + + sendBuffer.get()[0]=n%256; // store number of bytes that encrypts this frame (AAD bytes) + sendBuffer.get()[1]=n/256; + + unsigned long long nBytes; + crypto_aead_chacha20poly1305_ietf_encrypt(sendBuffer.get()+2,&nBytes, (uint8_t*) buffer+i,n,sendBuffer.get(),2,NULL,hapClient.a2cNonce.get(),hapClient.a2cKey); // encrypt the next portion of dataBuf with authentication tag appended + + hapClient.client.write(sendBuffer.get(),nBytes+2); // transmit encrypted frame + hapClient.a2cNonce.inc(); // increment nonce + } +} + +SendEncryptedContext::~SendEncryptedContext() +{ + if (usedStringLength > 0) + sendEncrypted(stringBuffer.get(), usedStringLength); + LOG2("-------- SENT ENCRYPTED! --------\n"); +} diff --git a/src/SendEncryptedContext.h b/src/SendEncryptedContext.h new file mode 100644 index 0000000..082c314 --- /dev/null +++ b/src/SendEncryptedContext.h @@ -0,0 +1,56 @@ +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020-2023 Michael Geramb + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + +#pragma once +#include "Utils.h" + +struct HAPClient; + + +///////////////////////////////////////////////// +// 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 + + TempBuffer stringBuffer; + int usedStringLength = 0; + int sentLength = 0; + HAPClient& hapClient; + TempBuffer sendBuffer; +public: + SendEncryptedContext(HAPClient& hapClient); + ~SendEncryptedContext(); + + void printf(const char* format, ...); + void print(const char* text); + char* reserveStringBuffer(int stringLength); +private: + void sendEncrypted(const char* buffer, int length); +}; \ No newline at end of file