Refactored Controller Structure and Add/Remove/List/Save Functions
Changed fixed-size array `struct Controller[MAX_CONTROLLERS]` to a dynamic linked-list of Controllers. Re-coded all related functions. MAX_CONTROLLERS no sets the size of any buffers but is only used to limit the ultimate size of the linked-list. Saved about 1K of RAM since most of the time there are only 2 Controllers defined (as opposed to the 16 allowed).
This commit is contained in:
parent
5a356432b3
commit
3396a5ff96
274
src/HAP.cpp
274
src/HAP.cpp
|
|
@ -80,7 +80,7 @@ void HAPClient::init(){
|
||||||
if(!nvs_get_blob(hapNVS,"ACCESSORY",NULL,&len)){ // if found long-term Accessory data in NVS
|
if(!nvs_get_blob(hapNVS,"ACCESSORY",NULL,&len)){ // if found long-term Accessory data in NVS
|
||||||
nvs_get_blob(hapNVS,"ACCESSORY",&accessory,&len); // retrieve data
|
nvs_get_blob(hapNVS,"ACCESSORY",&accessory,&len); // retrieve data
|
||||||
} else {
|
} else {
|
||||||
LOG0("Generating new random Accessory ID and Long-Term Ed25519 Signature Keys...\n");
|
LOG0("Generating new random Accessory ID and Long-Term Ed25519 Signature Keys...\n\n");
|
||||||
uint8_t buf[6];
|
uint8_t buf[6];
|
||||||
char cBuf[18];
|
char cBuf[18];
|
||||||
|
|
||||||
|
|
@ -95,21 +95,9 @@ void HAPClient::init(){
|
||||||
nvs_commit(hapNVS); // commit to NVS
|
nvs_commit(hapNVS); // commit to NVS
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!nvs_get_blob(hapNVS,"CONTROLLERS",NULL,&len)){ // if found long-term Controller Pairings data from NVS
|
|
||||||
nvs_get_blob(hapNVS,"CONTROLLERS",controllers,&len); // retrieve data
|
|
||||||
} else {
|
|
||||||
LOG0("Initializing storage for Paired Controllers data...\n\n");
|
|
||||||
|
|
||||||
HAPClient::removeControllers(); // clear all Controller data
|
|
||||||
|
|
||||||
nvs_set_blob(hapNVS,"CONTROLLERS",controllers,sizeof(controllers)); // update data
|
|
||||||
nvs_commit(hapNVS); // commit to NVS
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!nvs_get_blob(hapNVS,"CONTROLLERS",NULL,&len)){ // if found long-term Controller Pairings data from NVS
|
if(!nvs_get_blob(hapNVS,"CONTROLLERS",NULL,&len)){ // if found long-term Controller Pairings data from NVS
|
||||||
TempBuffer <Controller> tBuf(len/sizeof(Controller));
|
TempBuffer <Controller> tBuf(len/sizeof(Controller));
|
||||||
nvs_get_blob(hapNVS,"CONTROLLERS",tBuf.get(),&len); // retrieve data
|
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++){
|
for(int i=0;i<tBuf.size();i++){
|
||||||
if(tBuf.get()[i].allocated)
|
if(tBuf.get()[i].allocated)
|
||||||
controllerList.push_back(tBuf.get()[i]);
|
controllerList.push_back(tBuf.get()[i]);
|
||||||
|
|
@ -583,9 +571,6 @@ int HAPClient::postPairSetupURL(){
|
||||||
|
|
||||||
addController(iosDevicePairingID,iosDeviceLTPK,true); // save Pairing ID and LTPK for this Controller with admin privileges
|
addController(iosDevicePairingID,iosDeviceLTPK,true); // save Pairing ID and LTPK for this Controller with admin privileges
|
||||||
|
|
||||||
nvs_set_blob(hapNVS,"CONTROLLERS",controllers,sizeof(controllers)); // update data
|
|
||||||
nvs_commit(hapNVS); // commit to NVS
|
|
||||||
|
|
||||||
// Now perform the above steps in reverse to securely transmit the AccessoryLTPK to the Controller (HAP Section 5.6.6.2)
|
// Now perform the above steps in reverse to securely transmit the AccessoryLTPK to the Controller (HAP Section 5.6.6.2)
|
||||||
|
|
||||||
uint8_t accessoryX[32];
|
uint8_t accessoryX[32];
|
||||||
|
|
@ -800,12 +785,14 @@ int HAPClient::postPairVerifyURL(){
|
||||||
tlv8.val(kTLVType_Error,tagError_Unknown); // set Error=Unknown (there is no specific error type for missing/bad TLV data)
|
tlv8.val(kTLVType_Error,tagError_Unknown); // set Error=Unknown (there is no specific error type for missing/bad TLV data)
|
||||||
tlvRespond(); // send response to client
|
tlvRespond(); // send response to client
|
||||||
return(0);
|
return(0);
|
||||||
};
|
}
|
||||||
|
|
||||||
Controller *tPair; // temporary pointer to Controller
|
Controller *tPair; // temporary pointer to Controller
|
||||||
|
|
||||||
if(!(tPair=findController(tlv8.buf(kTLVType_Identifier)))){
|
if(!(tPair=findController(tlv8.buf(kTLVType_Identifier)))){
|
||||||
LOG0("\n*** ERROR: Unrecognized Controller PairingID\n\n");
|
LOG0("\n*** ERROR: Unrecognized Controller ID: ");
|
||||||
|
charPrintRow(tlv8.buf(kTLVType_Identifier),36,2);
|
||||||
|
LOG0("\n\n");
|
||||||
tlv8.clear(); // clear TLV records
|
tlv8.clear(); // clear TLV records
|
||||||
tlv8.val(kTLVType_State,pairState_M4); // set State=<M4>
|
tlv8.val(kTLVType_State,pairState_M4); // set State=<M4>
|
||||||
tlv8.val(kTLVType_Error,tagError_Authentication); // set Error=Authentication
|
tlv8.val(kTLVType_Error,tagError_Authentication); // set Error=Authentication
|
||||||
|
|
@ -813,6 +800,10 @@ int HAPClient::postPairVerifyURL(){
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOG2("\n*** Verifying session with Controller ID: ");
|
||||||
|
charPrintRow(tPair->ID,36,2);
|
||||||
|
LOG2("...\n");
|
||||||
|
|
||||||
size_t iosDeviceInfoLen=32+36+32;
|
size_t iosDeviceInfoLen=32+36+32;
|
||||||
uint8_t iosDeviceInfo[iosDeviceInfoLen];
|
uint8_t iosDeviceInfo[iosDeviceInfoLen];
|
||||||
|
|
||||||
|
|
@ -833,7 +824,7 @@ int HAPClient::postPairVerifyURL(){
|
||||||
tlv8.val(kTLVType_State,pairState_M4); // set State=<M4>
|
tlv8.val(kTLVType_State,pairState_M4); // set State=<M4>
|
||||||
tlvRespond(); // send response to client (unencrypted since cPair=NULL)
|
tlvRespond(); // send response to client (unencrypted since cPair=NULL)
|
||||||
|
|
||||||
cPair=tPair; // save Controller for this connection slot - connection is not verified and should be encrypted going forward
|
cPair=tPair; // save Controller for this connection slot - connection is now verified and should be encrypted going forward
|
||||||
|
|
||||||
hkdf.create(a2cKey,sharedCurveKey,32,"Control-Salt","Control-Read-Encryption-Key"); // create AccessoryToControllerKey (HAP Section 6.5.2)
|
hkdf.create(a2cKey,sharedCurveKey,32,"Control-Salt","Control-Read-Encryption-Key"); // create AccessoryToControllerKey (HAP Section 6.5.2)
|
||||||
hkdf.create(c2aKey,sharedCurveKey,32,"Control-Salt","Control-Write-Encryption-Key"); // create ControllerToAccessoryKey (HAP Section 6.5.2)
|
hkdf.create(c2aKey,sharedCurveKey,32,"Control-Salt","Control-Write-Encryption-Key"); // create ControllerToAccessoryKey (HAP Section 6.5.2)
|
||||||
|
|
@ -897,8 +888,6 @@ int HAPClient::postPairingsURL(){
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller *newCont;
|
|
||||||
|
|
||||||
LOG1("In Post Pairings #");
|
LOG1("In Post Pairings #");
|
||||||
LOG1(conNum);
|
LOG1(conNum);
|
||||||
LOG1(" (");
|
LOG1(" (");
|
||||||
|
|
@ -913,69 +902,58 @@ int HAPClient::postPairingsURL(){
|
||||||
|
|
||||||
switch(tlv8.val(kTLVType_Method)){
|
switch(tlv8.val(kTLVType_Method)){
|
||||||
|
|
||||||
case 3:
|
case 3: {
|
||||||
LOG1("Add...\n");
|
LOG1("Add...\n");
|
||||||
|
|
||||||
if(!tlv8.len(kTLVType_Identifier) || !tlv8.len(kTLVType_PublicKey) || !tlv8.len(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");
|
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.clear();
|
||||||
tlv8.val(kTLVType_State,pairState_M2); // set State=<M2>
|
tlv8.val(kTLVType_Error,tagError_Unknown);
|
||||||
tlv8.val(kTLVType_Error,tagError_Unknown); // set Error=Unknown (there is no specific error type for missing/bad TLV data)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!cPair->admin){
|
} else if(!cPair->admin){
|
||||||
LOG0("\n*** ERROR: Controller making request does not have admin privileges to add/update other Controllers\n\n");
|
LOG0("\n*** ERROR: Controller making request does not have admin privileges to add/update other Controllers\n\n");
|
||||||
tlv8.clear(); // clear TLV records
|
tlv8.clear();
|
||||||
tlv8.val(kTLVType_State,pairState_M2); // set State=<M2>
|
tlv8.val(kTLVType_Error,tagError_Authentication);
|
||||||
tlv8.val(kTLVType_Error,tagError_Authentication); // set Error=Authentication
|
|
||||||
break;
|
} else {
|
||||||
|
tagError err=addController(tlv8.buf(kTLVType_Identifier),tlv8.buf(kTLVType_PublicKey),tlv8.val(kTLVType_Permissions));
|
||||||
|
tlv8.clear();
|
||||||
|
if(err!=tagError_None)
|
||||||
|
tlv8.val(kTLVType_Error,err);
|
||||||
}
|
}
|
||||||
|
|
||||||
if((newCont=findController(tlv8.buf(kTLVType_Identifier))) && memcmp(tlv8.buf(kTLVType_PublicKey),newCont->LTPK,32)){ // requested Controller already exists, but LTPKs don't match
|
tlv8.val(kTLVType_State,pairState_M2);
|
||||||
LOG0("\n*** ERROR: Invalid request to update the LTPK of an exsiting Controller\n\n");
|
tlvRespond();
|
||||||
tlv8.clear(); // clear TLV records
|
return(1);
|
||||||
tlv8.val(kTLVType_State,pairState_M2); // set State=<M2>
|
|
||||||
tlv8.val(kTLVType_Error,tagError_Unknown); // set Error=Unknown
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!addController(tlv8.buf(kTLVType_Identifier),tlv8.buf(kTLVType_PublicKey),tlv8.val(kTLVType_Permissions)==1?true:false)){
|
case 4: {
|
||||||
LOG0("\n*** ERROR: Can't pair more than %d Controllers\n\n",MAX_CONTROLLERS);
|
|
||||||
tlv8.clear(); // clear TLV records
|
|
||||||
tlv8.val(kTLVType_State,pairState_M2); // set State=<M2>
|
|
||||||
tlv8.val(kTLVType_Error,tagError_MaxPeers); // set Error=MaxPeers
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
tlv8.clear(); // clear TLV records
|
|
||||||
tlv8.val(kTLVType_State,pairState_M2); // set State=<M2>
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4:
|
|
||||||
LOG1("Remove...\n");
|
LOG1("Remove...\n");
|
||||||
|
|
||||||
|
uint8_t id[36];
|
||||||
|
|
||||||
if(!tlv8.len(kTLVType_Identifier)){
|
if(!tlv8.len(kTLVType_Identifier)){
|
||||||
LOG0("\n*** ERROR: Required 'Identifier' TLV record for this step is bad or missing\n\n");
|
LOG0("\n*** ERROR: Required 'Identifier' TLV record for this step is bad or missing\n\n");
|
||||||
tlv8.clear(); // clear TLV records
|
tlv8.clear();
|
||||||
tlv8.val(kTLVType_State,pairState_M2); // set State=<M2>
|
tlv8.val(kTLVType_Error,tagError_Unknown);
|
||||||
tlv8.val(kTLVType_Error,tagError_Unknown); // set Error=Unknown (there is no specific error type for missing/bad TLV data)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!cPair->admin){
|
} else if(!cPair->admin){
|
||||||
LOG0("\n*** ERROR: Controller making request does not have admin privileges to remove Controllers\n\n");
|
LOG0("\n*** ERROR: Controller making request does not have admin privileges to remove Controllers\n\n");
|
||||||
tlv8.clear(); // clear TLV records
|
tlv8.clear();
|
||||||
tlv8.val(kTLVType_State,pairState_M2); // set State=<M2>
|
tlv8.val(kTLVType_Error,tagError_Authentication);
|
||||||
tlv8.val(kTLVType_Error,tagError_Authentication); // set Error=Authentication
|
} else {
|
||||||
break;
|
memcpy(id,tlv8.buf(kTLVType_Identifier),36);
|
||||||
|
tlv8.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
removeController(tlv8.buf(kTLVType_Identifier));
|
tlv8.val(kTLVType_State,pairState_M2);
|
||||||
|
tlvRespond(); // must send response before removing Controller below
|
||||||
|
|
||||||
tlv8.clear(); // clear TLV records
|
if(tlv8.val(kTLVType_Error)==-1)
|
||||||
tlv8.val(kTLVType_State,pairState_M2); // set State=<M2>
|
removeController(id);
|
||||||
break;
|
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
case 5: {
|
case 5: {
|
||||||
LOG1("List...\n");
|
LOG1("List...\n");
|
||||||
|
|
@ -992,36 +970,16 @@ int HAPClient::postPairingsURL(){
|
||||||
listControllers(tBuf.get());
|
listControllers(tBuf.get());
|
||||||
sendEncrypted(body,tBuf.get(),tBuf.len());
|
sendEncrypted(body,tBuf.get(),tBuf.len());
|
||||||
free(body);
|
free(body);
|
||||||
|
|
||||||
return(1);
|
return(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default: {
|
||||||
LOG0("\n*** ERROR: 'Method' TLV record is either missing or not set to either 3, 4, or 5 as required\n\n");
|
LOG0("\n*** ERROR: 'Method' TLV record is either missing or not set to either 3, 4, or 5 as required\n\n");
|
||||||
badRequestError(); // return with 400 error, which closes connection
|
badRequestError(); // return with 400 error, which closes connection
|
||||||
return(0);
|
return(0);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
} // switch
|
||||||
nvs_set_blob(hapNVS,"CONTROLLERS",controllers,sizeof(controllers)); // update Controller data
|
|
||||||
nvs_commit(hapNVS); // commit to NVS
|
|
||||||
|
|
||||||
tlvRespond();
|
|
||||||
|
|
||||||
// re-check connections and close any (or all) clients as a result of controllers that were removed above
|
|
||||||
// must be performed AFTER sending the TLV response, since that connection itself may be terminated below
|
|
||||||
|
|
||||||
for(int i=0;i<homeSpan.maxConnections;i++){ // loop over all connection slots
|
|
||||||
if(hap[i]->client){ // if slot is connected
|
|
||||||
|
|
||||||
if(!nAdminControllers() || (hap[i]->cPair && !hap[i]->cPair->allocated)){ // accessory unpaired, OR client connection is verified but points to a newly *unallocated* controller
|
|
||||||
LOG1("*** Terminating Client #");
|
|
||||||
LOG1(i);
|
|
||||||
LOG1("\n");
|
|
||||||
hap[i]->client.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // if client connected
|
|
||||||
} // loop over all connection slots
|
|
||||||
|
|
||||||
return(1);
|
return(1);
|
||||||
}
|
}
|
||||||
|
|
@ -1317,7 +1275,7 @@ int HAPClient::getStatusURL(){
|
||||||
mbedtls_version_get_string_full(mbtlsv);
|
mbedtls_version_get_string_full(mbtlsv);
|
||||||
response+="<tr><td>MbedTLS Version:</td><td>" + String(mbtlsv) + "</td></tr>\n";
|
response+="<tr><td>MbedTLS Version:</td><td>" + String(mbtlsv) + "</td></tr>\n";
|
||||||
|
|
||||||
response+="<tr><td>HomeKit Status:</td><td>" + String(nAdminControllers()?"PAIRED":"NOT PAIRED") + "</td></tr>\n";
|
response+="<tr><td>HomeKit Status:</td><td>" + String(HAPClient::nAdminControllers()?"PAIRED":"NOT PAIRED") + "</td></tr>\n";
|
||||||
response+="<tr><td>Max Log Entries:</td><td>" + String(homeSpan.webLog.maxEntries) + "</td></tr>\n";
|
response+="<tr><td>Max Log Entries:</td><td>" + String(homeSpan.webLog.maxEntries) + "</td></tr>\n";
|
||||||
response+="</table>\n";
|
response+="</table>\n";
|
||||||
response+="<p></p>";
|
response+="<p></p>";
|
||||||
|
|
@ -1582,9 +1540,9 @@ void HAPClient::charPrintRow(uint8_t *buf, int n, int minLogLevel){
|
||||||
|
|
||||||
Controller *HAPClient::findController(uint8_t *id){
|
Controller *HAPClient::findController(uint8_t *id){
|
||||||
|
|
||||||
for(int i=0;i<MAX_CONTROLLERS;i++){ // loop over all controller slots
|
for(auto it=controllerList.begin();it!=controllerList.end();it++){
|
||||||
if(controllers[i].allocated && !memcmp(controllers[i].ID,id,36)) // found matching ID
|
if(!memcmp((*it).ID,id,36))
|
||||||
return(controllers+i); // return with pointer to matching controller
|
return(&*it);
|
||||||
}
|
}
|
||||||
|
|
||||||
return(NULL); // no match
|
return(NULL); // no match
|
||||||
|
|
@ -1592,96 +1550,92 @@ Controller *HAPClient::findController(uint8_t *id){
|
||||||
|
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
|
|
||||||
Controller *HAPClient::getFreeController(){
|
|
||||||
|
|
||||||
for(int i=0;i<MAX_CONTROLLERS;i++){ // loop over all controller slots
|
|
||||||
if(!controllers[i].allocated) // found free slot
|
|
||||||
return(controllers+i); // return with pointer to free slot
|
|
||||||
}
|
|
||||||
|
|
||||||
return(NULL); // no free slots
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
|
|
||||||
Controller *HAPClient::addController(uint8_t *id, uint8_t *ltpk, boolean admin){
|
|
||||||
|
|
||||||
Controller *slot;
|
|
||||||
|
|
||||||
if((slot=findController(id))){ // found existing controller
|
|
||||||
memcpy(slot->LTPK,ltpk,32);
|
|
||||||
slot->admin=admin;
|
|
||||||
LOG2("\n*** Updated Controller: ");
|
|
||||||
charPrintRow(id,36,2);
|
|
||||||
LOG2(slot->admin?" (admin)\n\n":" (regular)\n\n");
|
|
||||||
return(slot);
|
|
||||||
}
|
|
||||||
|
|
||||||
if((slot=getFreeController())){ // get slot for new controller, if available
|
|
||||||
slot->allocated=true;
|
|
||||||
memcpy(slot->ID,id,36);
|
|
||||||
memcpy(slot->LTPK,ltpk,32);
|
|
||||||
slot->admin=admin;
|
|
||||||
LOG2("\n*** Added Controller: ");
|
|
||||||
charPrintRow(id,36,2);
|
|
||||||
LOG2(slot->admin?" (admin)\n\n":" (regular)\n\n");
|
|
||||||
return(slot);
|
|
||||||
}
|
|
||||||
|
|
||||||
return(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
|
||||||
|
|
||||||
int HAPClient::nAdminControllers(){
|
int HAPClient::nAdminControllers(){
|
||||||
|
|
||||||
int n=0;
|
int n=0;
|
||||||
|
for(auto it=controllerList.begin();it!=controllerList.end();it++)
|
||||||
for(int i=0;i<MAX_CONTROLLERS;i++){ // loop over all controller slots
|
n+=((*it).admin);
|
||||||
n+=(controllers[i].allocated && controllers[i].admin); // count number of allocated controllers with admin privileges
|
|
||||||
}
|
|
||||||
|
|
||||||
return(n);
|
return(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
|
|
||||||
void HAPClient::removeControllers(){
|
tagError HAPClient::addController(uint8_t *id, uint8_t *ltpk, boolean admin){
|
||||||
|
|
||||||
for(int i=0;i<MAX_CONTROLLERS;i++)
|
Controller *cTemp=findController(id);
|
||||||
controllers[i].allocated=false;
|
|
||||||
|
tagError err=tagError_None;
|
||||||
|
|
||||||
|
if(!cTemp){ // new controller
|
||||||
|
if(controllerList.size()<MAX_CONTROLLERS){
|
||||||
|
controllerList.emplace_back(id,ltpk,admin); // create and store data
|
||||||
|
LOG2("\n*** Added Controller: ");
|
||||||
|
charPrintRow(id,36,2);
|
||||||
|
LOG2(admin?" (admin)\n\n":" (regular)\n\n");
|
||||||
|
saveControllers();
|
||||||
|
} else {
|
||||||
|
LOG0("\n*** ERROR: Can't pair more than %d Controllers\n\n",MAX_CONTROLLERS);
|
||||||
|
err=tagError_MaxPeers;
|
||||||
|
}
|
||||||
|
} else if(!memcmp(ltpk,cTemp->LTPK,sizeof(cTemp->LTPK))){ // existing controller with same LTPK
|
||||||
|
LOG2("\n*** Updated Controller: ");
|
||||||
|
charPrintRow(id,36,2);
|
||||||
|
LOG2(" from %s to %s\n\n",cTemp->admin?"(admin)":"(regular)",admin?"(admin)":"(regular)");
|
||||||
|
cTemp->admin=admin;
|
||||||
|
saveControllers();
|
||||||
|
} else {
|
||||||
|
LOG0("\n*** ERROR: Invalid request to update the LTPK of an existing Controller\n\n");
|
||||||
|
err=tagError_Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
return(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
|
|
||||||
void HAPClient::removeController(uint8_t *id){
|
void HAPClient::removeController(uint8_t *id){
|
||||||
|
|
||||||
Controller *slot;
|
auto it=std::find_if(controllerList.begin(), controllerList.end(), [id](const Controller& cTemp){return(!memcmp(cTemp.ID,id,sizeof(cTemp.ID)));});
|
||||||
|
|
||||||
if((slot=findController(id))){ // remove controller if found
|
if(it==controllerList.end()){
|
||||||
LOG2("\n*** Removed Controller: ");
|
LOG2("\n*** Request to Remove Controller Ignored - Controller Not Found: ");
|
||||||
charPrintRow(id,36,2);
|
charPrintRow(id,36,2);
|
||||||
LOG2(slot->admin?" (admin)\n":" (regular)\n");
|
LOG2("\n");
|
||||||
slot->allocated=false;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG1("\n*** Removing Controller: ");
|
||||||
|
charPrintRow((*it).ID,36,2);
|
||||||
|
LOG1((*it).admin?" (admin)\n":" (regular)\n");
|
||||||
|
|
||||||
|
tearDown((*it).ID); // teardown any connections using this Controller
|
||||||
|
controllerList.erase(it); // remove Controller
|
||||||
|
|
||||||
|
if(!nAdminControllers()){ // no more admin Controllers
|
||||||
|
|
||||||
if(nAdminControllers()==0){ // if no more admins, remove all controllers
|
|
||||||
removeControllers();
|
|
||||||
LOG1("That was last Admin Controller! Removing any remaining Regular Controllers and unpairing Accessory\n");
|
LOG1("That was last Admin Controller! Removing any remaining Regular Controllers and unpairing Accessory\n");
|
||||||
|
|
||||||
|
tearDown(NULL); // teardown all remaining connections
|
||||||
|
controllerList.clear(); // remove all remaining Controllers
|
||||||
mdns_service_txt_item_set("_hap","_tcp","sf","1"); // set Status Flag = 1 (Table 6-8)
|
mdns_service_txt_item_set("_hap","_tcp","sf","1"); // set Status Flag = 1 (Table 6-8)
|
||||||
|
STATUS_UPDATE(start(LED_PAIRING_NEEDED),HS_PAIRING_NEEDED) // set optional Status LED
|
||||||
STATUS_UPDATE(start(LED_PAIRING_NEEDED),HS_PAIRING_NEEDED)
|
|
||||||
|
|
||||||
if(homeSpan.pairCallback) // if set, invoke user-defined Pairing Callback to indicate device has been un-paired
|
if(homeSpan.pairCallback) // if set, invoke user-defined Pairing Callback to indicate device has been un-paired
|
||||||
homeSpan.pairCallback(false);
|
homeSpan.pairCallback(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG2("\n");
|
saveControllers();
|
||||||
} else {
|
}
|
||||||
LOG2("\n*** Request to Remove Controller Ignored - Controller Not Found: ");
|
|
||||||
charPrintRow(id,36,2);
|
|
||||||
LOG2("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
|
||||||
|
void HAPClient::tearDown(uint8_t *id){
|
||||||
|
|
||||||
|
for(int i=0;i<homeSpan.maxConnections;i++){ // loop over all connection slots
|
||||||
|
if(hap[i]->client && (id==NULL || (hap[i]->cPair && !memcmp(id,hap[i]->cPair->ID,36)))){
|
||||||
|
LOG1("*** Terminating Client #%d\n",i);
|
||||||
|
hap[i]->client.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
|
|
@ -1744,11 +1698,8 @@ void HAPClient::saveControllers(){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int n=0;
|
|
||||||
TempBuffer <Controller> tBuf(controllerList.size()); // create temporary buffer to hold Controller data
|
TempBuffer <Controller> tBuf(controllerList.size()); // create temporary buffer to hold Controller data
|
||||||
|
std::copy(controllerList.begin(),controllerList.end(),tBuf.get()); // copy data from linked list to buffer
|
||||||
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_set_blob(hapNVS,"CONTROLLERS",tBuf.get(),tBuf.len()); // update data
|
||||||
nvs_commit(hapNVS); // commit to NVS
|
nvs_commit(hapNVS); // commit to NVS
|
||||||
|
|
@ -1792,7 +1743,6 @@ nvs_handle HAPClient::srpNVS;
|
||||||
HKDF HAPClient::hkdf;
|
HKDF HAPClient::hkdf;
|
||||||
pairState HAPClient::pairStatus;
|
pairState HAPClient::pairStatus;
|
||||||
Accessory HAPClient::accessory;
|
Accessory HAPClient::accessory;
|
||||||
Controller HAPClient::controllers[MAX_CONTROLLERS];
|
|
||||||
list<Controller> HAPClient::controllerList;
|
list<Controller> HAPClient::controllerList;
|
||||||
SRP6A HAPClient::srp;
|
SRP6A HAPClient::srp;
|
||||||
int HAPClient::conNum;
|
int HAPClient::conNum;
|
||||||
|
|
|
||||||
22
src/HAP.h
22
src/HAP.h
|
|
@ -50,10 +50,20 @@ struct Nonce {
|
||||||
// Paired Controller Structure for Permanently-Stored Data
|
// Paired Controller Structure for Permanently-Stored Data
|
||||||
|
|
||||||
struct Controller {
|
struct Controller {
|
||||||
boolean allocated=false; // slot is allocated with Controller data
|
boolean allocated=false; // DEPRECATED (but needed for backwards compatability with original NVS storage of Controller info)
|
||||||
boolean admin; // Controller has admin privileges
|
boolean admin; // Controller has admin privileges
|
||||||
uint8_t ID[36]; // Pairing ID
|
uint8_t ID[36]; // Pairing ID
|
||||||
uint8_t LTPK[32]; // Long Term Ed2519 Public Key
|
uint8_t LTPK[32]; // Long Term Ed2519 Public Key
|
||||||
|
|
||||||
|
Controller(){}
|
||||||
|
|
||||||
|
Controller(uint8_t *id, uint8_t *ltpk, boolean ad){
|
||||||
|
allocated=true;
|
||||||
|
admin=ad;
|
||||||
|
memcpy(ID,id,sizeof(ID));
|
||||||
|
memcpy(LTPK,ltpk,sizeof(LTPK));
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
|
|
@ -84,14 +94,13 @@ struct HAPClient {
|
||||||
static pairState pairStatus; // tracks pair-setup status
|
static pairState pairStatus; // tracks pair-setup status
|
||||||
static SRP6A srp; // stores all SRP-6A keys used for Pair-Setup
|
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 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 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
|
static int conNum; // connection number - used to keep track of per-connection EV notifications
|
||||||
|
|
||||||
// individual structures and data defined for each Hap Client connection
|
// individual structures and data defined for each Hap Client connection
|
||||||
|
|
||||||
WiFiClient client; // handle to client
|
WiFiClient client; // handle to client
|
||||||
Controller *cPair; // pointer to info on current, session-verified Paired Controller (NULL=un-verified, and therefore un-encrypted, connection)
|
Controller *cPair=NULL; // pointer to info on current, session-verified Paired Controller (NULL=un-verified, and therefore un-encrypted, connection)
|
||||||
|
|
||||||
// These keys are generated in the first call to pair-verify and used in the second call to pair-verify so must persist for a short period
|
// These keys are generated in the first call to pair-verify and used in the second call to pair-verify so must persist for a short period
|
||||||
|
|
||||||
|
|
@ -136,14 +145,13 @@ struct HAPClient {
|
||||||
static void charPrintRow(uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as CHAR, all on one row, subject to specified minimum log level
|
static void charPrintRow(uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as CHAR, all on one row, subject to specified minimum log level
|
||||||
|
|
||||||
static Controller *findController(uint8_t *id); // returns pointer to controller with matching ID (or NULL if no match)
|
static Controller *findController(uint8_t *id); // returns pointer to controller with matching ID (or NULL if no match)
|
||||||
static Controller *getFreeController(); // return pointer to next free controller slot (or NULL if no free slots)
|
static tagError addController(uint8_t *id, uint8_t *ltpk, boolean admin); // stores data for new Controller with specified data. Returns tagError (if any)
|
||||||
static Controller *addController(uint8_t *id, uint8_t *ltpk, boolean admin); // stores data for new Controller with specified data. Returns pointer to Controller slot on success, else NULL
|
|
||||||
static int nAdminControllers(); // returns number of admin Controllers stored
|
|
||||||
static void removeControllers(); // removes all Controllers (sets allocated flags to false for all slots)
|
|
||||||
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 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 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 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 saveControllers(); // saves Controller list in NVS
|
||||||
|
static int nAdminControllers(); // returns number of admin Controller
|
||||||
|
static void tearDown(uint8_t *id); // tears down connections using Controller with ID=id; tears down all connections if id=NULL
|
||||||
static void checkNotifications(); // checks for Event Notifications and reports to controllers as needed (HAP Section 6.8)
|
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 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
|
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
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ typedef enum {
|
||||||
// HAP Error Codes (HAP Table 5-5)
|
// HAP Error Codes (HAP Table 5-5)
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
tagError_None=0x00,
|
||||||
tagError_Unknown=0x01,
|
tagError_Unknown=0x01,
|
||||||
tagError_Authentication=0x02,
|
tagError_Authentication=0x02,
|
||||||
tagError_Backoff=0x03,
|
tagError_Backoff=0x03,
|
||||||
|
|
|
||||||
|
|
@ -260,7 +260,7 @@ void Span::pollTask() {
|
||||||
homeSpan.lastClientIP="0.0.0.0"; // reset stored IP address to show "0.0.0.0" if homeSpan.getClientIP() is used in any other context
|
homeSpan.lastClientIP="0.0.0.0"; // reset stored IP address to show "0.0.0.0" if homeSpan.getClientIP() is used in any other context
|
||||||
|
|
||||||
if(!hap[i]->client){ // client disconnected by server
|
if(!hap[i]->client){ // client disconnected by server
|
||||||
LOG1("** Disconnecting Client #");
|
LOG1("** Disconnected Client #");
|
||||||
LOG1(i);
|
LOG1(i);
|
||||||
LOG1(" (");
|
LOG1(" (");
|
||||||
LOG1(millis()/1000);
|
LOG1(millis()/1000);
|
||||||
|
|
@ -555,6 +555,8 @@ void Span::processSerialCommand(const char *c){
|
||||||
switch(c[0]){
|
switch(c[0]){
|
||||||
|
|
||||||
case 'Z': {
|
case 'Z': {
|
||||||
|
HAPClient::saveControllers();
|
||||||
|
break;
|
||||||
TempBuffer <uint8_t> tBuf(HAPClient::listControllers(NULL));
|
TempBuffer <uint8_t> tBuf(HAPClient::listControllers(NULL));
|
||||||
HAPClient::listControllers(tBuf.get());
|
HAPClient::listControllers(tBuf.get());
|
||||||
Serial.printf("SIZE = %d\n",tBuf.len());
|
Serial.printf("SIZE = %d\n",tBuf.len());
|
||||||
|
|
@ -688,19 +690,10 @@ void Span::processSerialCommand(const char *c){
|
||||||
|
|
||||||
case 'U': {
|
case 'U': {
|
||||||
|
|
||||||
HAPClient::removeControllers(); // clear all Controller data
|
HAPClient::controllerList.clear(); // clear all Controller data
|
||||||
nvs_set_blob(HAPClient::hapNVS,"CONTROLLERS",HAPClient::controllers,sizeof(HAPClient::controllers)); // update data
|
HAPClient::saveControllers();
|
||||||
nvs_commit(HAPClient::hapNVS); // commit to NVS
|
|
||||||
LOG0("\n*** HomeSpan Pairing Data DELETED ***\n\n");
|
LOG0("\n*** HomeSpan Pairing Data DELETED ***\n\n");
|
||||||
|
HAPClient::tearDown(NULL); // tear down all verified connections
|
||||||
for(int i=0;i<maxConnections;i++){ // loop over all connection slots
|
|
||||||
if(hap[i]->client){ // if slot is connected
|
|
||||||
LOG1("*** Terminating Client #");
|
|
||||||
LOG1(i);
|
|
||||||
LOG1("\n");
|
|
||||||
hap[i]->client.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG0("\nDEVICE NOT YET PAIRED -- PLEASE PAIR WITH HOMEKIT APP\n\n");
|
LOG0("\nDEVICE NOT YET PAIRED -- PLEASE PAIR WITH HOMEKIT APP\n\n");
|
||||||
mdns_service_txt_item_set("_hap","_tcp","sf","1"); // set Status Flag = 1 (Table 6-8)
|
mdns_service_txt_item_set("_hap","_tcp","sf","1"); // set Status Flag = 1 (Table 6-8)
|
||||||
|
|
@ -1016,12 +1009,10 @@ void Span::processSerialCommand(const char *c){
|
||||||
TempBuffer<char> tBuf(256);
|
TempBuffer<char> tBuf(256);
|
||||||
mbedtls_base64_encode((uint8_t *)tBuf.get(),256,&olen,(uint8_t *)&HAPClient::accessory,sizeof(struct Accessory));
|
mbedtls_base64_encode((uint8_t *)tBuf.get(),256,&olen,(uint8_t *)&HAPClient::accessory,sizeof(struct Accessory));
|
||||||
LOG0("Accessory data: %s\n",tBuf.get());
|
LOG0("Accessory data: %s\n",tBuf.get());
|
||||||
for(int i=0;i<HAPClient::MAX_CONTROLLERS;i++){
|
for(const auto &cont : HAPClient::controllerList){
|
||||||
if(HAPClient::controllers[i].allocated){
|
mbedtls_base64_encode((uint8_t *)tBuf.get(),256,&olen,(uint8_t *)(&cont),sizeof(struct Controller));
|
||||||
mbedtls_base64_encode((uint8_t *)tBuf.get(),256,&olen,(uint8_t *)(HAPClient::controllers+i),sizeof(struct Controller));
|
|
||||||
LOG0("Controller data: %s\n",tBuf.get());
|
LOG0("Controller data: %s\n",tBuf.get());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
LOG0("\n*** End Pairing Data\n\n");
|
LOG0("\n*** End Pairing Data\n\n");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -1048,21 +1039,24 @@ void Span::processSerialCommand(const char *c){
|
||||||
LOG0("\n");
|
LOG0("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int i=0;i<HAPClient::MAX_CONTROLLERS;i++){
|
HAPClient::controllerList.clear();
|
||||||
|
Controller tCont;
|
||||||
|
|
||||||
|
while(HAPClient::controllerList.size()<16){
|
||||||
tBuf.get()[0]='\0';
|
tBuf.get()[0]='\0';
|
||||||
LOG0(">>> Controller data: ");
|
LOG0(">>> Controller data: ");
|
||||||
readSerial(tBuf.get(),199);
|
readSerial(tBuf.get(),199);
|
||||||
if(strlen(tBuf.get())==0){
|
if(strlen(tBuf.get())==0){
|
||||||
LOG0("(done)\n");
|
LOG0("(done)\n");
|
||||||
while(i<HAPClient::MAX_CONTROLLERS) // clear data from remaining controller slots
|
break;
|
||||||
HAPClient::controllers[i++].allocated=false;
|
|
||||||
} else {
|
} else {
|
||||||
mbedtls_base64_decode((uint8_t *)(HAPClient::controllers+i),sizeof(struct Controller),&olen,(uint8_t *)tBuf.get(),strlen(tBuf.get()));
|
mbedtls_base64_decode((uint8_t *)(&tCont),sizeof(struct Controller),&olen,(uint8_t *)tBuf.get(),strlen(tBuf.get()));
|
||||||
if(olen!=sizeof(struct Controller)){
|
if(olen!=sizeof(struct Controller)){
|
||||||
LOG0("\n*** Error in size of Controller data - cloning cancelled. Restarting...\n\n");
|
LOG0("\n*** Error in size of Controller data - cloning cancelled. Restarting...\n\n");
|
||||||
reboot();
|
reboot();
|
||||||
} else {
|
} else {
|
||||||
HAPClient::charPrintRow(HAPClient::controllers[i].ID,36);
|
HAPClient::controllerList.push_back(tCont);
|
||||||
|
HAPClient::charPrintRow(tCont.ID,36);
|
||||||
LOG0("\n");
|
LOG0("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1076,8 +1070,7 @@ void Span::processSerialCommand(const char *c){
|
||||||
if(qSave[0]=='y'){
|
if(qSave[0]=='y'){
|
||||||
LOG0("(yes)\nData saved! Rebooting...");
|
LOG0("(yes)\nData saved! Rebooting...");
|
||||||
nvs_set_blob(HAPClient::hapNVS,"ACCESSORY",&HAPClient::accessory,sizeof(HAPClient::accessory)); // update data
|
nvs_set_blob(HAPClient::hapNVS,"ACCESSORY",&HAPClient::accessory,sizeof(HAPClient::accessory)); // update data
|
||||||
nvs_set_blob(HAPClient::hapNVS,"CONTROLLERS",HAPClient::controllers,sizeof(HAPClient::controllers));
|
HAPClient::saveControllers();
|
||||||
nvs_commit(HAPClient::hapNVS); // commit to NVS
|
|
||||||
reboot();
|
reboot();
|
||||||
} else
|
} else
|
||||||
if(qSave[0]=='n'){
|
if(qSave[0]=='n'){
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue