Beispiel #1
0
    def step4(self, encrypted_data):
        """Last pairing step."""
        chacha = chacha20.Chacha20Cipher(self._session_key, self._session_key)
        decrypted_tlv_bytes = chacha.decrypt(encrypted_data, nounce="PS-Msg06".encode())

        if not decrypted_tlv_bytes:
            raise exceptions.AuthenticationError("data decrypt failed")

        decrypted_tlv = hap_tlv8.read_tlv(decrypted_tlv_bytes)
        _LOGGER.debug("PS-Msg06: %s", decrypted_tlv)

        atv_identifier = decrypted_tlv[TlvValue.Identifier]
        atv_signature = decrypted_tlv[TlvValue.Signature]
        atv_pub_key = decrypted_tlv[TlvValue.PublicKey]
        log_binary(
            _LOGGER,
            "Device",
            Identifier=atv_identifier,
            Signature=atv_signature,
            Public=atv_pub_key,
        )

        # TODO: verify signature here

        return HapCredentials(
            atv_pub_key, self._auth_private, atv_identifier, self.pairing_id
        )
Beispiel #2
0
def _get_pairing_data(message: Dict[str, object]):
    pairing_data = message.get(PAIRING_DATA_KEY)
    if not pairing_data:
        raise exceptions.AuthenticationError("no pairing data in message")

    if not isinstance(pairing_data, bytes):
        raise exceptions.ProtocolError(
            f"Pairing data has unexpected type: {type(pairing_data)}")

    tlv = read_tlv(pairing_data)
    if TlvValue.Error in tlv:
        raise exceptions.AuthenticationError(stringify(tlv))

    return tlv
Beispiel #3
0
    def handle_crypto_pairing(self, message, inner):
        """Handle incoming crypto pairing message."""
        _LOGGER.debug("Received crypto pairing message")
        pairing_data = read_tlv(inner.pairingData)
        seqno = pairing_data[TlvValue.SeqNo][0]

        # Work-around for now to support "tries" to auth before pairing
        if seqno == 1:
            if TlvValue.PublicKey in pairing_data:
                self.has_paired = True
            elif TlvValue.Method in pairing_data:
                self.has_paired = False

        suffix = "paired" if self.has_paired else "pairing"
        method = "_seqno{0}_{1}".format(seqno, suffix)
        getattr(self, method)(pairing_data)
Beispiel #4
0
    def verify1(self, credentials, session_pub_key, encrypted):
        """First verification step."""
        self._shared = self._verify_private.exchange(
            X25519PublicKey.from_public_bytes(session_pub_key)
        )

        session_key = hkdf_expand(
            "Pair-Verify-Encrypt-Salt", "Pair-Verify-Encrypt-Info", self._shared
        )

        chacha = chacha20.Chacha20Cipher(session_key, session_key)
        decrypted_tlv = hap_tlv8.read_tlv(
            chacha.decrypt(encrypted, nounce="PV-Msg02".encode())
        )

        identifier = decrypted_tlv[TlvValue.Identifier]
        signature = decrypted_tlv[TlvValue.Signature]

        if identifier != credentials.atv_id:
            raise exceptions.AuthenticationError("incorrect device response")

        info = session_pub_key + bytes(identifier) + self._public_bytes
        ltpk = Ed25519PublicKey.from_public_bytes(bytes(credentials.ltpk))

        try:
            ltpk.verify(bytes(signature), bytes(info))
        except InvalidSignature as ex:
            raise exceptions.AuthenticationError("signature error") from ex

        device_info = self._public_bytes + credentials.client_id + session_pub_key

        device_signature = Ed25519PrivateKey.from_private_bytes(credentials.ltsk).sign(
            device_info
        )

        tlv = hap_tlv8.write_tlv(
            {
                TlvValue.Identifier: credentials.client_id,
                TlvValue.Signature: device_signature,
            }
        )

        return chacha.encrypt(tlv, nounce="PV-Msg03".encode())
Beispiel #5
0
def test_read_key_larger_than_255_bytes():
    assert read_tlv(LARGE_KEY_OUT) == LARGE_KEY_IN
Beispiel #6
0
def test_read_two_keys():
    assert read_tlv(DOUBLE_KEY_OUT) == DOUBLE_KEY_IN
Beispiel #7
0
def test_read_single_key():
    assert read_tlv(SINGLE_KEY_OUT) == SINGLE_KEY_IN
Beispiel #8
0
def _get_pairing_data(resp):
    tlv = read_tlv(resp.inner().pairingData)
    if TlvValue.Error in tlv:
        raise exceptions.AuthenticationError(stringify(tlv))
    return tlv