def _fragment_connect_request(crypto_instance: crypto.Crypto, client_uuid: uuid.UUID, pubkey_type: PublicKeyType, pubkey: bytes, userhash: str, auth_token: str, request_num: int = 0) -> List: """ Internal method to fragment ConnectRequest. Args: crypto_instance: Instance of :class:`Crypto` client_uuid: Client UUID pubkey_type Public Key Type pubkey: Public Key userhash: Xbox Live Account userhash auth_token: Xbox Live Account authentication token (XSTS) request_num: Request Number Returns: list: List of ConnectRequest fragments """ messages = [] # Calculate packet length (without authentication data) dummy_msg = factory.connect(client_uuid, pubkey_type, pubkey, b'\x00' * 16, u'', u'', 0, 0, 0) dummy_payload_len = packer.payload_length(dummy_msg) # Do fragmenting total_auth_len = len(userhash + auth_token) max_size = 1024 - dummy_payload_len fragments = total_auth_len // max_size overlap = total_auth_len % max_size if overlap > 0: fragments += 1 group_start = request_num group_end = group_start + fragments if fragments <= 1: raise FragmentError('Authentication data too small to fragment') auth_position = 0 for fragment_num in range(fragments): available = max_size current_hash = u'' if fragment_num == 0: current_hash = userhash available -= len(current_hash) current_auth = auth_token[auth_position:auth_position + available] auth_position += len(current_auth) iv = crypto_instance.generate_iv() messages.append( factory.connect(client_uuid, pubkey_type, pubkey, iv, current_hash, current_auth, request_num + fragment_num, group_start, group_end)) return messages
async def connect(self, userhash: str, xsts_token: str, client_uuid: uuid.UUID = uuid.uuid4(), request_num: int = 0, retries: int = 3) -> PairedIdentityState: """ Connect to console Args: userhash: Userhash from Xbox Live Authentication xsts_token: XSTS Token from Xbox Live Authentication client_uuid: Client UUID (default: Generate random uuid) request_num: Request number retries: Max. connect attempts Returns: Pairing State Raises: ProtocolError: If connection fails """ if not self.crypto: raise ProtocolError("No crypto") if isinstance(userhash, type(None)): userhash = '' if isinstance(xsts_token, type(None)): xsts_token = '' iv = self.crypto.generate_iv() pubkey_type = self.crypto.pubkey_type pubkey = self.crypto.pubkey_bytes msg = factory.connect(client_uuid, pubkey_type, pubkey, iv, userhash, xsts_token, request_num, request_num, request_num + 1) payload_len = packer.payload_length(msg) if payload_len < 1024: messages = [msg] else: messages = _fragment_connect_request(self.crypto, client_uuid, pubkey_type, pubkey, userhash, xsts_token, request_num) tries = 0 result = None while tries < retries and not result: for m in messages: await self.send_message(m) result = await self._await_ack('connect') if not result: raise ProtocolError("Exceeded connect retries") connect_result = result.protected_payload.connect_result if connect_result != ConnectionResult.Success: raise ProtocolError("Connecting failed! Result: %s" % connect_result) self.target_participant_id = 0 self.source_participant_id = result.protected_payload.participant_id await self.local_join() for channel, target_uuid in CHANNEL_MAP.items(): await self.start_channel(channel, target_uuid) asyncio.create_task(self._heartbeat_task()) return result.protected_payload.pairing_state
def connect(self, userhash, xsts_token, client_uuid=uuid.uuid4(), request_num=0, retries=3): """ Connect to console Args: userhash (str): Userhash from Xbox Live Authentication xsts_token (str): XSTS Token from Xbox Live Authentication client_uuid (UUID): Client UUID (default: Generate random uuid) request_num (int): Request number retries (int): Max. connect attempts Returns: int: Pairing State Raises: ProtocolError: If connection fails """ if not self.crypto: raise ProtocolError("No crypto") iv = self.crypto.generate_iv() pubkey_type = self.crypto.pubkey_type pubkey = self.crypto.pubkey_bytes msg = factory.connect(client_uuid, pubkey_type, pubkey, iv, userhash, xsts_token, request_num, request_num, request_num + 1) payload_len = packer.payload_length(msg) if payload_len < 1024: messages = [msg] else: messages = _fragment_connect_request(self.crypto, client_uuid, pubkey_type, pubkey, userhash, xsts_token, request_num) tries = 0 result = None while tries < retries and not result: for m in messages: self.send_message(m) result = self._await_ack('connect') if not result: raise ProtocolError("Exceeded connect retries") connect_result = result.protected_payload.connect_result if connect_result != ConnectionResult.Success: raise ProtocolError("Connecting failed! Result: %s" % connect_result) self.target_participant_id = 0 self.source_participant_id = result.protected_payload.participant_id self.local_join() for channel, target_uuid in CHANNEL_MAP.items(): self.start_channel(channel, target_uuid) gevent.spawn_later(self.HEARTBEAT_INTERVAL, self._heartbeat) return result.protected_payload.pairing_state