diff --git a/library.properties b/library.properties index 18d876c..4d04fdc 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=HomeSpan -version=1.1.3 +version=1.1.4 author=Gregg maintainer=Gregg sentence=A robust and extremely easy-to-use HomeKit implementation for the Espressif ESP32 running on the Arduino IDE. diff --git a/src/HAP.cpp b/src/HAP.cpp index 150a8df..f788d6a 100644 --- a/src/HAP.cpp +++ b/src/HAP.cpp @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020 Gregg E. Berman + * Copyright (c) 2020-2021 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * @@ -58,11 +58,14 @@ void HAPClient::init(){ } else { char c[128]; - sprintf(c,"Generating SRP verification data for default Setup Code: %.3s-%.2s-%.3s\n\n",homeSpan.defaultSetupCode,homeSpan.defaultSetupCode+3,homeSpan.defaultSetupCode+5); + sprintf(c,"Generating SRP verification data for default Setup Code: %.3s-%.2s-%.3s\n",homeSpan.defaultSetupCode,homeSpan.defaultSetupCode+3,homeSpan.defaultSetupCode+5); Serial.print(c); srp.createVerifyCode(homeSpan.defaultSetupCode,verifyData.verifyCode,verifyData.salt); // create verification code from default Setup Code and random salt nvs_set_blob(srpNVS,"VERIFYDATA",&verifyData,sizeof(verifyData)); // update data - nvs_commit(srpNVS); // commit to NVS + nvs_commit(srpNVS); // commit to NVS + Serial.print("Optional QR Code: "); + Serial.print(homeSpan.getQRCode(homeSpan.defaultSetupCode)); + Serial.print("\n\n"); } if(!nvs_get_blob(hapNVS,"ACCESSORY",NULL,&len)){ // if found long-term Accessory data in NVS @@ -647,6 +650,8 @@ int HAPClient::postPairSetupURL(){ } // switch + return(1); + } // postPairSetup ////////////////////////////////////// @@ -701,7 +706,7 @@ int HAPClient::postPairVerifyURL(){ memcpy(iosCurveKey,tlv8.buf(kTLVType_PublicKey),32); // save iosCurveKey (will persist until end of verification process) - int rVal=crypto_scalarmult_curve25519(sharedCurveKey,secretCurveKey,iosCurveKey); // generate (and persist) Pair Verify SharedSecret CurveKey from Accessory's Curve25519 secret key and Controller's Curve25519 public key (32 bytes) + crypto_scalarmult_curve25519(sharedCurveKey,secretCurveKey,iosCurveKey); // generate (and persist) Pair Verify SharedSecret CurveKey from Accessory's Curve25519 secret key and Controller's Curve25519 public key (32 bytes) uint8_t *accessoryPairingID = accessory.ID; // set accessoryPairingID size_t accessoryPairingIDLen = 17; @@ -842,6 +847,8 @@ int HAPClient::postPairVerifyURL(){ break; } // switch + + return(1); } // postPairVerify @@ -925,7 +932,7 @@ int HAPClient::postPairingsURL(){ break; } - if(newCont=findController(tlv8.buf(kTLVType_Identifier))){ + if((newCont=findController(tlv8.buf(kTLVType_Identifier)))){ tlv8.clear(); // clear TLV records tlv8.val(kTLVType_State,pairState_M2); // set State= if(!memcmp(cPair->LTPK,newCont->LTPK,32)){ // requested Controller already exists and LTPK matches @@ -1187,10 +1194,10 @@ int HAPClient::putPrepareURL(char *json){ uint32_t ttl; uint64_t pid; - if(cBuf=strstr(json,ttlToken)) - sscanf(cBuf+strlen(ttlToken),"%lu",&ttl); + if((cBuf=strstr(json,ttlToken))) + sscanf(cBuf+strlen(ttlToken),"%u",&ttl); - if(cBuf=strstr(json,pidToken)) + if((cBuf=strstr(json,pidToken))) sscanf(cBuf+strlen(ttlToken),"%llu",&pid); char jsonBuf[32]; @@ -1202,7 +1209,7 @@ int HAPClient::putPrepareURL(char *json){ status=StatusCode::InvalidValue; } - sprintf(jsonBuf,"{\"status\":%d}",status); + sprintf(jsonBuf,"{\"status\":%d}",(int)status); int nBytes=strlen(jsonBuf); int nChars=snprintf(NULL,0,"HTTP/1.1 200 OK\r\nContent-Type: application/hap+json\r\nContent-Length: %d\r\n\r\n",nBytes); // create Body with Content Length = size of JSON Buf char body[nChars+1]; @@ -1264,7 +1271,7 @@ void HAPClient::checkTimedWrites(){ for(auto tw=homeSpan.TimedWrites.begin(); tw!=homeSpan.TimedWrites.end(); tw++){ // loop over all Timed Writes using an iterator if(cTime>tw->second){ // timer has expired - sprintf(c,"Removing PID=%llu ALARM=%lu\n",tw->first,tw->second); + sprintf(c,"Removing PID=%llu ALARM=%u\n",tw->first,tw->second); LOG2(c); homeSpan.TimedWrites.erase(tw); } @@ -1338,7 +1345,6 @@ void HAPClient::tlvRespond(){ int HAPClient::receiveEncrypted(){ uint8_t buf[1042]; // maximum size of encoded message = 2+1024+16 bytes (HAP Section 6.5.2) - int nFrames=0; int nBytes=0; while(client.read(buf,2)==2){ // read initial 2-byte AAD record @@ -1498,7 +1504,7 @@ Controller *HAPClient::addController(uint8_t *id, uint8_t *ltpk, boolean admin){ Controller *slot; - if(slot=findController(id)){ + if((slot=findController(id))){ memcpy(slot->LTPK,ltpk,32); slot->admin=admin; LOG2("\n*** Updated Controller: "); @@ -1508,7 +1514,7 @@ Controller *HAPClient::addController(uint8_t *id, uint8_t *ltpk, boolean admin){ return(slot); } - if(slot=getFreeController()){ + if((slot=getFreeController())){ slot->allocated=true; memcpy(slot->ID,id,36); memcpy(slot->LTPK,ltpk,32); @@ -1553,7 +1559,7 @@ void HAPClient::removeController(uint8_t *id){ Controller *slot; - if(slot=findController(id)){ // remove controller if found + if((slot=findController(id))){ // remove controller if found LOG2("\n***Removed Controller: "); if(homeSpan.logLevel>1) charPrintRow(id,36); diff --git a/src/HAP.h b/src/HAP.h index e71c6ed..9730215 100644 --- a/src/HAP.h +++ b/src/HAP.h @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020 Gregg E. Berman + * Copyright (c) 2020-2021 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/HAPConstants.h b/src/HAPConstants.h index a332ff8..fdef845 100644 --- a/src/HAPConstants.h +++ b/src/HAPConstants.h @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020 Gregg E. Berman + * Copyright (c) 2020-2021 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/HKDF.cpp b/src/HKDF.cpp index 1ed8fe2..9c75be5 100644 --- a/src/HKDF.cpp +++ b/src/HKDF.cpp @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020 Gregg E. Berman + * Copyright (c) 2020-2021 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/HKDF.h b/src/HKDF.h index 20eb713..c2057ba 100644 --- a/src/HKDF.h +++ b/src/HKDF.h @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020 Gregg E. Berman + * Copyright (c) 2020-2021 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index a37e8f8..caab137 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020 Gregg E. Berman + * Copyright (c) 2020-2021 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * @@ -35,8 +35,6 @@ using namespace Utils; -WiFiServer hapServer(80); // HTTP Server (i.e. this acccesory) running on usual port 80 (local-scoped variable to this file only) - HAPClient **hap; // HAP Client structure containing HTTP client connections, parsing routines, and state variables (global-scoped variable) Span homeSpan; // HAP Attributes database and all related control functions for this Accessory (global-scoped variable) @@ -49,7 +47,7 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa this->displayName=displayName; this->hostNameBase=hostNameBase; this->modelName=modelName; - sprintf(this->category,"%d",catID); + sprintf(this->category,"%d",(int)catID); controlButton.init(controlPin); statusLED.init(statusPin); @@ -127,6 +125,7 @@ void Span::poll() { statusLED.start(LED_WIFI_NEEDED); } else { homeSpan.statusLED.start(LED_WIFI_CONNECTING); + hapServer=new WiFiServer(tcpPortNum,maxConnections); } controlButton.reset(); @@ -150,10 +149,10 @@ void Span::poll() { WiFiClient newClient; - if(newClient=hapServer.available()){ // found a new HTTP client - int freeSlot=getFreeSlot(); // get next free slot + if(hapServer && (newClient=hapServer->available())){ // found a new HTTP client + int freeSlot=getFreeSlot(); // get next free slot - if(freeSlot==-1){ // no available free slots + if(freeSlot==-1){ // no available free slots freeSlot=randombytes_uniform(maxConnections); LOG2("=======================================\n"); LOG1("** Freeing Client #"); @@ -362,23 +361,35 @@ void Span::checkConnect(){ id[17]='\0'; // add terminating null // create broadcaset name from server base name plus accessory ID (without ':') - - int nChars=snprintf(NULL,0,"%s-%.2s%.2s%.2s%.2s%.2s%.2s",hostNameBase,id,id+3,id+6,id+9,id+12,id+15); + + int nChars; + + if(!hostNameSuffix) + nChars=snprintf(NULL,0,"%s-%.2s%.2s%.2s%.2s%.2s%.2s",hostNameBase,id,id+3,id+6,id+9,id+12,id+15); + else + nChars=snprintf(NULL,0,"%s%s",hostNameBase,hostNameSuffix); + char hostName[nChars+1]; - sprintf(hostName,"%s-%.2s%.2s%.2s%.2s%.2s%.2s",hostNameBase,id,id+3,id+6,id+9,id+12,id+15); + + if(!hostNameSuffix) + sprintf(hostName,"%s-%.2s%.2s%.2s%.2s%.2s%.2s",hostNameBase,id,id+3,id+6,id+9,id+12,id+15); + else + sprintf(hostName,"%s%s",hostNameBase,hostNameSuffix); - Serial.print("\nStarting MDNS...\n"); - Serial.print("Broadcasting as: "); + Serial.print("\nStarting MDNS...\n\n"); + Serial.print("HostName: "); Serial.print(hostName); - Serial.print(".local ("); + Serial.print(".local:"); + Serial.print(tcpPortNum); + Serial.print("\nDisplay Name: "); Serial.print(displayName); - Serial.print(" / "); + Serial.print("\nModel Name: "); Serial.print(modelName); - Serial.print(")\n"); + Serial.print("\n"); - MDNS.begin(hostName); // set server host name (.local implied) - MDNS.setInstanceName(displayName); // set server display name - MDNS.addService("_hap","_tcp",80); // advertise HAP service on HTTP port (80) + MDNS.begin(hostName); // set server host name (.local implied) + MDNS.setInstanceName(displayName); // set server display name + MDNS.addService("_hap","_tcp",tcpPortNum); // advertise HAP service on specified port // add MDNS (Bonjour) TXT records for configurable as well as fixed values (HAP Table 6-7) @@ -399,10 +410,21 @@ void Span::checkConnect(){ else mdns_service_txt_item_set("_hap","_tcp","sf","0"); // set Status Flag = 0 + uint8_t hashInput[22]; + uint8_t hashOutput[64]; + char setupHash[9]; + size_t len; + + memcpy(hashInput,qrID,4); // Create the Seup ID for use with optional QR Codes. This is an undocumented feature of HAP R2! + memcpy(hashInput+4,id,17); // Step 1: Concatenate 4-character Setup ID and 17-character Accessory ID into hashInput + mbedtls_sha512_ret(hashInput,21,hashOutput,0); // Step 2: Perform SHA-512 hash on combined 21-byte hashInput to create 64-byte hashOutput + mbedtls_base64_encode((uint8_t *)setupHash,9,&len,hashOutput,4); // Step 3: Encode the first 4 bytes of hashOutput in base64, which results in an 8-character, null-terminated, setupHash + mdns_service_txt_item_set("_hap","_tcp","sh",setupHash); // Step 4: broadcast the resulting Setup Hash + Serial.print("\nStarting Web (HTTP) Server supporting up to "); Serial.print(maxConnections); Serial.print(" simultaneous connections...\n\n"); - hapServer.begin(); + hapServer->begin(); if(!HAPClient::nAdminControllers()){ Serial.print("DEVICE NOT YET PAIRED -- PLEASE PAIR WITH HOMEKIT APP\n\n"); @@ -415,6 +437,19 @@ void Span::checkConnect(){ /////////////////////////////// +void Span::setQRID(const char *id){ + + char tBuf[5]; + sscanf(id,"%4[0-9A-Za-z]",tBuf); + + if(strlen(id)==4 && strlen(tBuf)==4){ + qrID=id; + } + +} // setQRID + +/////////////////////////////// + void Span::processSerialCommand(const char *c){ switch(c[0]){ @@ -504,6 +539,10 @@ void Span::processSerialCommand(const char *c){ nvs_set_blob(HAPClient::srpNVS,"VERIFYDATA",&verifyData,sizeof(verifyData)); // update data nvs_commit(HAPClient::srpNVS); // commit to NVS Serial.print("New Code Saved!\n"); + + Serial.print("Optional QR Code: "); + Serial.print(getQRCode(setupCode)); + Serial.print("\n\n"); } } break; @@ -550,7 +589,7 @@ void Span::processSerialCommand(const char *c){ if(strlen(network.wifiData.ssid)>0){ Serial.print("*** Stopping all current WiFi services...\n\n"); - hapServer.end(); + hapServer->end(); MDNS.end(); WiFi.disconnect(); } @@ -714,6 +753,32 @@ void Span::processSerialCommand(const char *c){ /////////////////////////////// +const char *Span::getQRCode(const char *setupCode){ + + uint64_t n; + uint64_t iSetupCode; + uint64_t iCategory; + + sscanf(category,"%llu",&iCategory); + sscanf(setupCode,"%llu",&iSetupCode); + + n=(iCategory << 31) | (0xA << 27) | iSetupCode; + + qrCode=""; + + while(n>0){ + char c=n%36+48; + if(c>57) + c+=7; + qrCode=c+qrCode; + n/=36; + } + qrCode="X-HM://00" + qrCode + qrID; + return(qrCode.c_str()); +} + +/////////////////////////////// + int Span::sprintfAttributes(char *cBuf){ int nBytes=0; @@ -806,7 +871,7 @@ int Span::countCharacteristics(char *buf){ int nObj=0; const char tag[]="\"aid\""; - while(buf=strstr(buf,tag)){ // count number of characteristic objects in PUT JSON request + while((buf=strstr(buf,tag))){ // count number of characteristic objects in PUT JSON request nObj++; buf+=strlen(tag); } @@ -988,7 +1053,7 @@ int Span::sprintfAttributes(SpanBuf *pObj, int nObj, char *cBuf){ nChars+=snprintf(cBuf,cBuf?64:0,"{\"characteristics\":["); for(int i=0;i #include +#include #include "HAPConstants.h" diff --git a/src/Services.h b/src/Services.h index 664e034..ed3bb5f 100644 --- a/src/Services.h +++ b/src/Services.h @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020 Gregg E. Berman + * Copyright (c) 2020-2021 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/Settings.h b/src/Settings.h index a4dfce4..9b1ccc6 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020 Gregg E. Berman + * Copyright (c) 2020-2021 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * @@ -34,7 +34,7 @@ #define HS_MAJOR 1 #define HS_MINOR 1 -#define HS_PATCH 3 +#define HS_PATCH 4 #define STRINGIFY(x) _STR(x) #define _STR(x) #x @@ -61,6 +61,8 @@ #define DEFAULT_SETUP_CODE "46637726" // changed during network setup or with 'S' command +#define DEFAULT_QR_ID "HSPN" // change with homeSpan.setQRID(qrID); + #define DEFAULT_CONTROL_PIN 21 // change with homeSpan.setControlPin(pin) #define DEFAULT_STATUS_PIN 13 // change with homeSpan.setStatusPin(pin) @@ -73,6 +75,7 @@ #define DEFAULT_LOG_LEVEL 0 // change with homeSpan.setLogLevel(level) #define DEFAULT_MAX_CONNECTIONS 8 // change with homeSpan.setMaxConnections(num); +#define DEFAULT_TCP_PORT 80 // change with homeSpan.setPort(port); ///////////////////////////////////////////////////// diff --git a/src/TLV.h b/src/TLV.h index 077c895..ce11cde 100644 --- a/src/TLV.h +++ b/src/TLV.h @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020 Gregg E. Berman + * Copyright (c) 2020-2021 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * @@ -89,7 +89,9 @@ int TLV::create(tagType tag, int maxLen, const char *name){ tlv[numTags].name=name; tlv[numTags].len=-1; tlv[numTags].val=(uint8_t *)malloc(maxLen); - numTags++; + numTags++; + + return(1); } ////////////////////////////////////// diff --git a/src/Utils.cpp b/src/Utils.cpp index 9ba88dc..0bf1578 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020 Gregg E. Berman + * Copyright (c) 2020-2021 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * @@ -55,10 +55,11 @@ char *Utils::readSerial(char *c, int max){ return(c); // return updated string } - c[i]=buf; // store new character - - if(i