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)
This commit is contained in:
parent
17410e825e
commit
a84429f930
97
src/HAP.cpp
97
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 <Controller> 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<tBuf.size();i++){
|
||||
if(tBuf.get()[i].allocated)
|
||||
controllerList.push_back(tBuf.get()[i]);
|
||||
}
|
||||
}
|
||||
|
||||
LOG0("Accessory ID: ");
|
||||
charPrintRow(accessory.ID,17);
|
||||
LOG0(" LTPK: ");
|
||||
|
|
@ -114,7 +124,8 @@ void HAPClient::init(){
|
|||
|
||||
printControllers();
|
||||
|
||||
tlv8.create(kTLVType_State,1,"STATE"); // define the actual TLV records needed for the implementation of HAP; one for each kTLVType needed (HAP Table 5-6)
|
||||
tlv8.create(kTLVType_Separator,0,"SEPARATOR"); // define the actual TLV records needed for the implementation of HAP; one for each kTLVType needed (HAP Table 5-6)
|
||||
tlv8.create(kTLVType_State,1,"STATE");
|
||||
tlv8.create(kTLVType_PublicKey,384,"PUBKEY");
|
||||
tlv8.create(kTLVType_Method,1,"METHOD");
|
||||
tlv8.create(kTLVType_Salt,16,"SALT");
|
||||
|
|
@ -476,7 +487,7 @@ int HAPClient::postPairSetupURL(){
|
|||
|
||||
case pairState_M5: // 'Exchange 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_M6); // set State=<M6>
|
||||
|
|
@ -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=<M6>
|
||||
|
|
@ -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=<M2>
|
||||
|
|
@ -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=<M4>
|
||||
|
|
@ -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=<M4>
|
||||
|
|
@ -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=<M2>
|
||||
|
|
@ -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=<M2>
|
||||
|
|
@ -1683,21 +1694,13 @@ int HAPClient::listControllers(uint8_t *tlvBuf){
|
|||
tlv8.clear();
|
||||
tlv8.val(kTLVType_State,pairState_M2);
|
||||
|
||||
for(int i=0;i<MAX_CONTROLLERS;i++){ // loop over all controller slots
|
||||
if(controllers[i].allocated){
|
||||
|
||||
if(tlv8.val(kTLVType_State)==-1){ // if State is not set then this is not the first controller found
|
||||
n=tlv8.separate(tlvBuf); // add separator
|
||||
nBytes+=n;
|
||||
if(tlvBuf){
|
||||
tlvBuf+=n;
|
||||
LOG2("SEPARATOR(0)\n");
|
||||
}
|
||||
}
|
||||
|
||||
tlv8.val(kTLVType_Permissions,controllers[i].admin);
|
||||
memcpy(tlv8.buf(kTLVType_Identifier,36),controllers[i].ID,36);
|
||||
memcpy(tlv8.buf(kTLVType_PublicKey,32),controllers[i].LTPK,32);
|
||||
for(auto it=controllerList.begin();it!=controllerList.end();it++){
|
||||
if((*it).allocated){
|
||||
if(tlv8.val(kTLVType_State)==-1) // if State is not set then this is not the first controller found
|
||||
tlv8.val(kTLVType_Separator,1);
|
||||
tlv8.val(kTLVType_Permissions,(*it).admin);
|
||||
tlv8.buf(kTLVType_Identifier,(*it).ID,36);
|
||||
tlv8.buf(kTLVType_PublicKey,(*it).LTPK,32);
|
||||
n=tlv8.pack(tlvBuf);
|
||||
nBytes+=n;
|
||||
if(tlvBuf){
|
||||
|
|
@ -1717,24 +1720,41 @@ void HAPClient::printControllers(int minLogLevel){
|
|||
|
||||
if(homeSpan.logLevel<minLogLevel)
|
||||
return;
|
||||
|
||||
int n=0;
|
||||
|
||||
if(controllerList.empty()){
|
||||
Serial.printf("No Paired Controllers\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for(int i=0;i<MAX_CONTROLLERS;i++){ // loop over all controller slots
|
||||
if(controllers[i].allocated){
|
||||
Serial.printf("Paired Controller: ");
|
||||
charPrintRow(controllers[i].ID,36);
|
||||
Serial.printf("%s LTPK: ",controllers[i].admin?" (admin)":" (regular)");
|
||||
hexPrintRow(controllers[i].LTPK,32);
|
||||
Serial.printf("\n");
|
||||
n++;
|
||||
}
|
||||
for(auto it=controllerList.begin();it!=controllerList.end();it++){
|
||||
Serial.printf("Paired Controller: ");
|
||||
charPrintRow((*it).ID,36);
|
||||
Serial.printf("%s LTPK: ",(*it).admin?" (admin)":" (regular)");
|
||||
hexPrintRow((*it).LTPK,32);
|
||||
Serial.printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void HAPClient::saveControllers(){
|
||||
|
||||
if(controllerList.empty()){
|
||||
nvs_erase_key(hapNVS,"CONTROLLERS");
|
||||
return;
|
||||
}
|
||||
|
||||
if(n==0)
|
||||
Serial.printf("No Paired Controllers\n");
|
||||
int n=0;
|
||||
TempBuffer <Controller> 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<kTLVType,10> HAPClient::tlv8;
|
||||
TLV<kTLVType,11> 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<Controller> HAPClient::controllerList;
|
||||
SRP6A HAPClient::srp;
|
||||
int HAPClient::conNum;
|
||||
|
||||
|
|
|
|||
|
|
@ -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<kTLVType,10> tlv8; // TLV8 structure (HAP Section 14.1) with space for 10 TLV records of type kTLVType (HAP Table 5-6)
|
||||
static TLV<kTLVType,11> 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<Controller> 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
|
||||
|
|
|
|||
|
|
@ -554,6 +554,14 @@ void Span::processSerialCommand(const char *c){
|
|||
|
||||
switch(c[0]){
|
||||
|
||||
case 'Z': {
|
||||
TempBuffer <uint8_t> 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");
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
#include <list>
|
||||
#include <nvs.h>
|
||||
#include <ArduinoOTA.h>
|
||||
#include <esp_now.h>
|
||||
|
|
@ -55,6 +56,7 @@
|
|||
using std::vector;
|
||||
using std::unordered_map;
|
||||
using std::unordered_set;
|
||||
using std::list;
|
||||
|
||||
enum {
|
||||
GET_AID=1,
|
||||
|
|
|
|||
77
src/TLV.h
77
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<tagType, maxTags>::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<tagType, maxTags>::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<tagType, maxTags>::buf(tagType tag, int len){
|
|||
return(NULL);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
// TLV buf(tag, src, len)
|
||||
|
||||
template<class tagType, int maxTags>
|
||||
uint8_t *TLV<tagType, maxTags>::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;i<tlv->len;i+=255)
|
||||
cLen+=2;
|
||||
|
||||
memcpy(tlv->val,src,len);
|
||||
return(tlv->val);
|
||||
}
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
// TLV print()
|
||||
|
||||
|
|
@ -199,7 +226,7 @@ void TLV<tagType, maxTags>::print(int minLogLevel){
|
|||
|
||||
for(int i=0;i<numTags;i++){
|
||||
|
||||
if(tlv[i].len>0){
|
||||
if(tlv[i].len>=0){
|
||||
Serial.printf("%s(%d) ",tlv[i].name,tlv[i].len);
|
||||
|
||||
for(int j=0;j<tlv[i].len;j++)
|
||||
|
|
@ -209,21 +236,7 @@ void TLV<tagType, maxTags>::print(int minLogLevel){
|
|||
|
||||
} // len>0
|
||||
} // loop over all TLVs
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
// TLV separate(tlvBuf)
|
||||
|
||||
template<class tagType, int maxTags>
|
||||
int TLV<tagType, maxTags>::separate(uint8_t *tlvBuf){
|
||||
|
||||
if(tlvBuf){ // load separator
|
||||
*tlvBuf++=kTLVType_Separator;
|
||||
*tlvBuf=0;
|
||||
}
|
||||
|
||||
return(2);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
// TLV pack(tlvBuf)
|
||||
|
|
@ -234,19 +247,23 @@ int TLV<tagType, maxTags>::pack(uint8_t *tlvBuf){
|
|||
int n=0;
|
||||
int nBytes;
|
||||
|
||||
for(int i=0;i<numTags;i++){
|
||||
for(int i=0;i<numTags;i++){
|
||||
|
||||
if((nBytes=tlv[i].len)>0){
|
||||
for(int j=0;j<tlv[i].len;j+=255,nBytes-=255){
|
||||
if((nBytes=tlv[i].len)>=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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue