def _pairing_two(self, tlv_objects): """Obtain the challenge from the client (A) and client's proof that it knows the password (M). Verify M and generate the server's proof based on A (H_AMK). Send the H_AMK to the client. @param tlv_objects: The TLV data received from the client. @type tlv_object: dict """ logger.debug("Pairing [2/5]") A = tlv_objects[HAP_TLV_TAGS.PUBLIC_KEY] M = tlv_objects[HAP_TLV_TAGS.PASSWORD_PROOF] verifier = self.accessory_handler.srp_verifier verifier.set_A(A) hamk = verifier.verify(M) if hamk is None: # Probably the provided pincode was wrong. response = tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b'\x04', HAP_TLV_TAGS.ERROR_CODE, HAP_OPERATION_CODE.INVALID_REQUEST) self.send_response(200) self.send_header("Content-Type", self.PAIRING_RESPONSE_TYPE) self.end_response(response) return data = tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b'\x04', HAP_TLV_TAGS.PASSWORD_PROOF, hamk) self.send_response(200) self.send_header("Content-Type", self.PAIRING_RESPONSE_TYPE) self.end_response(data)
def _pairing_five(self, client_username, client_ltpk, encryption_key): """At that point we know the client has the accessory password and has a valid key pair. Add it as a pair and send a sever proof. Parameters are as for _pairing_four. """ logging.debug("Pairing [5/5]") session_key = self._accessory_driver.srp_verifier.get_session_key() output_key = hap_hkdf(long_to_bytes(session_key), PAIRING_5_SALT, PAIRING_5_INFO) server_public = self._state.public_key.to_bytes() mac = self._state.mac.encode() material = output_key + mac + server_public private_key = self._state.private_key server_proof = private_key.sign(material) message = tlv.encode(HAP_TLV_TAGS.USERNAME, mac, HAP_TLV_TAGS.PUBLIC_KEY, server_public, HAP_TLV_TAGS.PROOF, server_proof) cipher = CHACHA20_POLY1305(encryption_key, "python") aead_message = bytes( cipher.seal(PAIRING_5_NONCE, bytearray(message), b"")) client_uuid = uuid.UUID(str(client_username, "utf-8")) should_confirm = self._accessory_driver.pair(client_uuid, client_ltpk) if not should_confirm: self.send_response(500) return return tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b'\x06', HAP_TLV_TAGS.ENCRYPTED_DATA, aead_message)
def _pair_verify_two(self, tlv_objects): """Verify the client proof and upgrade to encrypted transport. @param tlv_objects: The TLV data received from the client. @type tlv_object: dict """ logger.debug("Pair verify [2/2]") encrypted_data = tlv_objects[HAP_TLV_TAGS.ENCRYPTED_DATA] cipher = ChaCha20Poly1305(self.enc_context["pre_session_key"]) decrypted_data = cipher.decrypt(self.PVERIFY_2_NONCE, bytes(encrypted_data), b"") assert decrypted_data is not None # TODO: dec_tlv_objects = tlv.decode(bytes(decrypted_data)) client_username = dec_tlv_objects[HAP_TLV_TAGS.USERNAME] material = (self.enc_context["client_public"] + client_username + self.enc_context["public_key"].serialize()) client_uuid = uuid.UUID(str(client_username, "ascii")) perm_client_public = self.state.paired_clients.get(client_uuid) if perm_client_public is None: logger.debug( "Client %s attempted pair verify without being paired first.", client_uuid, ) self.send_response(200) self.send_header("Content-Type", self.PAIRING_RESPONSE_TYPE) data = tlv.encode(HAP_TLV_TAGS.ERROR_CODE, HAP_OPERATION_CODE.INVALID_REQUEST) self.end_response(data) return verifying_key = ed25519.VerifyingKey(perm_client_public) try: verifying_key.verify(dec_tlv_objects[HAP_TLV_TAGS.PROOF], material) except ed25519.BadSignatureError: logger.error("Bad signature, abort.") self.send_response(200) self.send_header("Content-Type", self.PAIRING_RESPONSE_TYPE) data = tlv.encode(HAP_TLV_TAGS.ERROR_CODE, HAP_OPERATION_CODE.INVALID_REQUEST) self.end_response(data) return logger.debug( "Pair verify with client '%s' completed. Switching to " "encrypted transport.", self.client_address, ) data = tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b"\x04") self.send_response(200) self.send_header("Content-Type", self.PAIRING_RESPONSE_TYPE) self.end_response(data) self.response.shared_key = self.enc_context["shared_key"] self.is_encrypted = True del self.enc_context
def _pairing_five(self, client_username, client_ltpk, encryption_key): """At that point we know the client has the accessory password and has a valid key pair. Add it as a pair and send a sever proof. Parameters are as for _pairing_four. """ logger.debug("%s: Pairing [5/5]", self.client_address) session_key = self.accessory_handler.srp_verifier.get_session_key() output_key = hap_hkdf(long_to_bytes(session_key), self.PAIRING_5_SALT, self.PAIRING_5_INFO) server_public = self.state.public_key.to_bytes() mac = self.state.mac.encode() material = output_key + mac + server_public private_key = self.state.private_key server_proof = private_key.sign(material) message = tlv.encode( HAP_TLV_TAGS.USERNAME, mac, HAP_TLV_TAGS.PUBLIC_KEY, server_public, HAP_TLV_TAGS.PROOF, server_proof, ) cipher = ChaCha20Poly1305(encryption_key) aead_message = bytes( cipher.encrypt(self.PAIRING_5_NONCE, bytes(message), b"")) client_uuid = uuid.UUID(str(client_username, "utf-8")) should_confirm = self.accessory_handler.pair(client_uuid, client_ltpk) if not should_confirm: self.send_response_with_status( HTTPStatus.INTERNAL_SERVER_ERROR, HAP_SERVER_STATUS.INVALID_VALUE_IN_REQUEST, ) return tlv_data = tlv.encode( HAP_TLV_TAGS.SEQUENCE_NUM, HAP_TLV_STATES.M6, HAP_TLV_TAGS.ENCRYPTED_DATA, aead_message, ) self.response.pairing_changed = True self._send_tlv_pairing_response(tlv_data)
def test_attempt_to_pair_when_already_paired(driver): """Verify we respond with unavailable if already paired.""" driver.add_accessory(Accessory(driver, "TestAcc")) handler = hap_handler.HAPServerHandler(driver, "peername") handler.is_encrypted = False driver.pair( CLIENT_UUID, PUBLIC_KEY, ) response = hap_handler.HAPResponse() handler.response = response handler.request_body = tlv.encode( hap_handler.HAP_TLV_TAGS.SEQUENCE_NUM, hap_handler.HAP_TLV_STATES.M1, ) handler.handle_pairing() tlv_objects = tlv.decode(response.body) assert tlv_objects == { hap_handler.HAP_TLV_TAGS.SEQUENCE_NUM: hap_handler.HAP_TLV_STATES.M2, hap_handler.HAP_TLV_TAGS.ERROR_CODE: hap_handler.HAP_TLV_ERRORS.UNAVAILABLE, }
def test_invalid_pairing_three(driver): """Verify we respond with error with invalid request.""" driver.add_accessory(Accessory(driver, "TestAcc")) handler = hap_handler.HAPServerHandler(driver, "peername") handler.is_encrypted = False response = hap_handler.HAPResponse() handler.response = response handler.request_body = tlv.encode( hap_handler.HAP_TLV_TAGS.SEQUENCE_NUM, hap_handler.HAP_TLV_STATES.M5, hap_handler.HAP_TLV_TAGS.ENCRYPTED_DATA, b"", ) handler.accessory_handler.setup_srp_verifier() handler.accessory_handler.srp_verifier.set_A(b"") handler.handle_pairing() tlv_objects = tlv.decode(response.body) assert tlv_objects == { hap_handler.HAP_TLV_TAGS.SEQUENCE_NUM: hap_handler.HAP_TLV_STATES.M6, hap_handler.HAP_TLV_TAGS.ERROR_CODE: hap_handler.HAP_TLV_ERRORS.AUTHENTICATION, }
def test_list_pairings(driver): """Verify an encrypted list pairings request.""" driver.add_accessory(Accessory(driver, "TestAcc")) handler = hap_handler.HAPServerHandler(driver, "peername") handler.is_encrypted = True driver.pair( CLIENT_UUID, PUBLIC_KEY, ) assert CLIENT_UUID in driver.state.paired_clients response = hap_handler.HAPResponse() handler.response = response handler.request_body = tlv.encode(hap_handler.HAP_TLV_TAGS.REQUEST_TYPE, hap_handler.HAP_TLV_STATES.M5) handler.handle_pairings() tlv_objects = tlv.decode(response.body) assert tlv_objects == { hap_handler.HAP_TLV_TAGS.SEQUENCE_NUM: hap_handler.HAP_TLV_STATES.M2, hap_handler.HAP_TLV_TAGS.USERNAME: str(CLIENT_UUID).encode("utf8"), hap_handler.HAP_TLV_TAGS.PUBLIC_KEY: PUBLIC_KEY, hap_handler.HAP_TLV_TAGS.PERMISSIONS: hap_handler.HAP_PERMISSIONS.ADMIN, }
def test_pair_verify_one(driver): """Verify an unencrypted pair verify one.""" driver.add_accessory(Accessory(driver, "TestAcc")) handler = hap_handler.HAPServerHandler(driver, "peername") handler.is_encrypted = False driver.pair( CLIENT_UUID, PUBLIC_KEY, ) assert CLIENT_UUID in driver.state.paired_clients response = hap_handler.HAPResponse() handler.response = response handler.request_body = tlv.encode( hap_handler.HAP_TLV_TAGS.SEQUENCE_NUM, hap_handler.HAP_TLV_STATES.M1, hap_handler.HAP_TLV_TAGS.PUBLIC_KEY, PUBLIC_KEY, ) handler.handle_pair_verify() tlv_objects = tlv.decode(response.body) assert (tlv_objects[hap_handler.HAP_TLV_TAGS.SEQUENCE_NUM] == hap_handler.HAP_TLV_STATES.M2)
def test_add_pairing(driver): """Verify an encrypted add pairing request.""" driver.add_accessory(Accessory(driver, "TestAcc")) handler = hap_handler.HAPServerHandler(driver, "peername") handler.is_encrypted = True response = hap_handler.HAPResponse() handler.response = response handler.request_body = tlv.encode( hap_handler.HAP_TLV_TAGS.REQUEST_TYPE, hap_handler.HAP_TLV_STATES.M3, hap_handler.HAP_TLV_TAGS.USERNAME, str(CLIENT_UUID).encode("utf-8"), hap_handler.HAP_TLV_TAGS.PUBLIC_KEY, PUBLIC_KEY, hap_handler.HAP_TLV_TAGS.PERMISSIONS, hap_handler.HAP_PERMISSIONS.ADMIN, ) assert driver.state.paired is False handler.handle_pairings() assert tlv.decode(response.body) == { hap_handler.HAP_TLV_TAGS.SEQUENCE_NUM: hap_handler.HAP_TLV_STATES.M2 } assert driver.state.paired is True assert CLIENT_UUID in driver.state.paired_clients
def test_pair_verify_one_not_paired(driver): """Verify an unencrypted pair verify one.""" driver.add_accessory(Accessory(driver, "TestAcc")) handler = hap_handler.HAPServerHandler(driver, "peername") handler.is_encrypted = False response = hap_handler.HAPResponse() handler.response = response handler.request_body = tlv.encode( hap_handler.HAP_TLV_TAGS.SEQUENCE_NUM, hap_handler.HAP_TLV_STATES.M1, hap_handler.HAP_TLV_TAGS.PUBLIC_KEY, PUBLIC_KEY, ) handler.handle_pair_verify() tlv_objects = tlv.decode(response.body) assert tlv_objects == { hap_handler.HAP_TLV_TAGS.SEQUENCE_NUM: hap_handler.HAP_TLV_STATES.M2, hap_handler.HAP_TLV_TAGS.ERROR_CODE: hap_handler.HAP_TLV_ERRORS.AUTHENTICATION, }
def _pairing_two(self, tlv_objects): """Obtain the challenge from the client (A) and client's proof that it knows the password (M). Verify M and generate the server's proof based on A (H_AMK). Send the H_AMK to the client. @param tlv_objects: The TLV data received from the client. @type tlv_object: dict """ logger.debug("%s: Pairing [2/5]", self.client_address) A = tlv_objects[HAP_TLV_TAGS.PUBLIC_KEY] M = tlv_objects[HAP_TLV_TAGS.PASSWORD_PROOF] verifier = self.accessory_handler.srp_verifier verifier.set_A(A) hamk = verifier.verify(M) if hamk is None: # Probably the provided pincode was wrong. self._send_authentication_error_tlv_response(HAP_TLV_STATES.M4) return data = tlv.encode( HAP_TLV_TAGS.SEQUENCE_NUM, HAP_TLV_STATES.M4, HAP_TLV_TAGS.PASSWORD_PROOF, hamk, ) self._send_tlv_pairing_response(data)
def test_list_pairings_unencrypted(driver): """Verify an unencrypted list pairings request fails.""" driver.add_accessory(Accessory(driver, "TestAcc")) handler = hap_handler.HAPServerHandler(driver, "peername") handler.is_encrypted = False driver.pair( CLIENT_UUID, PUBLIC_KEY, ) assert CLIENT_UUID in driver.state.paired_clients response = hap_handler.HAPResponse() handler.response = response handler.request_body = tlv.encode(hap_handler.HAP_TLV_TAGS.REQUEST_TYPE, hap_handler.HAP_TLV_STATES.M5) handler.handle_pairings() tlv_objects = tlv.decode(response.body) assert tlv_objects == { hap_handler.HAP_TLV_TAGS.SEQUENCE_NUM: hap_handler.HAP_TLV_STATES.M2, hap_handler.HAP_TLV_TAGS.ERROR_CODE: hap_handler.HAP_TLV_ERRORS.AUTHENTICATION, }
def _get_streaming_status(self, stream_idx): """Get the streaming status in TLV format. Called when iOS reads the StreaminStatus ``Characteristic``. """ return tlv.encode(b'\x01', self._streaming_status[stream_idx], to_base64=True)
def _handle_remove_pairing(self, tlv_objects): """Remove pairing with the client.""" logging.debug('Removing client pairing.') client_username = tlv_objects[HAP_TLV_TAGS.USERNAME] client_uuid = uuid.UUID(str(client_username, "utf-8")) self._accessory_driver.unpair(client_uuid) return tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b"\x02")
def test_pair_verify_two_invaild_state(driver): """Verify an unencrypted pair verify two.""" driver.add_accessory(Accessory(driver, "TestAcc")) handler = hap_handler.HAPServerHandler(driver, "peername") handler.is_encrypted = False driver.pair( CLIENT_UUID, PUBLIC_KEY, ) assert CLIENT_UUID in driver.state.paired_clients response = hap_handler.HAPResponse() handler.response = response handler.request_body = tlv.encode( hap_handler.HAP_TLV_TAGS.SEQUENCE_NUM, hap_handler.HAP_TLV_STATES.M1, hap_handler.HAP_TLV_TAGS.PUBLIC_KEY, PUBLIC_KEY, ) handler.handle_pair_verify() tlv_objects = tlv.decode(response.body) assert (tlv_objects[hap_handler.HAP_TLV_TAGS.SEQUENCE_NUM] == hap_handler.HAP_TLV_STATES.M2) response = hap_handler.HAPResponse() handler.response = response handler.request_body = tlv.encode( hap_handler.HAP_TLV_TAGS.SEQUENCE_NUM, hap_handler.HAP_TLV_STATES.M3, hap_handler.HAP_TLV_TAGS.ENCRYPTED_DATA, b"invalid", ) handler.handle_pair_verify() tlv_objects = tlv.decode(response.body) assert tlv_objects == { hap_handler.HAP_TLV_TAGS.SEQUENCE_NUM: hap_handler.HAP_TLV_STATES.M4, hap_handler.HAP_TLV_TAGS.ERROR_CODE: hap_handler.HAP_TLV_ERRORS.AUTHENTICATION, }
def _send_authentication_error_tlv_response(self, sequence): """Send an authentication error tlv response.""" self._send_tlv_pairing_response( tlv.encode( HAP_TLV_TAGS.SEQUENCE_NUM, sequence, HAP_TLV_TAGS.ERROR_CODE, HAP_TLV_ERRORS.AUTHENTICATION, ))
def _pair_verify_one(self, tlv_objects): """Generate new session key pair and send a proof to the client. @param tlv_objects: The TLV data received from the client. @type tlv_object: dict """ logger.debug("Pair verify [1/2].") client_public = tlv_objects[HAP_TLV_TAGS.PUBLIC_KEY] private_key = curve25519.Private() public_key = private_key.get_public() shared_key = private_key.get_shared_key( curve25519.Public(client_public), # Key is hashed before being returned, we don't want it; This fixes that. lambda x: x, ) mac = self.state.mac.encode() material = public_key.serialize() + mac + client_public server_proof = self.state.private_key.sign(material) output_key = hap_hkdf(shared_key, self.PVERIFY_1_SALT, self.PVERIFY_1_INFO) self._set_encryption_ctx(client_public, private_key, public_key, shared_key, output_key) message = tlv.encode(HAP_TLV_TAGS.USERNAME, mac, HAP_TLV_TAGS.PROOF, server_proof) cipher = ChaCha20Poly1305(output_key) aead_message = bytes( cipher.encrypt(self.PVERIFY_1_NONCE, bytes(message), b"")) data = tlv.encode( HAP_TLV_TAGS.SEQUENCE_NUM, b"\x02", HAP_TLV_TAGS.ENCRYPTED_DATA, aead_message, HAP_TLV_TAGS.PUBLIC_KEY, public_key.serialize(), ) self.send_response(200) self.send_header("Content-Type", self.PAIRING_RESPONSE_TYPE) self.end_response(data)
def _handle_remove_pairing(self, tlv_objects): """Remove pairing with the client.""" client_username = tlv_objects[HAP_TLV_TAGS.USERNAME] client_uuid = uuid.UUID(str(client_username, "utf-8")) self.accessory_handler.unpair(client_uuid) data = tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b"\x02") self.send_response(200) self.send_header("Content-Type", self.PAIRING_RESPONSE_TYPE) self.end_response(data, True)
def _pair_verify_two(self, tlv_objects): """Verify the client proof and upgrade to encrypted transport. @param tlv_objects: The TLV data received from the client. @type tlv_object: dict """ logger.debug("%s: Pair verify [2/2]", self.client_address) encrypted_data = tlv_objects[HAP_TLV_TAGS.ENCRYPTED_DATA] cipher = ChaCha20Poly1305(self.enc_context["pre_session_key"]) try: decrypted_data = cipher.decrypt(self.PVERIFY_2_NONCE, bytes(encrypted_data), b"") except InvalidTag: self._send_authentication_error_tlv_response(HAP_TLV_STATES.M4) return assert decrypted_data is not None # TODO: dec_tlv_objects = tlv.decode(bytes(decrypted_data)) client_username = dec_tlv_objects[HAP_TLV_TAGS.USERNAME] material = (self.enc_context["client_public"] + client_username + self.enc_context["public_key"].serialize()) client_uuid = uuid.UUID(str(client_username, "utf-8")) perm_client_public = self.state.paired_clients.get(client_uuid) if perm_client_public is None: logger.error( "%s: Client %s attempted pair verify without being paired to %s first.", self.client_address, client_uuid, self.accessory_handler.accessory.display_name, ) self._send_authentication_error_tlv_response(HAP_TLV_STATES.M4) return verifying_key = ed25519.VerifyingKey(perm_client_public) try: verifying_key.verify(dec_tlv_objects[HAP_TLV_TAGS.PROOF], material) except ed25519.BadSignatureError: logger.error("%s: Bad signature, abort.", self.client_address) self._send_authentication_error_tlv_response(HAP_TLV_STATES.M4) return logger.debug( "%s: Pair verify with client completed. Switching to " "encrypted transport.", self.client_address, ) data = tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, HAP_TLV_STATES.M4) self._send_tlv_pairing_response(data) self.response.shared_key = self.enc_context["shared_key"] self.is_encrypted = True del self.enc_context
def _pairing_one(self): """Send the SRP salt and public key to the client. The SRP verifier is created at this step. """ logging.debug("Pairing [1/5]") self._accessory_driver.setup_srp_verifier() salt, B = self._accessory_driver.srp_verifier.get_challenge() return tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b'\x02', HAP_TLV_TAGS.SALT, salt, HAP_TLV_TAGS.PUBLIC_KEY, long_to_bytes(B))
def _pair_verify_one(self, tlv_objects): """Generate new session key pair and send a proof to the client. @param tlv_objects: The TLV data received from the client. @type tlv_object: dict """ logging.debug("Pair verify [1/2].") client_public = tlv_objects[HAP_TLV_TAGS.PUBLIC_KEY] private_key = curve25519.Private() public_key = private_key.get_public() shared_key = private_key.get_shared_key( curve25519.Public(client_public), # Key is hashed before being returned, we don't want it; This fixes that. lambda x: x) mac = self._state.mac.encode() material = public_key.serialize() + mac + client_public server_proof = self._state.private_key.sign(material) output_key = hap_hkdf(shared_key, PVERIFY_1_SALT, PVERIFY_1_INFO) enc_context = { "client_public": client_public, "private_key": private_key, "public_key": public_key, "shared_key": shared_key, "pre_session_key": output_key } message = tlv.encode(HAP_TLV_TAGS.USERNAME, mac, HAP_TLV_TAGS.PROOF, server_proof) cipher = CHACHA20_POLY1305(output_key, "python") aead_message = bytes( cipher.seal(PVERIFY_1_NONCE, bytearray(message), b"")) data = tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b'\x02', HAP_TLV_TAGS.ENCRYPTED_DATA, aead_message, HAP_TLV_TAGS.PUBLIC_KEY, public_key.serialize()) return data, enc_context
def _pair_verify_one(self, tlv_objects): """Generate new session key pair and send a proof to the client. @param tlv_objects: The TLV data received from the client. @type tlv_object: dict """ logger.debug("Pair verify [1/2].") client_public = tlv_objects[HAP_TLV_TAGS.PUBLIC_KEY] private_key = curve25519.Private() public_key = private_key.get_public() shared_key = private_key.get_shared_key( curve25519.Public(client_public), # Why not randomly hash the key before returning it # so that it is incompatible with another # implementation and force clients to use hacks? lambda x: x) mac = self.accessory.mac.encode() material = public_key.serialize() + mac + client_public server_proof = self.accessory.private_key.sign(material) output_key = hap_hkdf(shared_key, self.PVERIFY_1_SALT, self.PVERIFY_1_INFO) self._set_encryption_ctx(client_public, private_key, public_key, shared_key, output_key) message = tlv.encode(HAP_TLV_TAGS.USERNAME, mac, HAP_TLV_TAGS.PROOF, server_proof) cipher = CHACHA20_POLY1305(output_key, "python") aead_message = bytes( cipher.seal(self.PVERIFY_1_NONCE, bytearray(message), b"")) data = tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b'\x02', HAP_TLV_TAGS.ENCRYPTED_DATA, aead_message, HAP_TLV_TAGS.PUBLIC_KEY, public_key.serialize()) self.send_response(200) self.send_header("Content-Type", self.PAIRING_RESPONSE_TYPE) self.end_response(data)
def get_supported_rtp_config(support_srtp): """Return a tlv representation of the RTP configuration we support. SRTP support allows only the AES_CM_128_HMAC_SHA1_80 cipher for now. :param support_srtp: True if SRTP is supported, False otherwise. :type support_srtp: bool """ if support_srtp: crypto = SRTP_CRYPTO_SUITES['AES_CM_128_HMAC_SHA1_80'] else: crypto = SRTP_CRYPTO_SUITES['NONE'] return tlv.encode(RTP_CONFIG_TYPES['CRYPTO'], crypto, to_base64=True)
def _handle_add_pairing(self, tlv_objects): """Update client information.""" logging.debug('Adding client pairing.') client_username = tlv_objects[HAP_TLV_TAGS.USERNAME] client_public = tlv_objects[HAP_TLV_TAGS.PUBLIC_KEY] client_uuid = uuid.UUID(str(client_username, "utf-8")) should_confirm = self._accessory_driver.pair(client_uuid, client_public) if not should_confirm: self.send_response(500) return return tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b"\x02")
def _pairing_five(self, client_username, client_ltpk, encryption_key): """At that point we know the client has the accessory password and has a valid key pair. Add it as a pair and send a sever proof. Parameters are as for _pairing_four. """ logger.debug("Pairing [5/5]") session_key = self.accessory_handler.srp_verifier.get_session_key() output_key = hap_hkdf(long_to_bytes(session_key), self.PAIRING_5_SALT, self.PAIRING_5_INFO) server_public = self.state.public_key.to_bytes() mac = self.state.mac.encode() material = output_key + mac + server_public private_key = self.state.private_key server_proof = private_key.sign(material) message = tlv.encode(HAP_TLV_TAGS.USERNAME, mac, HAP_TLV_TAGS.PUBLIC_KEY, server_public, HAP_TLV_TAGS.PROOF, server_proof) cipher = ChaCha20Poly1305(encryption_key) aead_message = bytes( cipher.encrypt(self.PAIRING_5_NONCE, bytes(message), b"")) client_uuid = uuid.UUID(str(client_username, "utf-8")) should_confirm = self.accessory_handler.pair(client_uuid, client_ltpk) if not should_confirm: self.send_response(500) self.end_response(b'') return tlv_data = tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b'\x06', HAP_TLV_TAGS.ENCRYPTED_DATA, aead_message) self.send_response(200) self.send_header("Content-Type", self.PAIRING_RESPONSE_TYPE) self.end_response(tlv_data)
def _handle_add_pairing(self, tlv_objects): """Update client information.""" logger.debug("%s: Adding client pairing.", self.client_address) client_username = tlv_objects[HAP_TLV_TAGS.USERNAME] client_public = tlv_objects[HAP_TLV_TAGS.PUBLIC_KEY] client_uuid = uuid.UUID(str(client_username, "utf-8")) should_confirm = self.accessory_handler.pair(client_uuid, client_public) if not should_confirm: self._send_authentication_error_tlv_response(HAP_TLV_STATES.M2) return data = tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, HAP_TLV_STATES.M2) self._send_tlv_pairing_response(data)
def _pair_verify_two(self, tlv_objects, enc_context): """Verify the client proof and upgrade to encrypted transport. @param tlv_objects: The TLV data received from the client. @type tlv_object: dict """ logging.debug('Pair verify [2/2]') encrypted_data = tlv_objects[HAP_TLV_TAGS.ENCRYPTED_DATA] cipher = CHACHA20_POLY1305(enc_context['pre_session_key'], 'python') decrypted_data = cipher.open(PVERIFY_2_NONCE, bytearray(encrypted_data), b'') assert decrypted_data is not None # TODO: dec_tlv_objects = tlv.decode(bytes(decrypted_data)) client_username = dec_tlv_objects[HAP_TLV_TAGS.USERNAME] material = enc_context['client_public'] \ + client_username \ + enc_context['public_key'].serialize() client_uuid = uuid.UUID(str(client_username, 'ascii')) perm_client_public = self._state.paired_clients.get(client_uuid) if perm_client_public is None: logging.debug( 'Client %s attempted pair verify without being paired first.', client_uuid) return tlv.encode(HAP_TLV_TAGS.ERROR_CODE, HAP_OPERATION_CODE.INVALID_REQUEST) verifying_key = ed25519.VerifyingKey(perm_client_public) try: verifying_key.verify(dec_tlv_objects[HAP_TLV_TAGS.PROOF], material) except ed25519.BadSignatureError: logging.error('Bad signature, abort.') return tlv.encode(HAP_TLV_TAGS.ERROR_CODE, HAP_OPERATION_CODE.INVALID_REQUEST) return tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b'\x04'), None
def _handle_remove_pairing(self, tlv_objects): """Remove pairing with the client.""" logger.debug("Removing client pairing.") client_username = tlv_objects[HAP_TLV_TAGS.USERNAME] client_uuid = uuid.UUID(str(client_username, "utf-8")) self.accessory_handler.unpair(client_uuid) data = tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b"\x02") self.send_response(200) self.send_header("Content-Type", self.PAIRING_RESPONSE_TYPE) self.end_response(data) # Avoid updating the announcement until # after the response is sent. self.accessory_handler.finish_pair()
def _handle_add_pairing(self, tlv_objects): """Update client information.""" logger.debug("Renewing client information") client_username = tlv_objects[HAP_TLV_TAGS.USERNAME] client_public = tlv_objects[HAP_TLV_TAGS.PUBLIC_KEY] client_uuid = uuid.UUID(str(client_username, "utf-8")) should_confirm = self.accessory_handler.pair( client_uuid, client_public) if not should_confirm: self.send_response(500) return data = tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b"\x02") self.send_response(200) self.send_header("Content-Type", self.PAIRING_RESPONSE_TYPE) self.end_response(data)
def _pairing_one(self): """Send the SRP salt and public key to the client. The SRP verifier is created at this step. """ logger.debug("Pairing [1/5]") self.accessory_handler.setup_srp_verifier() salt, B = self.accessory_handler.srp_verifier.get_challenge() data = tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b'\x02', HAP_TLV_TAGS.SALT, salt, HAP_TLV_TAGS.PUBLIC_KEY, long_to_bytes(B)) self.send_response(200) self.send_header("Content-Type", self.PAIRING_RESPONSE_TYPE) self.end_response(data, False)