Пример #1
0
    async def finish_pairing(self, username: str, pin_code: int) -> bool:
        """Finish authentication process.

        A username (generated by new_credentials) and the PIN code shown on
        screen must be provided.
        """
        # Step 1
        self.srp.step1(pin_code)

        pub_key, proof = self.srp.step2(self._atv_pub_key, self._atv_salt)
        data = {
            hap_tlv8.TlvValue.SeqNo: b"\x03",
            hap_tlv8.TlvValue.PublicKey: pub_key,
            hap_tlv8.TlvValue.Proof: proof,
        }
        await self.http.post("/pair-setup",
                             body=hap_tlv8.write_tlv(data),
                             headers=_AIRPLAY_HEADERS)

        data = {
            hap_tlv8.TlvValue.SeqNo: b"\x05",
            hap_tlv8.TlvValue.EncryptedData: self.srp.step3(),
        }
        resp = await self.http.post("/pair-setup",
                                    body=hap_tlv8.write_tlv(data),
                                    headers=_AIRPLAY_HEADERS)
        pairing_data = hap_tlv8.read_tlv(resp.body)

        encrypted_data = pairing_data[hap_tlv8.TlvValue.EncryptedData]
        return self.srp.step4(encrypted_data)
Пример #2
0
    async def verify_credentials(self) -> bool:
        """Verify credentials with device."""
        _, public_key = self.srp.initialize()

        resp = await self.protocol.exchange_auth(
            FrameType.PV_Start,
            {
                PAIRING_DATA_KEY: write_tlv(
                    {TlvValue.SeqNo: b"\x01", TlvValue.PublicKey: public_key}
                ),
                "_auTy": 4,
            },
        )

        pairing_data = _get_pairing_data(resp)
        server_pub_key = pairing_data[TlvValue.PublicKey]
        encrypted = pairing_data[TlvValue.EncryptedData]
        log_binary(_LOGGER, "Device", Public=self.credentials.ltpk, Encrypted=encrypted)

        encrypted_data = self.srp.verify1(self.credentials, server_pub_key, encrypted)

        await self.protocol.exchange_auth(
            FrameType.PV_Next,
            {
                PAIRING_DATA_KEY: write_tlv(
                    {TlvValue.SeqNo: b"\x03", TlvValue.EncryptedData: encrypted_data}
                ),
            },
        )

        # TODO: check status code

        return True
Пример #3
0
    async def verify_credentials(self) -> bool:
        """Verify if device is allowed to use AirPlau."""
        self.srp.initialize()

        await self.http.post("/pair-pin-start", headers=_AIRPLAY_HEADERS)

        data = {
            hap_tlv8.TlvValue.Method: b"\x00",
            hap_tlv8.TlvValue.SeqNo: b"\x01"
        }
        resp = await self.http.post("/pair-setup",
                                    body=hap_tlv8.write_tlv(data),
                                    headers=_AIRPLAY_HEADERS)
        pairing_data = hap_tlv8.read_tlv(resp.body)

        atv_salt = pairing_data[hap_tlv8.TlvValue.Salt]
        atv_pub_key = pairing_data[hap_tlv8.TlvValue.PublicKey]

        self.srp.step1(_TRANSIENT_PIN)

        pub_key, proof = self.srp.step2(atv_pub_key, atv_salt)
        data = {
            hap_tlv8.TlvValue.SeqNo: b"\x03",
            hap_tlv8.TlvValue.PublicKey: pub_key,
            hap_tlv8.TlvValue.Proof: proof,
        }
        await self.http.post("/pair-setup",
                             body=hap_tlv8.write_tlv(data),
                             headers=_AIRPLAY_HEADERS)

        return True
Пример #4
0
    async def finish_pairing(self, username: str, pin_code: int) -> HapCredentials:
        """Finish pairing process."""
        self.srp.step1(pin_code)

        pub_key, proof = self.srp.step2(self._atv_pub_key, self._atv_salt)

        resp = await self.protocol.exchange_auth(
            FrameType.PS_Next,
            {
                PAIRING_DATA_KEY: write_tlv(
                    {
                        TlvValue.SeqNo: b"\x03",
                        TlvValue.PublicKey: pub_key,
                        TlvValue.Proof: proof,
                    }
                ),
                "_pwTy": 1,
            },
        )

        pairing_data = _get_pairing_data(resp)
        atv_proof = pairing_data[TlvValue.Proof]
        log_binary(_LOGGER, "Device", Proof=atv_proof)

        # TODO: Dummy data: what to set? needed at all?
        additional_data = {
            "altIRK": b"-\x54\xe0\x7a\x88*en\x11\xab\x82v-'%\xc5",
            "accountID": "DC6A7CB6-CA1A-4BF4-880D-A61B717814DB",
            "model": "AppleTV6,2",
            "wifiMAC": b"@\xff\xa1\x8f\xa1\xb9",
            "name": "Living Room",
            "mac": b"@\xc4\xff\x8f\xb1\x99",
        }

        encrypted_data = self.srp.step3(
            additional_data={17: opack.pack(additional_data)}
        )

        resp = await self.protocol.exchange_auth(
            FrameType.PS_Next,
            {
                PAIRING_DATA_KEY: write_tlv(
                    {
                        TlvValue.SeqNo: b"\x05",
                        TlvValue.EncryptedData: encrypted_data,
                    }
                ),
                "_pwTy": 1,
            },
        )

        pairing_data = _get_pairing_data(resp)
        encrypted_data = pairing_data[TlvValue.EncryptedData]

        return self.srp.step4(encrypted_data)
Пример #5
0
    def _m5_setup(self, pairing_data):
        session_key = hkdf_expand(
            "Pair-Setup-Encrypt-Salt",
            "Pair-Setup-Encrypt-Info",
            binascii.unhexlify(self.session.key),
        )

        acc_device_x = hkdf_expand(
            "Pair-Setup-Accessory-Sign-Salt",
            "Pair-Setup-Accessory-Sign-Info",
            binascii.unhexlify(self.session.key),
        )

        chacha = chacha20.Chacha20Cipher(session_key, session_key)
        decrypted_tlv_bytes = chacha.decrypt(
            pairing_data[TlvValue.EncryptedData], nounce="PS-Msg05".encode())

        _LOGGER.debug("MSG5 EncryptedData=%s", read_tlv(decrypted_tlv_bytes))

        other = {
            "altIRK": b"-\x54\xe0\x7a\x88*en\x11\xab\x82v-'%\xc5",
            "accountID": "DC6A7CB6-CA1A-4BF4-880D-A61B717814DB",
            "model": "AppleTV6,2",
            "wifiMAC": b"@\xff\xa1\x8f\xa1\xb9",
            "name": "Living Room",
            "mac": b"@\xc4\xff\x8f\xb1\x99",
        }

        device_info = acc_device_x + self.unique_id + self.keys.auth_pub
        signature = self.keys.sign.sign(device_info)

        tlv = {
            TlvValue.Identifier: self.unique_id,
            TlvValue.PublicKey: self.keys.auth_pub,
            TlvValue.Signature: signature,
            17: opack.pack(other),
        }

        tlv = write_tlv(tlv)

        chacha = chacha20.Chacha20Cipher(session_key, session_key)
        encrypted = chacha.encrypt(tlv, nounce="PS-Msg06".encode())

        tlv = write_tlv({
            TlvValue.SeqNo: b"\x06",
            TlvValue.EncryptedData: encrypted
        })

        self.send_to_client(FrameType.PS_Next, {"_pd": tlv})
        self.has_paired()
Пример #6
0
    def _m3_setup(self, pairing_data):
        pubkey = binascii.hexlify(pairing_data[TlvValue.PublicKey])
        self.session.process(pubkey, self.salt)

        if self.session.verify_proof(binascii.hexlify(pairing_data[TlvValue.Proof])):
            proof = binascii.unhexlify(self.session.key_proof_hash)
            tlv = {TlvValue.SeqNo: b"\x04", TlvValue.Proof: proof}
        else:
            tlv = {
                TlvValue.SeqNo: b"\x04",
                TlvValue.Error: bytes([ErrorCode.Authentication]),
            }

        write_shared_key = hkdf_expand(
            "Control-Salt",
            "Control-Write-Encryption-Key",
            binascii.unhexlify(self.session.key),
        )

        read_shared_key = hkdf_expand(
            "Control-Salt",
            "Control-Read-Encryption-Key",
            binascii.unhexlify(self.session.key),
        )

        self.enable_encryption(write_shared_key, read_shared_key)
        self.has_paired()
        return write_tlv(tlv)
Пример #7
0
    def _m5_setup(self, _):
        session_key = hkdf_expand(
            "Pair-Setup-Encrypt-Salt",
            "Pair-Setup-Encrypt-Info",
            binascii.unhexlify(self.session.key),
        )

        acc_device_x = hkdf_expand(
            "Pair-Setup-Accessory-Sign-Salt",
            "Pair-Setup-Accessory-Sign-Info",
            binascii.unhexlify(self.session.key),
        )

        device_info = acc_device_x + self.unique_id + self.keys.auth_pub
        signature = self.keys.sign.sign(device_info)

        tlv = write_tlv({
            TlvValue.Identifier: self.unique_id,
            TlvValue.PublicKey: self.keys.auth_pub,
            TlvValue.Signature: signature,
        })

        chacha = chacha20.Chacha20Cipher(session_key, session_key)
        encrypted = chacha.encrypt(tlv, nounce="PS-Msg06".encode())

        msg = messages.crypto_pairing({
            TlvValue.SeqNo: b"\x06",
            TlvValue.EncryptedData: encrypted
        })
        self.has_paired = True

        self.send_to_client(msg)
Пример #8
0
    def step3(self, additional_data=None):
        """Third pairing step."""
        ios_device_x = hkdf_expand(
            "Pair-Setup-Controller-Sign-Salt",
            "Pair-Setup-Controller-Sign-Info",
            binascii.unhexlify(self._session.key),
        )

        self._session_key = hkdf_expand(
            "Pair-Setup-Encrypt-Salt",
            "Pair-Setup-Encrypt-Info",
            binascii.unhexlify(self._session.key),
        )

        device_info = ios_device_x + self.pairing_id + self._auth_public
        device_signature = self._signing_key.sign(device_info)

        tlv = {
            TlvValue.Identifier: self.pairing_id,
            TlvValue.PublicKey: self._auth_public,
            TlvValue.Signature: device_signature,
        }

        if additional_data:
            tlv.update(additional_data)

        chacha = chacha20.Chacha20Cipher(self._session_key, self._session_key)
        encrypted_data = chacha.encrypt(write_tlv(tlv),
                                        nounce="PS-Msg05".encode())
        log_binary(_LOGGER, "Data", Encrypted=encrypted_data)
        return encrypted_data
Пример #9
0
 def _m1_setup(self, pairing_data):
     return write_tlv(
         {
             TlvValue.SeqNo: b"\x02",
             TlvValue.Salt: binascii.unhexlify(self.salt),
             TlvValue.PublicKey: binascii.unhexlify(self.session.public),
         }
     )
Пример #10
0
def crypto_pairing(pairing_data, is_pairing=False):
    """Create a new CRYPTO_PAIRING_MESSAGE."""
    message = create(protobuf.CRYPTO_PAIRING_MESSAGE)
    crypto = message.inner()
    crypto.status = 0
    crypto.pairingData = hap_tlv8.write_tlv(pairing_data)

    # Hardcoded values for now, might have to be changed
    crypto.isRetrying = False
    crypto.isUsingSystemPairing = False
    crypto.state = 2 if is_pairing else 0
    return message
Пример #11
0
 def _m1_setup(self, pairing_data):
     tlv = write_tlv({
         TlvValue.SeqNo:
         b"\x02",
         TlvValue.Salt:
         binascii.unhexlify(self.salt),
         TlvValue.PublicKey:
         binascii.unhexlify(self.session.public),
         27:
         b"\x01",
     })
     self.send_to_client(FrameType.PS_Next, {"_pd": tlv, "_pwTy": 1})
Пример #12
0
    def _m1_verify(self, pairing_data):
        server_pub_key = self.keys.verify_pub.public_bytes(
            encoding=serialization.Encoding.Raw,
            format=serialization.PublicFormat.Raw)
        client_pub_key = pairing_data[TlvValue.PublicKey]

        shared_key = self.keys.verify.exchange(
            X25519PublicKey.from_public_bytes(client_pub_key))

        session_key = hkdf_expand("Pair-Verify-Encrypt-Salt",
                                  "Pair-Verify-Encrypt-Info", shared_key)

        info = server_pub_key + self.unique_id + client_pub_key
        signature = self.keys.sign.sign(info)

        tlv = write_tlv({
            TlvValue.Identifier: self.unique_id,
            TlvValue.Signature: signature
        })

        chacha = chacha20.Chacha20Cipher(session_key, session_key)
        encrypted = chacha.encrypt(tlv, nounce="PV-Msg02".encode())

        tlv = write_tlv({
            TlvValue.SeqNo: b"\x02",
            TlvValue.PublicKey: server_pub_key,
            TlvValue.EncryptedData: encrypted,
        })

        self.output_key = hkdf_expand("", "ServerEncrypt-main", shared_key)
        self.input_key = hkdf_expand("", "ClientEncrypt-main", shared_key)

        log_binary(_LOGGER,
                   "Keys",
                   Output=self.output_key,
                   Input=self.input_key)

        self.send_to_client(FrameType.PV_Next, {"_pd": tlv})
Пример #13
0
    def _m3_setup(self, pairing_data):
        pubkey = binascii.hexlify(pairing_data[TlvValue.PublicKey]).decode()
        self.session.process(pubkey, self.salt)

        if self.session.verify_proof(
                binascii.hexlify(pairing_data[TlvValue.Proof])):
            proof = binascii.unhexlify(self.session.key_proof_hash)
            tlv = {TlvValue.Proof: proof, TlvValue.SeqNo: b"\x04"}
        else:
            tlv = {
                TlvValue.Error: bytes([ErrorCode.Authentication]),
                TlvValue.SeqNo: b"\x04",
            }

        self.send_to_client(FrameType.PS_Next, {"_pd": write_tlv(tlv)})
Пример #14
0
    async def start_pairing(self) -> None:
        """Start the authentication process.

        This method will show the expected PIN on screen.
        """
        self.srp.initialize()

        await self.http.post("/pair-pin-start", headers=_AIRPLAY_HEADERS)

        data = {
            hap_tlv8.TlvValue.Method: b"\x00",
            hap_tlv8.TlvValue.SeqNo: b"\x01"
        }
        resp = await self.http.post("/pair-setup",
                                    body=hap_tlv8.write_tlv(data),
                                    headers=_AIRPLAY_HEADERS)
        pairing_data = hap_tlv8.read_tlv(resp.body)

        self._atv_salt = pairing_data[hap_tlv8.TlvValue.Salt]
        self._atv_pub_key = pairing_data[hap_tlv8.TlvValue.PublicKey]
Пример #15
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 = 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 = write_tlv({
            TlvValue.Identifier: credentials.client_id,
            TlvValue.Signature: device_signature,
        })

        return chacha.encrypt(tlv, nounce="PV-Msg03".encode())
Пример #16
0
    async def start_pairing(self) -> None:
        """Start pairing procedure."""
        self.srp.initialize()
        await self.protocol.start()

        resp = await self.protocol.exchange_auth(
            FrameType.PS_Start,
            {
                PAIRING_DATA_KEY: write_tlv(
                    {TlvValue.Method: b"\x00", TlvValue.SeqNo: b"\x01"}
                ),
                "_pwTy": 1,
            },
        )

        pairing_data = _get_pairing_data(resp)
        self._atv_salt = pairing_data[TlvValue.Salt]
        self._atv_pub_key = pairing_data[TlvValue.PublicKey]
        log_binary(
            _LOGGER,
            "Got pub key and salt",
            Salt=self._atv_salt,
            PubKey=self._atv_pub_key,
        )
Пример #17
0
 async def _send(self, data: Dict[Any, Any]) -> HttpResponse:
     headers = copy(_AIRPLAY_HEADERS)
     headers["Content-Type"] = "application/octet-stream"
     return await self.http.post("/pair-verify",
                                 body=hap_tlv8.write_tlv(data),
                                 headers=headers)
Пример #18
0
 def _m3_verify(self, pairing_data):
     self.send_to_client(FrameType.PV_Next,
                         {"_pd": write_tlv({TlvValue.SeqNo: b"\x04"})})
     self.enable_encryption(self.output_key, self.input_key)
Пример #19
0
def test_write_single_key():
    assert write_tlv(SINGLE_KEY_IN) == SINGLE_KEY_OUT
Пример #20
0
def test_write_two_keys():
    assert write_tlv(DOUBLE_KEY_IN) == DOUBLE_KEY_OUT
Пример #21
0
def test_write_key_larger_than_255_bytes():
    # This will actually result in two serialized TLVs, one being 255 bytes
    # and the next one will contain the remaining one byte
    assert write_tlv(LARGE_KEY_IN) == LARGE_KEY_OUT