def test_long_values_decode_bytearray_to_list(self): example = bytearray.fromhex('060103' + ('09FF' + 255 * '61' + '092D' + 45 * '61') + '010568656c6c6f') expected = [[6, bytearray(b'\x03')], [9, bytearray(300 * b'a')], [1, bytearray(b'hello')]] data = TLV.decode_bytearray(example) self.assertListEqual(data, expected)
def test_separator_list(self): val = [ [TLV.kTLVType_State, TLV.M3], TLV.kTLVType_Separator_Pair, [TLV.kTLVType_State, TLV.M4], ] res = TLV.decode_bytearray(TLV.encode_list(val)) self.assertEqual(val, res)
def test_long_values_2(self): val = [ [TLV.kTLVType_State, TLV.M3], [TLV.kTLVType_Certificate, (150 * 'a' + 150 * 'b').encode()], [TLV.kTLVType_Identifier, 'hello'.encode()], ] res = TLV.decode_bytearray(TLV.encode_list(val)) self.assertEqual(val, res)
def perform_pair_setup_part2(pin, ios_pairing_id, write_fun, salt, server_public_key): """ Performs a pair setup operation as described in chapter 4.7 page 39 ff. :param pin: the setup code from the accessory :param ios_pairing_id: the id of the simulated ios device :param write_fun: a function that takes a bytes representation of a TLV, the expected keys as list and returns decoded TLV as list :return: a dict with the ios device's part of the pairing information :raises UnavailableError: if the device is already paired :raises MaxTriesError: if the device received more than 100 unsuccessful pairing attempts :raises BusyError: if a parallel pairing is ongoing :raises AuthenticationError: if the verification of the device's SRP proof fails :raises MaxPeersError: if the device cannot accept an additional pairing :raises IllegalData: if the verification of the accessory's data fails """ srp_client = SrpClient('Pair-Setup', pin) srp_client.set_salt(salt) srp_client.set_server_public_key(server_public_key) client_pub_key = srp_client.get_public_key() client_proof = srp_client.get_proof() response_tlv = TLV.encode_list([ (TLV.kTLVType_State, TLV.M3), (TLV.kTLVType_PublicKey, SrpClient.to_byte_array(client_pub_key)), (TLV.kTLVType_Proof, SrpClient.to_byte_array(client_proof)), ]) step4_expectations = [ TLV.kTLVType_State, TLV.kTLVType_Error, TLV.kTLVType_Proof ] response_tlv = write_fun(response_tlv, step4_expectations) # # Step #5 ios --> accessory (Exchange Request) (see page 43) # logging.debug('#5 ios -> accessory: send SRP exchange request') # M4 Verification (page 43) response_tlv = TLV.reorder(response_tlv, step4_expectations) assert response_tlv[0][0] == TLV.kTLVType_State and response_tlv[0][1] == TLV.M4, \ 'perform_pair_setup: State not M4' if response_tlv[1][0] == TLV.kTLVType_Error: error_handler(response_tlv[1][1], 'step 5') assert response_tlv[1][ 0] == TLV.kTLVType_Proof, 'perform_pair_setup: Not a proof' if not srp_client.verify_servers_proof(response_tlv[1][1]): raise AuthenticationError('Step #5: wrong proof!') # M5 Request generation (page 44) session_key = srp_client.get_session_key() ios_device_ltsk, ios_device_ltpk = ed25519.create_keypair() # reversed: # Pair-Setup-Encrypt-Salt instead of Pair-Setup-Controller-Sign-Salt # Pair-Setup-Encrypt-Info instead of Pair-Setup-Controller-Sign-Info hkdf_inst = hkdf.Hkdf('Pair-Setup-Controller-Sign-Salt'.encode(), SrpClient.to_byte_array(session_key), hash=hashlib.sha512) ios_device_x = hkdf_inst.expand('Pair-Setup-Controller-Sign-Info'.encode(), 32) hkdf_inst = hkdf.Hkdf('Pair-Setup-Encrypt-Salt'.encode(), SrpClient.to_byte_array(session_key), hash=hashlib.sha512) session_key = hkdf_inst.expand('Pair-Setup-Encrypt-Info'.encode(), 32) ios_device_pairing_id = ios_pairing_id.encode() ios_device_info = ios_device_x + ios_device_pairing_id + ios_device_ltpk.to_bytes( ) ios_device_signature = ios_device_ltsk.sign(ios_device_info) sub_tlv = [(TLV.kTLVType_Identifier, ios_device_pairing_id), (TLV.kTLVType_PublicKey, ios_device_ltpk.to_bytes()), (TLV.kTLVType_Signature, ios_device_signature)] sub_tlv_b = TLV.encode_list(sub_tlv) # taking tge iOSDeviceX as key was reversed from # https://github.com/KhaosT/HAP-NodeJS/blob/2ea9d761d9bd7593dd1949fec621ab085af5e567/lib/HAPServer.js # function handlePairStepFive calling encryption.encryptAndSeal encrypted_data_with_auth_tag = chacha20_aead_encrypt( bytes(), session_key, 'PS-Msg05'.encode(), bytes([0, 0, 0, 0]), sub_tlv_b) tmp = bytearray(encrypted_data_with_auth_tag[0]) tmp += encrypted_data_with_auth_tag[1] response_tlv = [(TLV.kTLVType_State, TLV.M5), (TLV.kTLVType_EncryptedData, tmp)] body = TLV.encode_list(response_tlv) step6_expectations = [ TLV.kTLVType_State, TLV.kTLVType_Error, TLV.kTLVType_EncryptedData ] response_tlv = write_fun(body, step6_expectations) # # Step #7 ios (Verification) (page 47) # response_tlv = TLV.reorder(response_tlv, step6_expectations) assert response_tlv[0][0] == TLV.kTLVType_State and response_tlv[0][ 1] == TLV.M6, 'perform_pair_setup: State not M6' if response_tlv[1][0] == TLV.kTLVType_Error: error_handler(response_tlv[1][1], 'step 7') assert response_tlv[1][ 0] == TLV.kTLVType_EncryptedData, 'perform_pair_setup: No encrypted data' decrypted_data = chacha20_aead_decrypt(bytes(), session_key, 'PS-Msg06'.encode(), bytes([0, 0, 0, 0]), response_tlv[1][1]) if decrypted_data is False: raise homekit.exception.IllegalData('step 7') response_tlv = TLV.decode_bytearray(decrypted_data) response_tlv = TLV.reorder(response_tlv, [ TLV.kTLVType_Identifier, TLV.kTLVType_PublicKey, TLV.kTLVType_Signature ]) assert response_tlv[2][ 0] == TLV.kTLVType_Signature, 'perform_pair_setup: No signature' accessory_sig = response_tlv[2][1] assert response_tlv[0][ 0] == TLV.kTLVType_Identifier, 'perform_pair_setup: No identifier' accessory_pairing_id = response_tlv[0][1] assert response_tlv[1][ 0] == TLV.kTLVType_PublicKey, 'perform_pair_setup: No public key' accessory_ltpk = response_tlv[1][1] hkdf_inst = hkdf.Hkdf('Pair-Setup-Accessory-Sign-Salt'.encode(), SrpClient.to_byte_array( srp_client.get_session_key()), hash=hashlib.sha512) accessory_x = hkdf_inst.expand('Pair-Setup-Accessory-Sign-Info'.encode(), 32) accessory_info = accessory_x + accessory_pairing_id + accessory_ltpk e25519s = ed25519.VerifyingKey(bytes(response_tlv[1][1])) try: e25519s.verify(bytes(accessory_sig), bytes(accessory_info)) except AssertionError: raise InvalidSignatureError('step #7') return { 'AccessoryPairingID': response_tlv[0][1].decode(), 'AccessoryLTPK': hexlify(response_tlv[1][1]).decode(), 'iOSPairingId': ios_pairing_id, 'iOSDeviceLTSK': ios_device_ltsk.to_ascii(encoding='hex').decode()[:64], 'iOSDeviceLTPK': hexlify(ios_device_ltpk.to_bytes()).decode() }