예제 #1
0
    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)
예제 #2
0
    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)
예제 #3
0
    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
예제 #4
0
    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)
예제 #5
0
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,
    }
예제 #6
0
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,
    }
예제 #7
0
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,
    }
예제 #8
0
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)
예제 #9
0
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
예제 #10
0
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,
    }
예제 #11
0
    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)
예제 #12
0
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,
    }
예제 #13
0
    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)
예제 #14
0
    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")
예제 #15
0
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,
    }
예제 #16
0
 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,
         ))
예제 #17
0
    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)
예제 #18
0
   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)
예제 #19
0
    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
예제 #20
0
    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))
예제 #21
0
    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
예제 #22
0
   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)
예제 #23
0
    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)
예제 #24
0
    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")
예제 #25
0
    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)
예제 #26
0
    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)
예제 #27
0
    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
예제 #28
0
    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()
예제 #29
0
   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)
예제 #30
0
    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)