diff --git a/src/HAP.cpp b/src/HAP.cpp index b0c891e..85f8cd1 100644 --- a/src/HAP.cpp +++ b/src/HAP.cpp @@ -523,7 +523,7 @@ int HAPClient::postPairSetupURL(uint8_t *content, size_t len){ auto itSignature=subTLV.find(kTLVType_Signature); auto itPublicKey=subTLV.find(kTLVType_PublicKey); - if(subTLV.len(itIdentifier)!=36 || subTLV.len(itSignature)!=crypto_sign_BYTES || subTLV.len(itPublicKey)<=0){ + if(subTLV.len(itIdentifier)!=36 || subTLV.len(itSignature)!=crypto_sign_BYTES || subTLV.len(itPublicKey)!=crypto_sign_PUBLICKEYBYTES){ LOG0("\n*** ERROR: One or more of required 'Identifier,' 'PublicKey,' and 'Signature' TLV records for this step is bad or missing\n\n"); responseTLV.add(kTLVType_State,pairState_M6); // set State= responseTLV.add(kTLVType_Error,tagError_Unknown); // set Error=Unknown (there is no specific error type for missing/bad TLV data) @@ -537,93 +537,70 @@ int HAPClient::postPairSetupURL(uint8_t *content, size_t len){ // Rather, it purposely does not transmit "iosDeviceX", which is derived from the SRP Shared Secret that only the Client and this Server know. // Note that the SALT and INFO text fields now match those in HAP Section 5.6.6.1 - uint8_t iosDeviceX[32]; - hkdf.create(iosDeviceX,srp.sharedSecret,64,"Pair-Setup-Controller-Sign-Salt","Pair-Setup-Controller-Sign-Info"); // derive iosDeviceX from SRP Shared Secret using HKDF - const size_t iosDeviceXLen=32; + TempBuffer iosDeviceX(32); + hkdf.create(iosDeviceX,srp.sharedSecret,64,"Pair-Setup-Controller-Sign-Salt","Pair-Setup-Controller-Sign-Info"); // derive iosDeviceX (32 bytes) from SRP Shared Secret using HKDF - uint8_t *iosDevicePairingID = tlv8.buf(kTLVType_Identifier); // set iosDevicePairingID from TLV record - size_t iosDevicePairingIDLen = tlv8.len(kTLVType_Identifier); + // Concatenate iosDeviceX, IOS ID, and IOS PublicKey into iosDeviceInfo + + TempBuffer iosDeviceInfo(iosDeviceX.len()+(*itIdentifier).len+(*itPublicKey).len); + Utils::memcat(iosDeviceInfo,3,iosDeviceX.get(),iosDeviceX.len(),(*itIdentifier).val.get(),(*itIdentifier).len,(*itPublicKey).val.get(),(*itPublicKey).len); - uint8_t *iosDeviceLTPK = tlv8.buf(kTLVType_PublicKey); // set iosDeviceLTPK (Ed25519 long-term public key) from TLV record - size_t iosDeviceLTPKLen = tlv8.len(kTLVType_PublicKey); - - size_t iosDeviceInfoLen=iosDeviceXLen+iosDevicePairingIDLen+iosDeviceLTPKLen; // total size of re-constituted message, iosDeviceInfo - uint8_t iosDeviceInfo[iosDeviceInfoLen]; - - memcpy(iosDeviceInfo,iosDeviceX,iosDeviceXLen); // iosDeviceInfo = iosDeviceX - memcpy(iosDeviceInfo+iosDeviceXLen,iosDevicePairingID,iosDevicePairingIDLen); // +iosDevicePairingID - memcpy(iosDeviceInfo+iosDeviceXLen+iosDevicePairingIDLen,iosDeviceLTPK,iosDeviceLTPKLen); // +iosDeviceLTPK - - uint8_t *iosDeviceSignature = tlv8.buf(kTLVType_Signature); // set iosDeviceSignature from TLV record (an Ed25519 should always be 64 bytes) - - if(crypto_sign_verify_detached(iosDeviceSignature, iosDeviceInfo, iosDeviceInfoLen, iosDeviceLTPK) != 0){ // verify signature of iosDeviceInfo using iosDeviceLTPK + if(crypto_sign_verify_detached(*itSignature, iosDeviceInfo, iosDeviceInfo.len(), *itPublicKey) != 0){ // verify signature of iosDeviceInfo using iosDeviceLTPK LOG0("\n*** ERROR: LPTK Signature Verification Failed\n\n"); - tlv8.clear(); // clear TLV records - tlv8.val(kTLVType_State,pairState_M6); // set State= - tlv8.val(kTLVType_Error,tagError_Authentication); // set Error=Authentication - tlvRespond(); // send response to client - pairStatus=pairState_M1; // reset pairStatus to first step of unpaired + responseTLV.add(kTLVType_State,pairState_M6); // set State= + responseTLV.add(kTLVType_Error,tagError_Authentication); // set Error=Authentication + tlvRespond(responseTLV); // send response to client + pairStatus=pairState_M1; // reset pairStatus to first step of unpaired return(0); } - addController(iosDevicePairingID,iosDeviceLTPK,true); // save Pairing ID and LTPK for this Controller with admin privileges + addController(*itIdentifier,*itPublicKey,true); // save Pairing ID and LTPK for this Controller with admin privileges // 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]; + TempBuffer accessoryX(32); hkdf.create(accessoryX,srp.sharedSecret,64,"Pair-Setup-Accessory-Sign-Salt","Pair-Setup-Accessory-Sign-Info"); // derive accessoryX from SRP Shared Secret using HKDF - const size_t accessoryXLen=32; - uint8_t *accessoryPairingID=accessory.ID; // set accessoryPairingID from storage - const size_t accessoryPairingIDLen=17; + // Concatenate accessoryX, Accessory ID, and Accessory PublicKey into accessoryInfo - uint8_t *accessoryLTPK=accessory.LTPK; // set accessoryLTPK (Ed25519 long-term public key) from storage - const size_t accessoryLTPKLen=32; + TempBuffer accessoryInfo(accessoryX.len()+sizeof(accessory.ID)+sizeof(accessory.LTPK)); + Utils::memcat(accessoryInfo,3,accessoryX.get(),accessoryX.len(),accessory.ID,sizeof(accessory.ID),accessory.LTPK,sizeof(accessory.LTPK)); - const size_t accessoryInfoLen=accessoryXLen+accessoryPairingIDLen+accessoryLTPKLen; // total size of accessoryInfo - uint8_t accessoryInfo[accessoryInfoLen]; + subTLV.clear(); // clear existing SUBTLV records - memcpy(accessoryInfo,accessoryX,accessoryXLen); // accessoryInfo = accessoryX - memcpy(accessoryInfo+accessoryXLen,accessoryPairingID,accessoryPairingIDLen); // +accessoryPairingID - memcpy(accessoryInfo+accessoryXLen+accessoryPairingIDLen,accessoryLTPK,accessoryLTPKLen); // +accessoryLTPK + itSignature=subTLV.add(kTLVType_Signature,64,NULL); // create blank Signature TLV with space for 64 bytes - tlv8.clear(); // clear existing TLV records + crypto_sign_detached(*itSignature,NULL,accessoryInfo,accessoryInfo.len(),accessory.LTSK); // produce signature of accessoryInfo using AccessoryLTSK (Ed25519 long-term secret key) - crypto_sign_detached(tlv8.buf(kTLVType_Signature,64),NULL,accessoryInfo,accessoryInfoLen,accessory.LTSK); // produce signature of accessoryInfo using AccessoryLTSK (Ed25519 long-term secret key) - - tlv8.buf(kTLVType_Identifier,accessoryPairingID,accessoryPairingIDLen); // set Identifier TLV record as accessoryPairingID - tlv8.buf(kTLVType_PublicKey,accessoryLTPK,accessoryLTPKLen); // set PublicKey TLV record as accessoryLTPK + subTLV.add(kTLVType_Identifier,sizeof(accessory.ID),accessory.ID); // set Identifier TLV record as accessoryPairingID + subTLV.add(kTLVType_PublicKey,sizeof(accessory.LTPK),accessory.LTPK); // set PublicKey TLV record as accessoryLTPK LOG2("------- ENCRYPTING SUB-TLVS -------\n"); - tlv8.print(2); + subTLV.print(); - size_t subTLVLen=tlv8.pack(NULL); // get size of buffer needed to store sub-TLV - uint8_t subTLV[subTLVLen]; - subTLVLen=tlv8.pack(subTLV); // create sub-TLV by packing Identifier, PublicKey, and Signature TLV records together + TempBuffer subPack(subTLV.pack_size()); // create sub-TLV by packing Identifier, PublicKey and Signature TLV records together + subTLV.pack(subPack); - tlv8.clear(); // clear existing TLV records + // Encrypt the subTLV data using the same sessionKey as above with ChaCha20-Poly1305 - // Final step is to encrypt the subTLV data using the same sessionKey as above with ChaCha20-Poly1305 - - unsigned long long edLen; + itEncryptedData=responseTLV.add(kTLVType_EncryptedData,subPack.len()+crypto_aead_chacha20poly1305_IETF_ABYTES,NULL); //create blank EncryptedData TLV with space for subTLV + Authentication Tag - crypto_aead_chacha20poly1305_ietf_encrypt(tlv8.buf(kTLVType_EncryptedData),&edLen,subTLV,subTLVLen,NULL,0,NULL,(unsigned char *)"\x00\x00\x00\x00PS-Msg06",sessionKey); - + crypto_aead_chacha20poly1305_ietf_encrypt(*itEncryptedData,NULL,subPack,subPack.len(),NULL,0,NULL,(unsigned char *)"\x00\x00\x00\x00PS-Msg06",sessionKey); + LOG2("---------- END SUB-TLVS! ----------\n"); - tlv8.buf(kTLVType_EncryptedData,edLen); // set length of EncryptedData TLV record, which should now include the Authentication Tag at the end as required by HAP - tlv8.val(kTLVType_State,pairState_M6); // set State= + responseTLV.add(kTLVType_State,pairState_M6); // set State= - tlvRespond(); // send response to client + tlvRespond(responseTLV); // send response to client - mdns_service_txt_item_set("_hap","_tcp","sf","0"); // broadcast new status + mdns_service_txt_item_set("_hap","_tcp","sf","0"); // broadcast new status LOG1("\n*** ACCESSORY PAIRED! ***\n"); STATUS_UPDATE(on(),HS_PAIRED) - if(homeSpan.pairCallback) // if set, invoke user-defined Pairing Callback to indicate device has been paired + if(homeSpan.pairCallback) // if set, invoke user-defined Pairing Callback to indicate device has been paired homeSpan.pairCallback(true); return(1); diff --git a/src/HAP.h b/src/HAP.h index f411a16..d587cb2 100644 --- a/src/HAP.h +++ b/src/HAP.h @@ -65,10 +65,10 @@ struct Nonce { // Paired Controller Structure for Permanently-Stored Data struct Controller { - boolean allocated=false; // DEPRECATED (but needed for backwards compatability with original NVS storage of Controller info) - boolean admin; // Controller has admin privileges - uint8_t ID[36]; // Pairing ID - uint8_t LTPK[32]; // Long Term Ed2519 Public Key + boolean allocated=false; // DEPRECATED (but needed for backwards compatability with original NVS storage of Controller info) + boolean admin; // Controller has admin privileges + uint8_t ID[36]; // Pairing ID + uint8_t LTPK[crypto_sign_PUBLICKEYBYTES]; // Long Term Ed2519 Public Key Controller(){} @@ -85,9 +85,9 @@ struct Controller { // Accessory Structure for Permanently-Stored Data struct Accessory { - uint8_t ID[17]; // Pairing ID in form "XX:XX:XX:XX:XX:XX" - uint8_t LTSK[64]; // secret key for Ed25519 signatures - uint8_t LTPK[32]; // public key for Ed25519 signatures + uint8_t ID[17]; // Pairing ID in form "XX:XX:XX:XX:XX:XX" + uint8_t LTSK[crypto_sign_SECRETKEYBYTES]; // secret key for Ed25519 signatures + uint8_t LTPK[crypto_sign_PUBLICKEYBYTES]; // public key for Ed25519 signatures }; ///////////////////////////////////////////////// diff --git a/src/Utils.cpp b/src/Utils.cpp index 7af158e..ca22729 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -87,6 +87,19 @@ String Utils::mask(char *c, int n){ return(s); } // mask +////////////////////////////////////// + +void Utils::memcat(uint8_t *buf, size_t n...){ + va_list args; + va_start(args, n); + while(n-->0){ + uint8_t *addBuf=va_arg(args,uint8_t *); + size_t len=va_arg(args,size_t); + memcpy(buf,addBuf,len); + buf+=len; + } +} + //////////////////////////////// // PushButton // //////////////////////////////// diff --git a/src/Utils.h b/src/Utils.h index c89faa7..1842aed 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -42,6 +42,7 @@ namespace Utils { char *readSerial(char *c, int max); // read serial port into 'c' until , but storing only first 'max' characters (the rest are discarded) String mask(char *c, int n); // simply utility that creates a String from 'c' with all except the first and last 'n' characters replaced by '*' +void memcat(uint8_t *buf, size_t n...); }