def __init__(self, file_id: bytes, preloaded_audio_key: bool,
                     audio_key_time: int):
            self.file_id = None if file_id is None else Utils.bytes_to_hex(
                file_id)
            self.preloaded_audio_key = preloaded_audio_key
            self.audio_key_time = audio_key_time

            if preloaded_audio_key and audio_key_time != -1:
                raise RuntimeError()
Example #2
0
 def __init__(self,
              device_type: Connect.DeviceType,
              device_name: str,
              preferred_locale: str,
              conf: Session.Configuration,
              device_id: str = None):
     self.preferred_locale = preferred_locale
     self.conf = conf
     self.device_type = device_type
     self.device_name = device_name
     self.device_id = device_id if device_id is not None else Utils.random_hex_string(
         40)
    def resolve_storage_interactive(
            self, file_id: bytes,
            preload: bool) -> StorageResolve.StorageResolveResponse:
        resp = self.session.api().send(
            "GET", (self.STORAGE_RESOLVE_INTERACTIVE_PREFETCH
                    if preload else self.STORAGE_RESOLVE_INTERACTIVE).format(
                        Utils.bytes_to_hex(file_id)), None, None)
        if resp.status_code != 200:
            raise RuntimeError(resp.status_code)

        body = resp.content
        if body is None:
            RuntimeError("Response body is empty!")

        storage_resolve_response = StorageResolve.StorageResolveResponse()
        storage_resolve_response.ParseFromString(body)
        return storage_resolve_response
 def public_key_array(self):
     return Utils.to_byte_array(self.public_key)
 def get_episode_gid(self):
     if self.episode_gid is None:
         raise RuntimeError("Not an episode!")
     return Utils.bytes_to_hex(self.episode_gid)
 def get_file_id(self):
     if self.file_id is None:
         raise RuntimeError("Not a file!")
     return Utils.bytes_to_hex(self.file_id)
Example #7
0
        def run(self) -> None:
            self.session._LOGGER.debug("Session.Receiver started")

            while self.running:
                packet: Packet
                cmd: bytes
                try:
                    # noinspection PyProtectedMember
                    packet = self.session._cipherPair.receive_encoded(
                        self.session._conn)
                    cmd = Packet.Type.parse(packet.cmd)
                    if cmd is None:
                        self.session._LOGGER.info(
                            "Skipping unknown command cmd: 0x{}, payload: {}".
                            format(Utils.bytes_to_hex(packet.cmd),
                                   packet.payload))
                        continue
                except RuntimeError as ex:
                    if self.running:
                        self.session._LOGGER.fatal(
                            "Failed reading packet! {}".format(ex))
                        # noinspection PyProtectedMember
                        self.session._reconnect()
                    break

                if not self.running:
                    break
                if cmd == Packet.Type.ping:
                    # noinspection PyProtectedMember
                    if self.session._scheduledReconnect is not None:
                        # noinspection PyProtectedMember
                        self.session._scheduler.cancel(
                            self.session._scheduledReconnect)

                    def anonymous():
                        self.session._LOGGER.warning(
                            "Socket timed out. Reconnecting...")
                        self.session._reconnect()

                    # noinspection PyProtectedMember
                    self.session.scheduled_reconnect = self.session._scheduler.enter(
                        2 * 60 + 5, 1, anonymous)
                    self.session.send(Packet.Type.pong, packet.payload)
                    continue
                if cmd == Packet.Type.pong_ack:
                    continue
                if cmd == Packet.Type.country_code:
                    self.session.country_code = packet.payload.decode()
                    self.session._LOGGER.info(
                        "Received country_code: {}".format(
                            self.session.country_code))
                    continue
                if cmd == Packet.Type.license_version:
                    license_version = BytesInputStream(packet.payload)
                    license_id = license_version.read_short()
                    if license_id != 0:
                        buffer = license_version.read()
                        self.session._LOGGER.info(
                            "Received license_version: {}, {}".format(
                                license_id, buffer.decode()))
                    else:
                        self.session._LOGGER.info(
                            "Received license_version: {}".format(license_id))
                    continue
                if cmd == Packet.Type.unknown_0x10:
                    self.session._LOGGER.debug("Received 0x10: {}".format(
                        Utils.bytes_to_hex(packet.payload)))
                    continue
                if cmd == Packet.Type.mercury_sub or \
                                        cmd == Packet.Type.mercury_unsub or \
                                        cmd == Packet.Type.mercury_event or \
                                        cmd == Packet.Type.mercury_req:
                    self.session.mercury().dispatch(packet)
                    continue
                if cmd == Packet.Type.aes_key or \
                                        cmd == Packet.Type.aes_key_error:
                    self.session.audio_key().dispatch(packet)
                    continue
                if cmd == Packet.Type.channel_error or \
                                        cmd == Packet.Type.stream_chunk_res:
                    self.session.channel().dispatch(packet)
                    continue
                if cmd == Packet.Type.product_info:
                    # noinspection PyProtectedMember
                    self.session._parse_product_info(packet.payload)
                    continue
                self.session._LOGGER.info("Skipping {}".format(
                    Utils.bytes_to_hex(cmd)))

            self.session._LOGGER.debug("Session.Receiver stopped")
Example #8
0
    def _connect(self) -> None:
        acc = Session.Accumulator()

        # Send ClientHello

        nonce = os.urandom(0x10)

        client_hello = Keyexchange.ClientHello(
            build_info=Version.standard_build_info(),
            cryptosuites_supported=[
                Keyexchange.Cryptosuite.CRYPTO_SUITE_SHANNON
            ],
            login_crypto_hello=Keyexchange.LoginCryptoHelloUnion(
                diffie_hellman=Keyexchange.LoginCryptoDiffieHellmanHello(
                    gc=self._keys.public_key_array(), server_keys_known=1), ),
            client_nonce=nonce,
            padding=bytes([0x1e]))

        client_hello_bytes = client_hello.SerializeToString()
        length = 2 + 4 + len(client_hello_bytes)
        self._conn.write_byte(0)
        self._conn.write_byte(4)
        self._conn.write_int(length)
        self._conn.write(client_hello_bytes)
        self._conn.flush()

        acc.write_byte(0)
        acc.write_byte(4)
        acc.write_int(length)
        acc.write(client_hello_bytes)

        # Read APResponseMessage

        length = self._conn.read_int()
        acc.write_int(length)
        buffer = self._conn.read(length - 4)
        acc.write(buffer)

        ap_response_message = Keyexchange.APResponseMessage()
        ap_response_message.ParseFromString(buffer)
        shared_key = Utils.to_byte_array(
            self._keys.compute_shared_key(
                ap_response_message.challenge.login_crypto_challenge.
                diffie_hellman.gs))

        # Check gs_signature

        rsa = RSA.construct((int.from_bytes(self._serverKey, "big"), 65537))
        pkcs1_v1_5 = PKCS1_v1_5.new(rsa)
        sha1 = SHA1.new()
        sha1.update(ap_response_message.challenge.login_crypto_challenge.
                    diffie_hellman.gs)
        # noinspection PyTypeChecker
        if not pkcs1_v1_5.verify(
                sha1, ap_response_message.challenge.login_crypto_challenge.
                diffie_hellman.gs_signature):
            raise RuntimeError("Failed signature check!")

        # Solve challenge

        data = b""

        for i in range(1, 6):
            # noinspection PyTypeChecker
            mac = HMAC.new(shared_key, digestmod=SHA1)
            mac.update(acc.array())
            mac.update(bytes([i]))
            data += mac.digest()

        # noinspection PyTypeChecker
        mac = HMAC.new(data[:20], digestmod=SHA1)
        mac.update(acc.array())

        challenge = mac.digest()
        client_response_plaintext = Keyexchange.ClientResponsePlaintext(
            login_crypto_response=Keyexchange.LoginCryptoResponseUnion(
                diffie_hellman=Keyexchange.LoginCryptoDiffieHellmanResponse(
                    hmac=challenge)),
            pow_response=Keyexchange.PoWResponseUnion(),
            crypto_response=Keyexchange.CryptoResponseUnion())

        client_response_plaintext_bytes = client_response_plaintext.SerializeToString(
        )
        length = 4 + len(client_response_plaintext_bytes)
        self._conn.write_int(length)
        self._conn.write(client_response_plaintext_bytes)
        self._conn.flush()

        try:
            self._conn.set_timeout(1)
            scrap = self._conn.read(4)
            if 4 == len(scrap):
                length = (scrap[0] << 24) | (scrap[1] << 16) | (
                    scrap[2] << 8) | (scrap[3] & 0xff)
                payload = self._conn.read(length - 4)
                failed = Keyexchange.APResponseMessage()
                failed.ParseFromString(payload)
                raise RuntimeError(failed)
        except socket.timeout:
            pass
        finally:
            self._conn.set_timeout(0)

        with self._authLock:
            self._cipherPair = CipherPair(data[20:52], data[52:84])

            self._authLockBool = True

        self._LOGGER.info("Connection successfully!")