diff --git a/src/HAP.cpp b/src/HAP.cpp index caa5824..abcd6f4 100644 --- a/src/HAP.cpp +++ b/src/HAP.cpp @@ -385,7 +385,7 @@ int HAPClient::postPairSetupURL(uint8_t *content, size_t len){ auto itPublicKey=responseTLV.add(kTLVType_PublicKey,384,NULL); // create blank PublicKey TLV with space for 384 bytes - if(srp==NULL) // create instance of SRP (if not already created) to persist until Pairing is fully complete + if(srp==NULL) // create instance of SRP (if not already created) to persist until Pairing-Setup M5 completes srp=new SRP6A; TempBuffer verifyData; // retrieve verification data (should already be stored in NVS) @@ -416,7 +416,7 @@ int HAPClient::postPairSetupURL(uint8_t *content, size_t len){ pairStatus=pairState_M1; // reset pairStatus to first step of unpaired return(0); }; - + srp->createSessionKey(*itPublicKey,(*itPublicKey).len); // create session key, K, from client Public Key, A if(!srp->verifyClientProof(*itClientProof)){ // verify client Proof, M1 @@ -430,19 +430,22 @@ int HAPClient::postPairSetupURL(uint8_t *content, size_t len){ auto itAccProof=responseTLV.add(kTLVType_Proof,64,NULL); // create blank accessory Proof TLV with space for 64 bytes srp->createAccProof(*itAccProof); // M1 has been successully verified; now create accessory Proof M2 + tlvRespond(responseTLV); // send response to client pairStatus=pairState_M5; // set next expected pair-state request from client + return(1); } break; case pairState_M5:{ // 'Exchange Request' + responseTLV.add(kTLVType_State,pairState_M6); // set State= + auto itEncryptedData=iosTLV.find(kTLVType_EncryptedData); if(iosTLV.len(itEncryptedData)<=0){ LOG0("\n*** ERROR: Required 'EncryptedData' TLV record 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) tlvRespond(responseTLV); // send response to client pairStatus=pairState_M1; // reset pairStatus to first step of unpaired @@ -457,18 +460,17 @@ int HAPClient::postPairSetupURL(uint8_t *content, size_t len){ // Note the SALT and INFO text fields used by HKDF to create this Session Key are NOT the same as those for creating iosDeviceX. // The iosDeviceX HKDF calculations are separate and will be performed further below with the SALT and INFO as specified in the HAP docs. - TempBuffer srpSessionKey(crypto_box_PUBLICKEYBYTES); // temporary space - used only in this block - hkdf.create(srpSessionKey,srp->sharedSecret,64,"Pair-Setup-Encrypt-Salt","Pair-Setup-Encrypt-Info"); // create SessionKey + TempBuffer sessionKey(crypto_box_PUBLICKEYBYTES); // temporary space - used only in this block + hkdf.create(sessionKey,srp->K,64,"Pair-Setup-Encrypt-Salt","Pair-Setup-Encrypt-Info"); // create SessionKey LOG2("------- DECRYPTING SUB-TLVS -------\n"); // use SessionKey to decrypt encryptedData TLV with padded nonce="PS-Msg05" - TempBuffer decrypted((*itEncryptedData).len-crypto_aead_chacha20poly1305_IETF_ABYTES); // temporary storage for decrypted data + TempBuffer decrypted((*itEncryptedData).len-crypto_aead_chacha20poly1305_IETF_ABYTES); // temporary storage for decrypted data - if(crypto_aead_chacha20poly1305_ietf_decrypt(decrypted, NULL, NULL, *itEncryptedData, (*itEncryptedData).len, NULL, 0, (unsigned char *)"\x00\x00\x00\x00PS-Msg05", srpSessionKey)==-1){ + if(crypto_aead_chacha20poly1305_ietf_decrypt(decrypted, NULL, NULL, *itEncryptedData, (*itEncryptedData).len, NULL, 0, (unsigned char *)"\x00\x00\x00\x00PS-Msg05", sessionKey)==-1){ LOG0("\n*** ERROR: Exchange-Request Authentication Failed\n\n"); - 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 @@ -486,7 +488,6 @@ int HAPClient::postPairSetupURL(uint8_t *content, size_t len){ if(subTLV.len(itIdentifier)!=hap_controller_IDBYTES || 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) tlvRespond(responseTLV); // send response to client pairStatus=pairState_M1; // reset pairStatus to first step of unpaired @@ -499,15 +500,14 @@ int HAPClient::postPairSetupURL(uint8_t *content, size_t len){ // Note that the SALT and INFO text fields now match those in HAP Section 5.6.6.1 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 + hkdf.create(iosDeviceX,srp->K,64,"Pair-Setup-Controller-Sign-Salt","Pair-Setup-Controller-Sign-Info"); // derive iosDeviceX (32 bytes) from SRP Shared Secret using HKDF // Concatenate iosDeviceX, IOS ID, and IOS PublicKey into iosDeviceInfo TempBuffer iosDeviceInfo(iosDeviceX,iosDeviceX.len(),(*itIdentifier).val.get(),(*itIdentifier).len,(*itPublicKey).val.get(),(*itPublicKey).len,NULL); - if(crypto_sign_verify_detached(*itSignature, iosDeviceInfo, iosDeviceInfo.len(), *itPublicKey) != 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"); - 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 @@ -519,7 +519,7 @@ int HAPClient::postPairSetupURL(uint8_t *content, size_t len){ // Now perform the above steps in reverse to securely transmit the AccessoryLTPK to the Controller (HAP Section 5.6.6.2) 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 + hkdf.create(accessoryX,srp->K,64,"Pair-Setup-Accessory-Sign-Salt","Pair-Setup-Accessory-Sign-Info"); // derive accessoryX from SRP Shared Secret using HKDF // Concatenate accessoryX, Accessory ID, and Accessory PublicKey into accessoryInfo @@ -531,28 +531,28 @@ int HAPClient::postPairSetupURL(uint8_t *content, size_t len){ crypto_sign_detached(*itSignature,NULL,accessoryInfo,accessoryInfo.len(),accessory.LTSK); // produce signature of accessoryInfo using AccessoryLTSK (Ed25519 long-term secret key) - subTLV.add(kTLVType_Identifier,hap_accessory_IDBYTES,accessory.ID); // set Identifier TLV record as accessoryPairingID - subTLV.add(kTLVType_PublicKey,crypto_sign_PUBLICKEYBYTES,accessory.LTPK); // set PublicKey TLV record as accessoryLTPK + subTLV.add(kTLVType_Identifier,hap_accessory_IDBYTES,accessory.ID); // set Identifier TLV record as accessoryPairingID + subTLV.add(kTLVType_PublicKey,crypto_sign_PUBLICKEYBYTES,accessory.LTPK); // set PublicKey TLV record as accessoryLTPK LOG2("------- ENCRYPTING SUB-TLVS -------\n"); subTLV.print(); - TempBuffer subPack(subTLV.pack_size()); // 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); // Encrypt the subTLV data using the same SRP Session Key as above with ChaCha20-Poly1305 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(*itEncryptedData,NULL,subPack,subPack.len(),NULL,0,NULL,(unsigned char *)"\x00\x00\x00\x00PS-Msg06",srpSessionKey); + 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"); - - responseTLV.add(kTLVType_State,pairState_M6); // set State= tlvRespond(responseTLV); // send response to client + delete srp; // delete SRP - no longer needed once pairing is completed + mdns_service_txt_item_set("_hap","_tcp","sf","0"); // broadcast new status LOG1("\n*** ACCESSORY PAIRED! ***\n"); diff --git a/src/SRP.cpp b/src/SRP.cpp index f58a212..a204375 100644 --- a/src/SRP.cpp +++ b/src/SRP.cpp @@ -49,10 +49,6 @@ SRP6A::SRP6A(){ mbedtls_mpi_init(&S); mbedtls_mpi_init(&k); mbedtls_mpi_init(&u); - mbedtls_mpi_init(&K); - mbedtls_mpi_init(&M1); - mbedtls_mpi_init(&M1V); - mbedtls_mpi_init(&M2); mbedtls_mpi_init(&_rr); mbedtls_mpi_init(&t1); mbedtls_mpi_init(&t2); @@ -80,10 +76,6 @@ SRP6A::~SRP6A(){ mbedtls_mpi_free(&S); mbedtls_mpi_free(&k); mbedtls_mpi_free(&u); - mbedtls_mpi_free(&K); - mbedtls_mpi_free(&M1); - mbedtls_mpi_free(&M1V); - mbedtls_mpi_free(&M2); mbedtls_mpi_free(&_rr); mbedtls_mpi_free(&t1); mbedtls_mpi_free(&t2); @@ -117,9 +109,10 @@ void SRP6A::createVerifyCode(const char *setupCode, Verification *vData){ // compute v = g^x %N mbedtls_mpi_exp_mod(&v,&g,&x,&N,&_rr); // create verifier, v (_rr is an internal "helper" structure that mbedtls uses to speed up subsequent exponential calculations) - mbedtls_mpi_write_binary(&v,vData->verifyCode,384); // write v into verifyCode + mbedtls_mpi_write_binary(&v,vData->verifyCode,384); // write v into verifyCode (padding with initial zeros is less than 384 bytes) free(icp); + } ////////////////////////////////////// @@ -147,14 +140,14 @@ void SRP6A::createPublicKey(const Verification *vData, uint8_t *publicKey){ mbedtls_sha512_ret(tBuf,768,tHash,0); // create hash of data mbedtls_mpi_read_binary(&k,tHash,64); // load hash result into k - // compute B = (kv + g^b) %N + // compute B = (k*v + g^b) %N mbedtls_mpi_mul_mpi(&t1,&k,&v); // t1 = k*v mbedtls_mpi_exp_mod(&t2,&g,&b,&N,&_rr); // t2 = g^b %N mbedtls_mpi_add_mpi(&t3,&t1,&t2); // t3 = t1 + t2 mbedtls_mpi_mod_mpi(&B,&t3,&N); // B = t3 %N = ACCESSORY PUBLIC KEY - mbedtls_mpi_write_binary(&B,publicKey,384); // write B into publicKey + mbedtls_mpi_write_binary(&B,publicKey,384); // write B into publicKey (padding with initial zeros is less than 384 bytes) } @@ -169,25 +162,22 @@ void SRP6A::createSessionKey(const uint8_t *publicKey, size_t len){ // compute u = SHA512( PAD(A) | PAD(B) ) - mbedtls_mpi_write_binary(&A,tBuf,384); // write A into first half of staging buffer (will pad to fill 384 bytes) - mbedtls_mpi_write_binary(&B,tBuf+384,384); // write B into second half of staging buffer (will pad to fill 384 bytes) + mbedtls_mpi_write_binary(&A,tBuf,384); // write A into first half of staging buffer (padding with initial zeros is less than 384 bytes) + mbedtls_mpi_write_binary(&B,tBuf+384,384); // write B into second half of staging buffer (padding with initial zeros is less than 384 bytes) mbedtls_sha512_ret(tBuf,768,tHash,0); // create hash of data mbedtls_mpi_read_binary(&u,tHash,64); // load hash result into mpi structure u - // compute S = (Av^u)^b %N + // compute S = (A * v^u)^b %N mbedtls_mpi_exp_mod(&t1,&v,&u,&N,&_rr); // t1 = v^u %N mbedtls_mpi_mul_mpi(&t2,&A,&t1); // t2 = A*t1 mbedtls_mpi_mod_mpi(&t1,&t2,&N); // t1 = t2 %N (this is needed to reduce size of t2 before next calculation) mbedtls_mpi_exp_mod(&S,&t1,&b,&N,&_rr); // S = t1^b %N - // compute K = SHA512( S ) + // compute K = SHA512( PAD(S) ) mbedtls_mpi_write_binary(&S,tBuf,384); // write S into staging buffer (only first half of buffer will be used) - mbedtls_sha512_ret(tBuf,384,tHash,0); // create hash of data - mbedtls_mpi_read_binary(&K,tHash,64); // load hash result into mpi structure K. This is the SRP SHARED SECRET KEY - - mbedtls_mpi_write_binary(&K,sharedSecret,64); // store SHARED SECRET in easy-to-use binary (uint8_t) format + mbedtls_sha512_ret(tBuf,384,K,0); // create hash of data - this is the SRP SHARED SESSION KEY, K } @@ -199,12 +189,12 @@ int SRP6A::verifyClientProof(const uint8_t *proof){ TempBuffer tBuf(976); // temporary buffer for staging TempBuffer tHash(64); // temporary buffer for storing SHA-512 results - mbedtls_mpi_read_binary(&M1,proof,64); // load client Proof into M1 + memcpy(M1,proof,64); // load client Proof into M1 size_t count=0; // total number of bytes for final hash size_t sLen; - // compute M1V + // compute M1V = SHA512( SHA512(N) xor SHA512(g) | SHA512(I) | s | A | B | K ) mbedtls_mpi_write_binary(&N,tBuf,384); // write N into staging buffer mbedtls_sha512_ret(tBuf,384,tHash,0); // create hash of data @@ -225,15 +215,12 @@ int SRP6A::verifyClientProof(const uint8_t *proof){ mbedtls_mpi_write_binary(&B,tBuf+count,sLen); // concatenate B to staging buffer. Note B is NOT padded with leading zeros (so may be less than 384 bytes) count+=sLen; // increment total bytes written to staging buffer - mbedtls_mpi_write_binary(&K,tBuf+count,64); // concatenate K to staging buffer (should always be 64 bytes since it is a hashed value) + memcpy(tBuf+count,K,64); // concatenate K to staging buffer (should always be 64 bytes since it is a hashed value) count+=64; // final total of bytes written to staging buffer - mbedtls_sha512_ret(tBuf,count,tHash,0); // create hash of data - mbedtls_mpi_read_binary(&M1V,tHash,64); // load hash result into mpi structure M1V + mbedtls_sha512_ret(tBuf,count,tHash,0); // create hash of data - this is M1V - // check that client Proof M1 matches M1V - - if(!mbedtls_mpi_cmp_mpi(&M1,&M1V)) // cmp_mpi uses same logic as strcmp: returns 0 if EQUAL, otherwise +/- 1 + if(!memcmp(M1,tHash,64)) // check that client Proof M1 matches M1V return(1); // success - proof from HAP Client is verified return(0); @@ -243,18 +230,15 @@ int SRP6A::verifyClientProof(const uint8_t *proof){ void SRP6A::createAccProof(uint8_t *proof){ - uint8_t tBuf[512]; // temporary buffer for staging + TempBuffer tBuf(512); // temporary buffer for staging - // compute M2 = H( A | M1 | K ) + // compute M2 = SHA512( A | M1 | K ) mbedtls_mpi_write_binary(&A,tBuf,384); // write A into staging buffer - mbedtls_mpi_write_binary(&M1,tBuf+384,64); // concatenate M1 (now verified) to staging buffer - mbedtls_mpi_write_binary(&K,tBuf+448,64); // concatenate K to staging buffer - mbedtls_sha512_ret(tBuf,512,tBuf,0); // create hash of data - mbedtls_mpi_read_binary(&M2,tBuf,64); // load hash results into mpi structure M2 - - mbedtls_mpi_write_binary(&M2,proof,64); // write M2 into proof - + memcpy(tBuf+384,M1,64); // concatenate M1 (now verified) to staging buffer + memcpy(tBuf+448,K,64); // concatenate K to staging buffer + mbedtls_sha512_ret(tBuf,512,proof,0); // create hash of data writing directly to proof - this is M2 + } ////////////////////////////////////// diff --git a/src/SRP.h b/src/SRP.h index 4ac73dc..50c3088 100644 --- a/src/SRP.h +++ b/src/SRP.h @@ -88,18 +88,13 @@ struct SRP6A { mbedtls_mpi A; // A - public key RECEIVED from HAP Client (max 384 bytes) mbedtls_mpi u; // u = H(PAD(A) | PAB(B)) - "u-factor" (64 bytes) mbedtls_mpi S; // S = (A*v^u)^b %N - SRP shared "premaster" key, based on accessory private key and client public key (max 384 bytes) - mbedtls_mpi K; // K = H( S ) - SRP SHARED SECRET KEY (64 bytes) - mbedtls_mpi M1; // M1 - proof RECEIVED from HAP Client (64 bytes) - mbedtls_mpi M1V; // M1V - accessory's independent computation of M1 to verify proof (see code for details of computation) - mbedtls_mpi M2; // M2 - accessory's counter-proof to send to HAP Client after M1=M1V has been verified (64 bytes) - - mbedtls_mpi t1; // temporary mpi structures for intermediate results - mbedtls_mpi t2; - mbedtls_mpi t3; - + uint8_t K[64]; // K = H(S) - SRP SHARED SECRET KEY (64 bytes) + uint8_t M1[64]; // M1 - proof RECEIVED from HAP Client (64 bytes) + mbedtls_mpi t1; // temp1 - temporary mpi structures for intermediate results + mbedtls_mpi t2; // temp2 - temporary mpi structures for intermediate results + mbedtls_mpi t3; // temp3 - temporary mpi structures for intermediate results mbedtls_mpi _rr; // _rr - temporary "helper" for large exponential modulus calculations - uint8_t sharedSecret[64]; // permanent storage for binary version of SHARED SECRET KEY for ease of use upstream SRP6A(); // initializes N, G, and computes k ~SRP6A();