From a84429f930858e321da45dcce3276fcfea49bdc3 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 30 Jul 2023 21:37:47 -0500 Subject: [PATCH] Refactored TLV * Added support for zero-length TLV * Added SEPARATOR as a formal kTLVType (and updated listControllers() to use) * Added `uint8_t *buf(tagType tag, uint8_t *src, int len);` to load buffer needing external memcpy (and updated listControllers() to use) --- src/HAP.cpp | 97 +++++++++++++++++++++++++++++------------------- src/HAP.h | 4 +- src/HomeSpan.cpp | 8 ++++ src/HomeSpan.h | 2 + src/TLV.h | 77 +++++++++++++++++++++++--------------- src/Utils.h | 9 ++++- 6 files changed, 126 insertions(+), 71 deletions(-) diff --git a/src/HAP.cpp b/src/HAP.cpp index 2e5c51a..57d474a 100644 --- a/src/HAP.cpp +++ b/src/HAP.cpp @@ -106,6 +106,16 @@ void HAPClient::init(){ nvs_commit(hapNVS); // commit to NVS } + if(!nvs_get_blob(hapNVS,"CONTROLLERS",NULL,&len)){ // if found long-term Controller Pairings data from NVS + TempBuffer tBuf(len/sizeof(Controller)); + nvs_get_blob(hapNVS,"CONTROLLERS",tBuf.get(),&len); // retrieve data + Serial.printf("*** SIZE %d ***\n",tBuf.size()); + for(int i=0;i @@ -526,7 +537,7 @@ int HAPClient::postPairSetupURL(){ tlv8.print(2); // print decrypted TLV data LOG2("------- END DECRYPTED TLVS! -------\n"); - if(!tlv8.buf(kTLVType_Identifier) || !tlv8.buf(kTLVType_PublicKey) || !tlv8.buf(kTLVType_Signature)){ + if(!tlv8.len(kTLVType_Identifier) || !tlv8.len(kTLVType_PublicKey) || !tlv8.len(kTLVType_Signature)){ LOG0("\n*** ERROR: One or more of required 'Identifier,' 'PublicKey,' and 'Signature' TLV records for this step is bad or missing\n\n"); tlv8.clear(); // clear TLV records tlv8.val(kTLVType_State,pairState_M6); // set State= @@ -679,7 +690,7 @@ int HAPClient::postPairVerifyURL(){ case pairState_M1: // 'Verify Start Request' - if(!tlv8.buf(kTLVType_PublicKey)){ + if(!tlv8.len(kTLVType_PublicKey)){ LOG0("\n*** ERROR: Required 'PublicKey' TLV record for this step is bad or missing\n\n"); tlv8.clear(); // clear TLV records tlv8.val(kTLVType_State,pairState_M2); // set State= @@ -745,7 +756,7 @@ int HAPClient::postPairVerifyURL(){ case pairState_M3: // 'Verify Finish Request' - if(!tlv8.buf(kTLVType_EncryptedData)){ + if(!tlv8.len(kTLVType_EncryptedData)){ LOG0("\n*** ERROR: Required 'EncryptedData' TLV record for this step is bad or missing\n\n"); tlv8.clear(); // clear TLV records tlv8.val(kTLVType_State,pairState_M4); // set State= @@ -782,7 +793,7 @@ int HAPClient::postPairVerifyURL(){ tlv8.print(2); // print decrypted TLV data LOG2("------- END DECRYPTED TLVS! -------\n"); - if(!tlv8.buf(kTLVType_Identifier) || !tlv8.buf(kTLVType_Signature)){ + if(!tlv8.len(kTLVType_Identifier) || !tlv8.len(kTLVType_Signature)){ LOG0("\n*** ERROR: One or more of required 'Identifier,' and 'Signature' TLV records for this step is bad or missing\n\n"); tlv8.clear(); // clear TLV records tlv8.val(kTLVType_State,pairState_M4); // set State= @@ -905,7 +916,7 @@ int HAPClient::postPairingsURL(){ case 3: LOG1("Add...\n"); - if(!tlv8.buf(kTLVType_Identifier) || !tlv8.buf(kTLVType_PublicKey) || !tlv8.buf(kTLVType_Permissions)){ + if(!tlv8.len(kTLVType_Identifier) || !tlv8.len(kTLVType_PublicKey) || !tlv8.len(kTLVType_Permissions)){ LOG0("\n*** ERROR: One or more of required 'Identifier,' 'PublicKey,' and 'Permissions' TLV records for this step is bad or missing\n\n"); tlv8.clear(); // clear TLV records tlv8.val(kTLVType_State,pairState_M2); // set State= @@ -944,7 +955,7 @@ int HAPClient::postPairingsURL(){ case 4: LOG1("Remove...\n"); - if(!tlv8.buf(kTLVType_Identifier)){ + if(!tlv8.len(kTLVType_Identifier)){ LOG0("\n*** ERROR: Required 'Identifier' TLV record for this step is bad or missing\n\n"); tlv8.clear(); // clear TLV records tlv8.val(kTLVType_State,pairState_M2); // set State= @@ -1683,21 +1694,13 @@ int HAPClient::listControllers(uint8_t *tlvBuf){ tlv8.clear(); tlv8.val(kTLVType_State,pairState_M2); - for(int i=0;i tBuf(controllerList.size()); // create temporary buffer to hold Controller data + + for(auto it=controllerList.begin();it!=controllerList.end();it++) // store Controller data in temporary buffer + tBuf.get()[n++]=(*it); + + nvs_set_blob(hapNVS,"CONTROLLERS",tBuf.get(),tBuf.len()); // update data + nvs_commit(hapNVS); // commit to NVS } + ////////////////////////////////////// Nonce::Nonce(){ @@ -1766,13 +1786,14 @@ void Nonce::inc(){ // instantiate all static HAP Client structures and data -TLV HAPClient::tlv8; +TLV HAPClient::tlv8; nvs_handle HAPClient::hapNVS; nvs_handle HAPClient::srpNVS; HKDF HAPClient::hkdf; pairState HAPClient::pairStatus; Accessory HAPClient::accessory; -Controller HAPClient::controllers[MAX_CONTROLLERS]; +Controller HAPClient::controllers[MAX_CONTROLLERS]; +list HAPClient::controllerList; SRP6A HAPClient::srp; int HAPClient::conNum; diff --git a/src/HAP.h b/src/HAP.h index d46ea63..0c7cedd 100644 --- a/src/HAP.h +++ b/src/HAP.h @@ -77,7 +77,7 @@ struct HAPClient { static const int MAX_CONTROLLERS=16; // maximum number of paired controllers (HAP requires at least 16) static const int MAX_ACCESSORIES=41; // maximum number of allowed Acessories (HAP limit=150, but not enough memory in ESP32 to run that many) - static TLV tlv8; // TLV8 structure (HAP Section 14.1) with space for 10 TLV records of type kTLVType (HAP Table 5-6) + static TLV tlv8; // TLV8 structure (HAP Section 14.1) with space for 11 TLV records of type kTLVType (HAP Table 5-6) 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 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 @@ -85,6 +85,7 @@ struct HAPClient { static SRP6A srp; // stores all SRP-6A keys used for Pair-Setup static Accessory accessory; // Accessory ID and Ed25519 public and secret keys- permanently stored static Controller controllers[MAX_CONTROLLERS]; // Paired Controller IDs and ED25519 long-term public keys - permanently stored + static list controllerList; // linked-list of Paired Controller IDs and ED25519 long-term public keys - permanently stored static int conNum; // connection number - used to keep track of per-connection EV notifications // individual structures and data defined for each Hap Client connection @@ -142,6 +143,7 @@ struct HAPClient { static void removeController(uint8_t *id); // removes specific Controller. If no remaining admin Controllers, remove all others (if any) as per HAP requirements. static void printControllers(int minLogLevel=0); // prints IDs of all allocated (paired) Controller, subject to specified minimum log level static int listControllers(uint8_t *tlvBuf); // creates and prints a multi-TLV list of Controllers (HAP Section 5.12) + static void saveControllers(); // saves Controller list in NVS static void checkNotifications(); // checks for Event Notifications and reports to controllers as needed (HAP Section 6.8) static void checkTimedWrites(); // checks for expired Timed Write PIDs, and clears any found (HAP Section 6.7.2.4) static void eventNotify(SpanBuf *pObj, int nObj, int ignoreClient=-1); // transmits EVENT Notifications for nObj SpanBuf objects, pObj, with optional flag to ignore a specific client diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 70ab0d2..a5fbc5f 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -554,6 +554,14 @@ void Span::processSerialCommand(const char *c){ switch(c[0]){ + case 'Z': { + TempBuffer tBuf(HAPClient::listControllers(NULL)); + HAPClient::listControllers(tBuf.get()); + Serial.printf("SIZE = %d\n",tBuf.len()); + HAPClient::hexPrintRow(tBuf.get(),tBuf.len()); + break; + } + case 's': { LOG0("\n*** HomeSpan Status ***\n\n"); diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 7edb2c7..99a58ea 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ using std::vector; using std::unordered_map; using std::unordered_set; +using std::list; enum { GET_AID=1, diff --git a/src/TLV.h b/src/TLV.h index f6d7e0a..6bca6db 100644 --- a/src/TLV.h +++ b/src/TLV.h @@ -55,11 +55,11 @@ public: int val(tagType tag, uint8_t val); // sets and returns VAL for TLV with matching TAG (or -1 if no match) uint8_t *buf(tagType tag); // returns VAL Buffer for TLV with matching TAG (or NULL if no match) uint8_t *buf(tagType tag, int len); // set length and returns VAL Buffer for TLV with matching TAG (or NULL if no match or if LEN>MAX) + uint8_t *buf(tagType tag, uint8_t *src, int len); // copies len bytes of src into VAL buffer, and sets length to len, for TLV with matching TAG; returns VAL Buffer on success, or NULL if no match or if LEN>MAX int len(tagType tag); // returns LEN for TLV matching TAG (or 0 if TAG is found but LEN not yet set; -1 if no match at all) void print(int minLogLevel=0); // prints all defined TLVs (those with length>0), subject to specified minimum log level int unpack(uint8_t *tlvBuf, int nBytes); // unpacks nBytes of TLV content from single byte buffer into individual TLV records (return 1 on success, 0 if fail) int pack(uint8_t *tlvBuf); // if tlvBuf!=NULL, packs all defined TLV records (LEN>0) into a single byte buffer, spitting large TLVs into separate 255-byte chunks. Returns number of bytes (that would be) stored in buffer - int separate(uint8_t *tlvBuf); // if tlvBuf!=NULL, packs a TLV separator into a single byte buffer. Returns number of bytes (that would be) stored in buffer }; // TLV @@ -129,8 +129,12 @@ int TLV::val(tagType tag){ tlv_t *tlv=find(tag); - if(tlv && tlv->len>0) - return(tlv->val[0]); + if(tlv && tlv->len>=0){ + if(tlv->maxLen>0) + return(tlv->val[0]); + else + return(0); + } return(-1); } @@ -144,8 +148,9 @@ int TLV::val(tagType tag, uint8_t val){ tlv_t *tlv=find(tag); if(tlv){ - tlv->val[0]=val; - tlv->len=1; + if(tlv->maxLen>0) + tlv->val[0]=val; + tlv->len=(tlv->maxLen>0); cLen+=tlv->len+2; return(val); } @@ -188,6 +193,28 @@ uint8_t *TLV::buf(tagType tag, int len){ return(NULL); } +////////////////////////////////////// +// TLV buf(tag, src, len) + +template +uint8_t *TLV::buf(tagType tag, uint8_t *src, int len){ + + tlv_t *tlv=find(tag); + + if(tlv && len<=tlv->maxLen){ + tlv->len=len; + cLen+=tlv->len; + + for(int i=0;ilen;i+=255) + cLen+=2; + + memcpy(tlv->val,src,len); + return(tlv->val); + } + + return(NULL); +} + ////////////////////////////////////// // TLV print() @@ -199,7 +226,7 @@ void TLV::print(int minLogLevel){ for(int i=0;i0){ + if(tlv[i].len>=0){ Serial.printf("%s(%d) ",tlv[i].name,tlv[i].len); for(int j=0;j::print(int minLogLevel){ } // len>0 } // loop over all TLVs -} - -////////////////////////////////////// -// TLV separate(tlvBuf) - -template -int TLV::separate(uint8_t *tlvBuf){ - - if(tlvBuf){ // load separator - *tlvBuf++=kTLVType_Separator; - *tlvBuf=0; - } - - return(2); -} +} ////////////////////////////////////// // TLV pack(tlvBuf) @@ -234,19 +247,23 @@ int TLV::pack(uint8_t *tlvBuf){ int n=0; int nBytes; - for(int i=0;i0){ - for(int j=0;j=0){ + int j=0; + do{ + int wBytes=nBytes>255?255:nBytes; if(tlvBuf!=NULL){ *tlvBuf++=tlv[i].tag; - *tlvBuf++=nBytes>255?255:nBytes; - memcpy(tlvBuf,tlv[i].val+j,nBytes>255?255:nBytes); - tlvBuf+=nBytes>255?255:nBytes; + *tlvBuf++=wBytes; + memcpy(tlvBuf,tlv[i].val+j,wBytes); + tlvBuf+=wBytes; } - n+=(nBytes>255?255:nBytes)+2; - } // j-loop - } // len>0 + n+=wBytes+2; + j+=wBytes; + nBytes-=wBytes; + } while(nBytes>0); + } // len>=0 } // loop over all TLVs diff --git a/src/Utils.h b/src/Utils.h index 46b52a3..4272494 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -48,11 +48,12 @@ class TempBuffer { bufType *buf; int nBytes; + int nElements; public: - TempBuffer(size_t len){ - nBytes=len*sizeof(bufType); + TempBuffer(int _nElements) : nElements(_nElements) { + nBytes=nElements*sizeof(bufType); buf=(bufType *)malloc(nBytes); if(buf==NULL){ Serial.print("\n\n*** FATAL ERROR: Requested allocation of "); @@ -70,6 +71,10 @@ class TempBuffer { return(nBytes); } + int size(){ + return(nElements); + } + bufType *get(){ return(buf); }