pymobiledevice3 5.0.1__py3-none-any.whl → 5.0.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pymobiledevice3 might be problematic. Click here for more details.

Files changed (143) hide show
  1. misc/plist_sniffer.py +15 -15
  2. misc/remotexpc_sniffer.py +29 -28
  3. pymobiledevice3/__main__.py +128 -102
  4. pymobiledevice3/_version.py +2 -2
  5. pymobiledevice3/bonjour.py +36 -59
  6. pymobiledevice3/ca.py +32 -24
  7. pymobiledevice3/cli/activation.py +7 -7
  8. pymobiledevice3/cli/afc.py +19 -19
  9. pymobiledevice3/cli/amfi.py +4 -4
  10. pymobiledevice3/cli/apps.py +51 -39
  11. pymobiledevice3/cli/backup.py +58 -32
  12. pymobiledevice3/cli/bonjour.py +25 -18
  13. pymobiledevice3/cli/cli_common.py +112 -81
  14. pymobiledevice3/cli/companion_proxy.py +4 -4
  15. pymobiledevice3/cli/completions.py +10 -10
  16. pymobiledevice3/cli/crash.py +37 -31
  17. pymobiledevice3/cli/developer.py +602 -520
  18. pymobiledevice3/cli/diagnostics.py +38 -33
  19. pymobiledevice3/cli/lockdown.py +79 -74
  20. pymobiledevice3/cli/mounter.py +85 -68
  21. pymobiledevice3/cli/notification.py +10 -10
  22. pymobiledevice3/cli/pcap.py +19 -14
  23. pymobiledevice3/cli/power_assertion.py +12 -10
  24. pymobiledevice3/cli/processes.py +10 -10
  25. pymobiledevice3/cli/profile.py +88 -77
  26. pymobiledevice3/cli/provision.py +17 -17
  27. pymobiledevice3/cli/remote.py +186 -110
  28. pymobiledevice3/cli/restore.py +43 -40
  29. pymobiledevice3/cli/springboard.py +30 -28
  30. pymobiledevice3/cli/syslog.py +85 -58
  31. pymobiledevice3/cli/usbmux.py +21 -20
  32. pymobiledevice3/cli/version.py +3 -2
  33. pymobiledevice3/cli/webinspector.py +157 -79
  34. pymobiledevice3/common.py +1 -1
  35. pymobiledevice3/exceptions.py +154 -60
  36. pymobiledevice3/irecv.py +49 -53
  37. pymobiledevice3/irecv_devices.py +1489 -492
  38. pymobiledevice3/lockdown.py +396 -242
  39. pymobiledevice3/lockdown_service_provider.py +5 -7
  40. pymobiledevice3/osu/os_utils.py +18 -9
  41. pymobiledevice3/osu/posix_util.py +28 -15
  42. pymobiledevice3/osu/win_util.py +14 -8
  43. pymobiledevice3/pair_records.py +19 -19
  44. pymobiledevice3/remote/common.py +4 -4
  45. pymobiledevice3/remote/core_device/app_service.py +94 -67
  46. pymobiledevice3/remote/core_device/core_device_service.py +17 -14
  47. pymobiledevice3/remote/core_device/device_info.py +5 -5
  48. pymobiledevice3/remote/core_device/diagnostics_service.py +10 -8
  49. pymobiledevice3/remote/core_device/file_service.py +47 -33
  50. pymobiledevice3/remote/remote_service_discovery.py +53 -35
  51. pymobiledevice3/remote/remotexpc.py +64 -42
  52. pymobiledevice3/remote/tunnel_service.py +371 -293
  53. pymobiledevice3/remote/utils.py +12 -11
  54. pymobiledevice3/remote/xpc_message.py +145 -125
  55. pymobiledevice3/resources/dsc_uuid_map.py +19 -19
  56. pymobiledevice3/resources/firmware_notifications.py +16 -16
  57. pymobiledevice3/restore/asr.py +27 -27
  58. pymobiledevice3/restore/base_restore.py +90 -47
  59. pymobiledevice3/restore/consts.py +87 -66
  60. pymobiledevice3/restore/device.py +11 -11
  61. pymobiledevice3/restore/fdr.py +46 -46
  62. pymobiledevice3/restore/ftab.py +19 -19
  63. pymobiledevice3/restore/img4.py +130 -133
  64. pymobiledevice3/restore/mbn.py +35 -54
  65. pymobiledevice3/restore/recovery.py +125 -135
  66. pymobiledevice3/restore/restore.py +524 -523
  67. pymobiledevice3/restore/restore_options.py +122 -115
  68. pymobiledevice3/restore/restored_client.py +25 -22
  69. pymobiledevice3/restore/tss.py +378 -270
  70. pymobiledevice3/service_connection.py +50 -46
  71. pymobiledevice3/services/accessibilityaudit.py +137 -127
  72. pymobiledevice3/services/afc.py +350 -291
  73. pymobiledevice3/services/amfi.py +21 -18
  74. pymobiledevice3/services/companion.py +23 -19
  75. pymobiledevice3/services/crash_reports.py +60 -46
  76. pymobiledevice3/services/debugserver_applist.py +3 -3
  77. pymobiledevice3/services/device_arbitration.py +8 -8
  78. pymobiledevice3/services/device_link.py +56 -48
  79. pymobiledevice3/services/diagnostics.py +971 -968
  80. pymobiledevice3/services/dtfetchsymbols.py +8 -8
  81. pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py +4 -4
  82. pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py +4 -4
  83. pymobiledevice3/services/dvt/instruments/activity_trace_tap.py +85 -74
  84. pymobiledevice3/services/dvt/instruments/application_listing.py +2 -3
  85. pymobiledevice3/services/dvt/instruments/condition_inducer.py +7 -6
  86. pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +442 -421
  87. pymobiledevice3/services/dvt/instruments/device_info.py +11 -11
  88. pymobiledevice3/services/dvt/instruments/energy_monitor.py +1 -1
  89. pymobiledevice3/services/dvt/instruments/graphics.py +1 -1
  90. pymobiledevice3/services/dvt/instruments/location_simulation.py +1 -1
  91. pymobiledevice3/services/dvt/instruments/location_simulation_base.py +10 -10
  92. pymobiledevice3/services/dvt/instruments/network_monitor.py +17 -17
  93. pymobiledevice3/services/dvt/instruments/notifications.py +1 -1
  94. pymobiledevice3/services/dvt/instruments/process_control.py +25 -10
  95. pymobiledevice3/services/dvt/instruments/screenshot.py +2 -2
  96. pymobiledevice3/services/dvt/instruments/sysmontap.py +15 -15
  97. pymobiledevice3/services/dvt/testmanaged/xcuitest.py +42 -52
  98. pymobiledevice3/services/file_relay.py +10 -10
  99. pymobiledevice3/services/heartbeat.py +8 -7
  100. pymobiledevice3/services/house_arrest.py +12 -15
  101. pymobiledevice3/services/installation_proxy.py +119 -100
  102. pymobiledevice3/services/lockdown_service.py +12 -5
  103. pymobiledevice3/services/misagent.py +22 -19
  104. pymobiledevice3/services/mobile_activation.py +84 -72
  105. pymobiledevice3/services/mobile_config.py +330 -301
  106. pymobiledevice3/services/mobile_image_mounter.py +137 -116
  107. pymobiledevice3/services/mobilebackup2.py +188 -150
  108. pymobiledevice3/services/notification_proxy.py +11 -11
  109. pymobiledevice3/services/os_trace.py +69 -51
  110. pymobiledevice3/services/pcapd.py +306 -306
  111. pymobiledevice3/services/power_assertion.py +10 -9
  112. pymobiledevice3/services/preboard.py +4 -4
  113. pymobiledevice3/services/remote_fetch_symbols.py +16 -14
  114. pymobiledevice3/services/remote_server.py +176 -146
  115. pymobiledevice3/services/restore_service.py +16 -16
  116. pymobiledevice3/services/screenshot.py +13 -10
  117. pymobiledevice3/services/simulate_location.py +7 -7
  118. pymobiledevice3/services/springboard.py +15 -15
  119. pymobiledevice3/services/syslog.py +5 -5
  120. pymobiledevice3/services/web_protocol/alert.py +3 -3
  121. pymobiledevice3/services/web_protocol/automation_session.py +183 -179
  122. pymobiledevice3/services/web_protocol/cdp_screencast.py +44 -36
  123. pymobiledevice3/services/web_protocol/cdp_server.py +19 -19
  124. pymobiledevice3/services/web_protocol/cdp_target.py +411 -373
  125. pymobiledevice3/services/web_protocol/driver.py +47 -45
  126. pymobiledevice3/services/web_protocol/element.py +74 -63
  127. pymobiledevice3/services/web_protocol/inspector_session.py +106 -102
  128. pymobiledevice3/services/web_protocol/selenium_api.py +3 -3
  129. pymobiledevice3/services/web_protocol/session_protocol.py +15 -10
  130. pymobiledevice3/services/web_protocol/switch_to.py +11 -12
  131. pymobiledevice3/services/webinspector.py +129 -117
  132. pymobiledevice3/tcp_forwarder.py +35 -22
  133. pymobiledevice3/tunneld/api.py +20 -15
  134. pymobiledevice3/tunneld/server.py +212 -133
  135. pymobiledevice3/usbmux.py +183 -138
  136. pymobiledevice3/utils.py +14 -11
  137. {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.3.dist-info}/METADATA +1 -1
  138. pymobiledevice3-5.0.3.dist-info/RECORD +173 -0
  139. pymobiledevice3-5.0.1.dist-info/RECORD +0 -173
  140. {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.3.dist-info}/WHEEL +0 -0
  141. {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.3.dist-info}/entry_points.txt +0 -0
  142. {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.3.dist-info}/licenses/LICENSE +0 -0
  143. {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.3.dist-info}/top_level.txt +0 -0
@@ -21,9 +21,8 @@ from socket import create_connection
21
21
  from ssl import VerifyMode
22
22
  from typing import Optional, TextIO, cast
23
23
 
24
- from construct import Const, Container
24
+ from construct import Const, Container, GreedyBytes, GreedyRange, Int8ul, Int16ub, Int64ul, Prefixed, Struct
25
25
  from construct import Enum as ConstructEnum
26
- from construct import GreedyBytes, GreedyRange, Int8ul, Int16ub, Int64ul, Prefixed, Struct
27
26
  from cryptography.hazmat.primitives import hashes
28
27
  from cryptography.hazmat.primitives._serialization import Encoding, NoEncryption, PrivateFormat, PublicFormat
29
28
  from cryptography.hazmat.primitives.asymmetric import rsa
@@ -55,10 +54,20 @@ except ImportError:
55
54
 
56
55
  from pymobiledevice3.bonjour import DEFAULT_BONJOUR_TIMEOUT, browse_remotepairing
57
56
  from pymobiledevice3.ca import make_cert
58
- from pymobiledevice3.exceptions import PairingError, PyMobileDevice3Exception, QuicProtocolNotSupportedError, \
59
- RemotePairingCompletedError, UserDeniedPairingError
60
- from pymobiledevice3.pair_records import PAIRING_RECORD_EXT, create_pairing_records_cache_folder, generate_host_id, \
61
- get_remote_pairing_record_filename, iter_remote_paired_identifiers
57
+ from pymobiledevice3.exceptions import (
58
+ PairingError,
59
+ PyMobileDevice3Exception,
60
+ QuicProtocolNotSupportedError,
61
+ RemotePairingCompletedError,
62
+ UserDeniedPairingError,
63
+ )
64
+ from pymobiledevice3.pair_records import (
65
+ PAIRING_RECORD_EXT,
66
+ create_pairing_records_cache_folder,
67
+ generate_host_id,
68
+ get_remote_pairing_record_filename,
69
+ iter_remote_paired_identifiers,
70
+ )
62
71
  from pymobiledevice3.remote.common import TunnelProtocol
63
72
  from pymobiledevice3.remote.remote_service import RemoteService
64
73
  from pymobiledevice3.remote.remote_service_discovery import RemoteServiceDiscoveryService
@@ -67,7 +76,7 @@ from pymobiledevice3.remote.xpc_message import XpcInt64Type, XpcUInt64Type
67
76
  from pymobiledevice3.service_connection import ServiceConnection
68
77
  from pymobiledevice3.utils import asyncio_print_traceback
69
78
 
70
- DEFAULT_INTERFACE_NAME = 'pymobiledevice3-tunnel'
79
+ DEFAULT_INTERFACE_NAME = "pymobiledevice3-tunnel"
71
80
  TIMEOUT = 1
72
81
 
73
82
  OSUTIL = get_os_utils()
@@ -81,58 +90,60 @@ UDP_HEADER_SIZE = 8
81
90
  IOS_DEVICE_MTU_SIZE = 1500
82
91
  packet_builder.PACKET_MAX_SIZE = IOS_DEVICE_MTU_SIZE - IPV6_HEADER_SIZE - UDP_HEADER_SIZE
83
92
 
84
- PairingDataComponentType = ConstructEnum(Int8ul,
85
- METHOD=0x00,
86
- IDENTIFIER=0x01,
87
- SALT=0x02,
88
- PUBLIC_KEY=0x03,
89
- PROOF=0x04,
90
- ENCRYPTED_DATA=0x05,
91
- STATE=0x06,
92
- ERROR=0x07,
93
- RETRY_DELAY=0x08,
94
- CERTIFICATE=0x09,
95
- SIGNATURE=0x0a,
96
- PERMISSIONS=0x0b,
97
- FRAGMENT_DATA=0x0c,
98
- FRAGMENT_LAST=0x0d,
99
- SESSION_ID=0x0e,
100
- TTL=0x0f,
101
- EXTRA_DATA=0x10,
102
- INFO=0x11,
103
- ACL=0x12,
104
- FLAGS=0x13,
105
- VALIDATION_DATA=0x14,
106
- MFI_AUTH_TOKEN=0x15,
107
- MFI_PRODUCT_TYPE=0x16,
108
- SERIAL_NUMBER=0x17,
109
- MFI_AUTH_TOKEN_UUID=0x18,
110
- APP_FLAGS=0x19,
111
- OWNERSHIP_PROOF=0x1a,
112
- SETUP_CODE_TYPE=0x1b,
113
- PRODUCTION_DATA=0x1c,
114
- APP_INFO=0x1d,
115
- SEPARATOR=0xff)
93
+ PairingDataComponentType = ConstructEnum(
94
+ Int8ul,
95
+ METHOD=0x00,
96
+ IDENTIFIER=0x01,
97
+ SALT=0x02,
98
+ PUBLIC_KEY=0x03,
99
+ PROOF=0x04,
100
+ ENCRYPTED_DATA=0x05,
101
+ STATE=0x06,
102
+ ERROR=0x07,
103
+ RETRY_DELAY=0x08,
104
+ CERTIFICATE=0x09,
105
+ SIGNATURE=0x0A,
106
+ PERMISSIONS=0x0B,
107
+ FRAGMENT_DATA=0x0C,
108
+ FRAGMENT_LAST=0x0D,
109
+ SESSION_ID=0x0E,
110
+ TTL=0x0F,
111
+ EXTRA_DATA=0x10,
112
+ INFO=0x11,
113
+ ACL=0x12,
114
+ FLAGS=0x13,
115
+ VALIDATION_DATA=0x14,
116
+ MFI_AUTH_TOKEN=0x15,
117
+ MFI_PRODUCT_TYPE=0x16,
118
+ SERIAL_NUMBER=0x17,
119
+ MFI_AUTH_TOKEN_UUID=0x18,
120
+ APP_FLAGS=0x19,
121
+ OWNERSHIP_PROOF=0x1A,
122
+ SETUP_CODE_TYPE=0x1B,
123
+ PRODUCTION_DATA=0x1C,
124
+ APP_INFO=0x1D,
125
+ SEPARATOR=0xFF,
126
+ )
116
127
 
117
128
  PairingDataComponentTLV8 = Struct(
118
- 'type' / PairingDataComponentType,
119
- 'data' / Prefixed(Int8ul, GreedyBytes),
129
+ "type" / PairingDataComponentType,
130
+ "data" / Prefixed(Int8ul, GreedyBytes),
120
131
  )
121
132
 
122
133
  PairingDataComponentTLVBuf = GreedyRange(PairingDataComponentTLV8)
123
134
 
124
- PairConsentResult = namedtuple('PairConsentResult', 'public_key salt pin')
135
+ PairConsentResult = namedtuple("PairConsentResult", "public_key salt pin")
125
136
 
126
137
  CDTunnelPacket = Struct(
127
- 'magic' / Const(b'CDTunnel'),
128
- 'body' / Prefixed(Int16ub, GreedyBytes),
138
+ "magic" / Const(b"CDTunnel"),
139
+ "body" / Prefixed(Int16ub, GreedyBytes),
129
140
  )
130
141
 
131
- REPAIRING_PACKET_MAGIC = b'RPPairing'
142
+ REPAIRING_PACKET_MAGIC = b"RPPairing"
132
143
 
133
144
  RPPairingPacket = Struct(
134
- 'magic' / Const(REPAIRING_PACKET_MAGIC),
135
- 'body' / Prefixed(Int16ub, GreedyBytes),
145
+ "magic" / Const(REPAIRING_PACKET_MAGIC),
146
+ "body" / Prefixed(Int16ub, GreedyBytes),
136
147
  )
137
148
 
138
149
 
@@ -140,7 +151,7 @@ class RemotePairingTunnel(ABC):
140
151
  def __init__(self):
141
152
  self._queue = asyncio.Queue()
142
153
  self._tun_read_task = None
143
- self._logger = logging.getLogger(f'{__name__}.{self.__class__.__name__}')
154
+ self._logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")
144
155
  self.tun = None
145
156
 
146
157
  @abstractmethod
@@ -159,11 +170,11 @@ class RemotePairingTunnel(ABC):
159
170
  async def tun_read_task(self) -> None:
160
171
  read_size = self.tun.mtu + len(LOOPBACK_HEADER)
161
172
  try:
162
- if sys.platform != 'win32':
173
+ if sys.platform != "win32":
163
174
  while True:
164
175
  packet = await asyncio.to_thread(self.tun.read, read_size)
165
176
  assert packet.startswith(LOOPBACK_HEADER)
166
- packet = packet[len(LOOPBACK_HEADER):]
177
+ packet = packet[len(LOOPBACK_HEADER) :]
167
178
  await self.send_packet_to_device(packet)
168
179
  else:
169
180
  while True:
@@ -174,12 +185,12 @@ class RemotePairingTunnel(ABC):
174
185
  continue
175
186
  await self.send_packet_to_device(packet)
176
187
  except ConnectionResetError:
177
- self._logger.warning(f'got connection reset in {asyncio.current_task().get_name()}')
188
+ self._logger.warning(f"got connection reset in {asyncio.current_task().get_name()}")
178
189
  except OSError:
179
- self._logger.warning(f'got oserror in {asyncio.current_task().get_name()}')
190
+ self._logger.warning(f"got oserror in {asyncio.current_task().get_name()}")
180
191
 
181
192
  def start_tunnel(self, address: str, mtu: int, interface_name=DEFAULT_INTERFACE_NAME) -> None:
182
- if 'win32' == sys.platform:
193
+ if sys.platform == "win32":
183
194
  # Only win32 tunnel implementation supports interface name
184
195
  self.tun = TunTapDevice(interface_name)
185
196
  else:
@@ -187,10 +198,10 @@ class RemotePairingTunnel(ABC):
187
198
  self.tun.addr = address
188
199
  self.tun.mtu = mtu
189
200
  self.tun.up()
190
- self._tun_read_task = asyncio.create_task(self.tun_read_task(), name=f'tun-read-{address}')
201
+ self._tun_read_task = asyncio.create_task(self.tun_read_task(), name=f"tun-read-{address}")
191
202
 
192
203
  async def stop_tunnel(self) -> None:
193
- self._logger.debug(f'[{asyncio.current_task().get_name()}] stopping tunnel')
204
+ self._logger.debug(f"[{asyncio.current_task().get_name()}] stopping tunnel")
194
205
  self._tun_read_task.cancel()
195
206
  with suppress(CancelledError):
196
207
  await self._tun_read_task
@@ -200,7 +211,7 @@ class RemotePairingTunnel(ABC):
200
211
 
201
212
  @staticmethod
202
213
  def _encode_cdtunnel_packet(data: dict) -> bytes:
203
- return CDTunnelPacket.build({'body': json.dumps(data).encode()})
214
+ return CDTunnelPacket.build({"body": json.dumps(data).encode()})
204
215
 
205
216
 
206
217
  class RemotePairingQuicTunnel(RemotePairingTunnel, QuicConnectionProtocol):
@@ -214,10 +225,8 @@ class RemotePairingQuicTunnel(RemotePairingTunnel, QuicConnectionProtocol):
214
225
  self._keep_alive_task = None
215
226
 
216
227
  async def wait_closed(self) -> None:
217
- try:
228
+ with suppress(asyncio.CancelledError):
218
229
  await QuicConnectionProtocol.wait_closed(self)
219
- except asyncio.CancelledError:
220
- pass
221
230
 
222
231
  async def send_packet_to_device(self, packet: bytes) -> None:
223
232
  self._quic.send_datagram_frame(packet)
@@ -229,9 +238,10 @@ class RemotePairingQuicTunnel(RemotePairingTunnel, QuicConnectionProtocol):
229
238
  async def request_tunnel_establish(self) -> dict:
230
239
  stream_id = self._quic.get_next_available_stream_id()
231
240
  # pad the data with random data to force the MTU size correctly
232
- self._quic.send_datagram_frame(b'x' * 1024)
233
- self._quic.send_stream_data(stream_id, self._encode_cdtunnel_packet(
234
- {'type': 'clientHandshakeRequest', 'mtu': self.REQUESTED_MTU}))
241
+ self._quic.send_datagram_frame(b"x" * 1024)
242
+ self._quic.send_stream_data(
243
+ stream_id, self._encode_cdtunnel_packet({"type": "clientHandshakeRequest", "mtu": self.REQUESTED_MTU})
244
+ )
235
245
  self.transmit()
236
246
  return await self._queue.get()
237
247
 
@@ -261,7 +271,7 @@ class RemotePairingQuicTunnel(RemotePairingTunnel, QuicConnectionProtocol):
261
271
 
262
272
  @staticmethod
263
273
  def _encode_cdtunnel_packet(data: dict) -> bytes:
264
- return CDTunnelPacket.build({'body': json.dumps(data).encode()})
274
+ return CDTunnelPacket.build({"body": json.dumps(data).encode()})
265
275
 
266
276
 
267
277
  class RemotePairingTcpTunnel(RemotePairingTunnel):
@@ -283,30 +293,27 @@ class RemotePairingTcpTunnel(RemotePairingTunnel):
283
293
  while True:
284
294
  try:
285
295
  ipv6_header = await self._reader.readexactly(IPV6_HEADER_SIZE)
286
- ipv6_length = struct.unpack('>H', ipv6_header[4:6])[0]
296
+ ipv6_length = struct.unpack(">H", ipv6_header[4:6])[0]
287
297
  ipv6_body = await self._reader.readexactly(ipv6_length)
288
298
  self.tun.write(LOOPBACK_HEADER + ipv6_header + ipv6_body)
289
299
  except asyncio.exceptions.IncompleteReadError:
290
300
  await asyncio.sleep(1)
291
301
  except OSError as e:
292
- self._logger.warning(f'got {e.__class__.__name__} in {asyncio.current_task().get_name()}')
302
+ self._logger.warning(f"got {e.__class__.__name__} in {asyncio.current_task().get_name()}")
293
303
  await self.wait_closed()
294
304
 
295
305
  async def wait_closed(self) -> None:
296
- try:
306
+ with suppress(OSError):
297
307
  await self._writer.wait_closed()
298
- except OSError:
299
- pass
300
308
 
301
309
  async def request_tunnel_establish(self) -> dict:
302
- self._writer.write(self._encode_cdtunnel_packet(
303
- {'type': 'clientHandshakeRequest', 'mtu': self.REQUESTED_MTU}))
310
+ self._writer.write(self._encode_cdtunnel_packet({"type": "clientHandshakeRequest", "mtu": self.REQUESTED_MTU}))
304
311
  await self._writer.drain()
305
312
  return json.loads(CDTunnelPacket.parse(await self._reader.read(self.REQUESTED_MTU)).body)
306
313
 
307
314
  def start_tunnel(self, address: str, mtu: int, interface_name=DEFAULT_INTERFACE_NAME) -> None:
308
315
  super().start_tunnel(address, mtu, interface_name=interface_name)
309
- self._sock_read_task = asyncio.create_task(self.sock_read_task(), name=f'sock-read-task-{address}')
316
+ self._sock_read_task = asyncio.create_task(self.sock_read_task(), name=f"sock-read-task-{address}")
310
317
 
311
318
  async def stop_tunnel(self) -> None:
312
319
  self._sock_read_task.cancel()
@@ -315,10 +322,8 @@ class RemotePairingTcpTunnel(RemotePairingTunnel):
315
322
  await super().stop_tunnel()
316
323
  if not self._writer.is_closing():
317
324
  self._writer.close()
318
- try:
325
+ with suppress(OSError):
319
326
  await self._writer.wait_closed()
320
- except OSError:
321
- pass
322
327
 
323
328
 
324
329
  @dataclasses.dataclass
@@ -392,112 +397,143 @@ class RemotePairingProtocol(StartTcpTunnel):
392
397
  raise RemotePairingCompletedError()
393
398
 
394
399
  async def create_quic_listener(self, private_key: RSAPrivateKey) -> dict:
395
- request = {'request': {'_0': {'createListener': {
396
- 'key': base64.b64encode(
397
- private_key.public_key().public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo)
398
- ).decode(),
399
- 'peerConnectionsInfo': [{'owningPID': os.getpid(), 'owningProcessName': 'CoreDeviceService'}],
400
- 'transportProtocolType': 'quic'}}}}
400
+ request = {
401
+ "request": {
402
+ "_0": {
403
+ "createListener": {
404
+ "key": base64.b64encode(
405
+ private_key.public_key().public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo)
406
+ ).decode(),
407
+ "peerConnectionsInfo": [{"owningPID": os.getpid(), "owningProcessName": "CoreDeviceService"}],
408
+ "transportProtocolType": "quic",
409
+ }
410
+ }
411
+ }
412
+ }
401
413
 
402
414
  response = await self._send_receive_encrypted_request(request)
403
- return response['createListener']
415
+ return response["createListener"]
404
416
 
405
417
  async def create_tcp_listener(self) -> dict:
406
- request = {'request': {'_0': {'createListener': {
407
- 'key': base64.b64encode(self.encryption_key).decode(),
408
- 'peerConnectionsInfo': [{'owningPID': os.getpid(), 'owningProcessName': 'CoreDeviceService'}],
409
- 'transportProtocolType': 'tcp'}}}}
418
+ request = {
419
+ "request": {
420
+ "_0": {
421
+ "createListener": {
422
+ "key": base64.b64encode(self.encryption_key).decode(),
423
+ "peerConnectionsInfo": [{"owningPID": os.getpid(), "owningProcessName": "CoreDeviceService"}],
424
+ "transportProtocolType": "tcp",
425
+ }
426
+ }
427
+ }
428
+ }
410
429
  response = await self._send_receive_encrypted_request(request)
411
- return response['createListener']
430
+ return response["createListener"]
412
431
 
413
432
  @asynccontextmanager
414
433
  async def start_quic_tunnel(
415
- self, secrets_log_file: Optional[TextIO] = None,
416
- max_idle_timeout: float = RemotePairingQuicTunnel.MAX_IDLE_TIMEOUT) -> AsyncGenerator[TunnelResult, None]:
434
+ self,
435
+ secrets_log_file: Optional[TextIO] = None,
436
+ max_idle_timeout: float = RemotePairingQuicTunnel.MAX_IDLE_TIMEOUT,
437
+ ) -> AsyncGenerator[TunnelResult, None]:
417
438
  private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
418
439
  parameters = await self.create_quic_listener(private_key)
419
440
  cert = make_cert(private_key, private_key.public_key())
420
441
  configuration = QuicConfiguration(
421
- alpn_protocols=['RemotePairingTunnelProtocol'],
442
+ alpn_protocols=["RemotePairingTunnelProtocol"],
422
443
  is_client=True,
423
444
  verify_mode=VerifyMode.CERT_NONE,
424
445
  verify_hostname=False,
425
446
  max_datagram_frame_size=RemotePairingQuicTunnel.MAX_QUIC_DATAGRAM,
426
- idle_timeout=max_idle_timeout
447
+ idle_timeout=max_idle_timeout,
448
+ )
449
+ configuration.load_cert_chain(
450
+ cert.public_bytes(Encoding.PEM),
451
+ private_key.private_bytes(Encoding.PEM, PrivateFormat.TraditionalOpenSSL, NoEncryption()).decode(),
427
452
  )
428
- configuration.load_cert_chain(cert.public_bytes(Encoding.PEM),
429
- private_key.private_bytes(Encoding.PEM, PrivateFormat.TraditionalOpenSSL,
430
- NoEncryption()).decode())
431
453
  configuration.secrets_log_file = secrets_log_file
432
454
 
433
455
  host = self.hostname
434
- port = parameters['port']
456
+ port = parameters["port"]
435
457
 
436
- self.logger.debug(f'Connecting to {host}:{port}')
458
+ self.logger.debug(f"Connecting to {host}:{port}")
437
459
  try:
438
460
  async with aioquic_connect(
439
- host,
440
- port,
441
- configuration=configuration,
442
- create_protocol=RemotePairingQuicTunnel,
461
+ host,
462
+ port,
463
+ configuration=configuration,
464
+ create_protocol=RemotePairingQuicTunnel,
443
465
  ) as client:
444
- self.logger.debug('quic connected')
466
+ self.logger.debug("quic connected")
445
467
  client = cast(RemotePairingQuicTunnel, client)
446
468
  await client.wait_connected()
447
469
  handshake_response = await client.request_tunnel_establish()
448
- client.start_tunnel(handshake_response['clientParameters']['address'],
449
- handshake_response['clientParameters']['mtu'],
450
- interface_name=f'{DEFAULT_INTERFACE_NAME}-{self.remote_identifier}')
470
+ client.start_tunnel(
471
+ handshake_response["clientParameters"]["address"],
472
+ handshake_response["clientParameters"]["mtu"],
473
+ interface_name=f"{DEFAULT_INTERFACE_NAME}-{self.remote_identifier}",
474
+ )
451
475
  try:
452
476
  yield TunnelResult(
453
- client.tun.name, handshake_response['serverAddress'], handshake_response['serverRSDPort'],
454
- TunnelProtocol.QUIC, client)
477
+ client.tun.name,
478
+ handshake_response["serverAddress"],
479
+ handshake_response["serverRSDPort"],
480
+ TunnelProtocol.QUIC,
481
+ client,
482
+ )
455
483
  finally:
456
484
  await client.stop_tunnel()
457
- except ConnectionError:
485
+ except ConnectionError as e:
458
486
  raise QuicProtocolNotSupportedError(
459
- 'iOS 18.2+ removed QUIC protocol support. Use TCP instead (requires python3.13+)')
487
+ "iOS 18.2+ removed QUIC protocol support. Use TCP instead (requires python3.13+)"
488
+ ) from e
460
489
 
461
490
  @asynccontextmanager
462
491
  async def start_tcp_tunnel(self) -> AsyncGenerator[TunnelResult, None]:
463
492
  parameters = await self.create_tcp_listener()
464
493
  host = self.hostname
465
- port = parameters['port']
494
+ port = parameters["port"]
466
495
  sock = create_connection((host, port))
467
496
  OSUTIL.set_keepalive(sock)
468
497
  if sys.version_info >= (3, 13):
469
498
  ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
470
499
  ctx.check_hostname = False
471
500
  ctx.verify_mode = ssl.CERT_NONE
472
- ctx.set_ciphers('PSK')
501
+ ctx.set_ciphers("PSK")
473
502
  ctx.set_psk_client_callback(lambda hint: (None, self.encryption_key))
474
503
  else:
475
504
  # TODO: remove this when python3.12 becomes deprecated
476
505
  ctx = SSLPSKContext(ssl.PROTOCOL_TLSv1_2)
477
506
  ctx.psk = self.encryption_key
478
- ctx.set_ciphers('PSK')
479
- reader, writer = await asyncio.open_connection(sock=sock, ssl=ctx, server_hostname='')
507
+ ctx.set_ciphers("PSK")
508
+ reader, writer = await asyncio.open_connection(sock=sock, ssl=ctx, server_hostname="")
480
509
  tunnel = RemotePairingTcpTunnel(reader, writer)
481
510
  handshake_response = await tunnel.request_tunnel_establish()
482
511
 
483
- tunnel.start_tunnel(handshake_response['clientParameters']['address'],
484
- handshake_response['clientParameters']['mtu'],
485
- interface_name=f'{DEFAULT_INTERFACE_NAME}-{self.remote_identifier}')
512
+ tunnel.start_tunnel(
513
+ handshake_response["clientParameters"]["address"],
514
+ handshake_response["clientParameters"]["mtu"],
515
+ interface_name=f"{DEFAULT_INTERFACE_NAME}-{self.remote_identifier}",
516
+ )
486
517
 
487
518
  try:
488
519
  yield TunnelResult(
489
- tunnel.tun.name, handshake_response['serverAddress'], handshake_response['serverRSDPort'],
490
- TunnelProtocol.TCP, tunnel)
520
+ tunnel.tun.name,
521
+ handshake_response["serverAddress"],
522
+ handshake_response["serverRSDPort"],
523
+ TunnelProtocol.TCP,
524
+ tunnel,
525
+ )
491
526
  finally:
492
527
  await tunnel.stop_tunnel()
493
528
 
494
529
  def save_pair_record(self) -> None:
495
530
  self.pair_record_path.write_bytes(
496
531
  plistlib.dumps({
497
- 'public_key': self.ed25519_private_key.public_key().public_bytes_raw(),
498
- 'private_key': self.ed25519_private_key.private_bytes_raw(),
499
- 'remote_unlock_host_key': self.remote_unlock_host_key
500
- }))
532
+ "public_key": self.ed25519_private_key.public_key().public_bytes_raw(),
533
+ "private_key": self.ed25519_private_key.private_bytes_raw(),
534
+ "remote_unlock_host_key": self.remote_unlock_host_key,
535
+ })
536
+ )
501
537
  OSUTIL.chown_to_non_sudo_if_needed(self.pair_record_path)
502
538
 
503
539
  @property
@@ -508,17 +544,19 @@ class RemotePairingProtocol(StartTcpTunnel):
508
544
 
509
545
  @property
510
546
  def remote_identifier(self) -> str:
511
- return self.handshake_info['peerDeviceInfo']['identifier']
547
+ return self.handshake_info["peerDeviceInfo"]["identifier"]
512
548
 
513
549
  @property
514
550
  def remote_device_model(self) -> str:
515
- return self.handshake_info['peerDeviceInfo']['model']
551
+ return self.handshake_info["peerDeviceInfo"]["model"]
516
552
 
517
553
  @property
518
554
  def pair_record_path(self) -> Path:
519
555
  pair_records_cache_directory = create_pairing_records_cache_folder()
520
- return (pair_records_cache_directory /
521
- f'{get_remote_pairing_record_filename(self.remote_identifier)}.{PAIRING_RECORD_EXT}')
556
+ return (
557
+ pair_records_cache_directory
558
+ / f"{get_remote_pairing_record_filename(self.remote_identifier)}.{PAIRING_RECORD_EXT}"
559
+ )
522
560
 
523
561
  async def _pair(self) -> None:
524
562
  pairing_consent_result = await self._request_pair_consent()
@@ -530,47 +568,49 @@ class RemotePairingProtocol(StartTcpTunnel):
530
568
  self.save_pair_record()
531
569
 
532
570
  async def _request_pair_consent(self) -> PairConsentResult:
533
- """ Display a Trust / Don't Trust dialog """
571
+ """Display a Trust / Don't Trust dialog"""
534
572
 
535
573
  tlv = PairingDataComponentTLVBuf.build([
536
- {'type': PairingDataComponentType.METHOD, 'data': b'\x00'},
537
- {'type': PairingDataComponentType.STATE, 'data': b'\x01'},
574
+ {"type": PairingDataComponentType.METHOD, "data": b"\x00"},
575
+ {"type": PairingDataComponentType.STATE, "data": b"\x01"},
538
576
  ])
539
577
 
540
- await self._send_pairing_data({'data': tlv,
541
- 'kind': 'setupManualPairing',
542
- 'sendingHost': platform.node(),
543
- 'startNewSession': True})
544
- self.logger.info('Waiting user pairing consent')
578
+ await self._send_pairing_data({
579
+ "data": tlv,
580
+ "kind": "setupManualPairing",
581
+ "sendingHost": platform.node(),
582
+ "startNewSession": True,
583
+ })
584
+ self.logger.info("Waiting user pairing consent")
545
585
  response = await self._receive_plain_response()
546
- response = response['event']['_0']
586
+ response = response["event"]["_0"]
547
587
 
548
588
  pin = None
549
- if 'pairingRejectedWithError' in response:
589
+ if "pairingRejectedWithError" in response:
550
590
  raise PairingError(
551
- response['pairingRejectedWithError']['wrappedError']['userInfo']['NSLocalizedDescription'])
552
- elif 'awaitingUserConsent' in response:
591
+ response["pairingRejectedWithError"]["wrappedError"]["userInfo"]["NSLocalizedDescription"]
592
+ )
593
+ elif "awaitingUserConsent" in response:
553
594
  pairing_data = await self._receive_pairing_data()
554
595
  else:
555
596
  # On tvOS no consent is needed and pairing data is returned immediately.
556
- pairing_data = self._decode_bytes_if_needed(response['pairingData']['_0']['data'])
597
+ pairing_data = self._decode_bytes_if_needed(response["pairingData"]["_0"]["data"])
557
598
  # On tvOS we need pin to setup pairing.
558
- if 'AppleTV' in self.remote_device_model:
559
- pin = input('Enter PIN: ')
599
+ if "AppleTV" in self.remote_device_model:
600
+ pin = input("Enter PIN: ")
560
601
 
561
602
  data = self.decode_tlv(PairingDataComponentTLVBuf.parse(pairing_data))
562
- return PairConsentResult(public_key=data[PairingDataComponentType.PUBLIC_KEY],
563
- salt=data[PairingDataComponentType.SALT],
564
- pin=pin)
603
+ return PairConsentResult(
604
+ public_key=data[PairingDataComponentType.PUBLIC_KEY], salt=data[PairingDataComponentType.SALT], pin=pin
605
+ )
565
606
 
566
607
  def _init_srp_context(self, pairing_consent_result: PairConsentResult) -> None:
567
608
  # Receive server public and salt and process them.
568
- pin = pairing_consent_result.pin or '000000'
609
+ pin = pairing_consent_result.pin or "000000"
569
610
  client_session = SRPClientSession(
570
- SRPContext('Pair-Setup', password=pin, prime=PRIME_3072, generator=PRIME_3072_GEN,
571
- hash_func=hashlib.sha512))
572
- client_session.process(pairing_consent_result.public_key.hex(),
573
- pairing_consent_result.salt.hex())
611
+ SRPContext("Pair-Setup", password=pin, prime=PRIME_3072, generator=PRIME_3072_GEN, hash_func=hashlib.sha512)
612
+ )
613
+ client_session.process(pairing_consent_result.public_key.hex(), pairing_consent_result.salt.hex())
574
614
  self.srp_context = client_session
575
615
  self.encryption_key = binascii.unhexlify(self.srp_context.key)
576
616
 
@@ -579,17 +619,18 @@ class RemotePairingProtocol(StartTcpTunnel):
579
619
  client_session_key_proof = binascii.unhexlify(self.srp_context.key_proof)
580
620
 
581
621
  tlv = PairingDataComponentTLVBuf.build([
582
- {'type': PairingDataComponentType.STATE, 'data': b'\x03'},
583
- {'type': PairingDataComponentType.PUBLIC_KEY, 'data': client_public[:255]},
584
- {'type': PairingDataComponentType.PUBLIC_KEY, 'data': client_public[255:]},
585
- {'type': PairingDataComponentType.PROOF, 'data': client_session_key_proof},
622
+ {"type": PairingDataComponentType.STATE, "data": b"\x03"},
623
+ {"type": PairingDataComponentType.PUBLIC_KEY, "data": client_public[:255]},
624
+ {"type": PairingDataComponentType.PUBLIC_KEY, "data": client_public[255:]},
625
+ {"type": PairingDataComponentType.PROOF, "data": client_session_key_proof},
586
626
  ])
587
627
 
588
628
  response = await self._send_receive_pairing_data({
589
- 'data': tlv,
590
- 'kind': 'setupManualPairing',
591
- 'sendingHost': platform.node(),
592
- 'startNewSession': False})
629
+ "data": tlv,
630
+ "kind": "setupManualPairing",
631
+ "sendingHost": platform.node(),
632
+ "startNewSession": False,
633
+ })
593
634
  data = self.decode_tlv(PairingDataComponentTLVBuf.parse(response))
594
635
  assert self.srp_context.verify_proof(data[PairingDataComponentType.PROOF].hex().encode())
595
636
 
@@ -599,8 +640,8 @@ class RemotePairingProtocol(StartTcpTunnel):
599
640
  setup_encryption_key = HKDF(
600
641
  algorithm=hashes.SHA512(),
601
642
  length=32,
602
- salt=b'Pair-Setup-Encrypt-Salt',
603
- info=b'Pair-Setup-Encrypt-Info',
643
+ salt=b"Pair-Setup-Encrypt-Salt",
644
+ info=b"Pair-Setup-Encrypt-Info",
604
645
  ).derive(self.encryption_key)
605
646
 
606
647
  self.ed25519_private_key = Ed25519PrivateKey.generate()
@@ -610,8 +651,8 @@ class RemotePairingProtocol(StartTcpTunnel):
610
651
  signbuf = HKDF(
611
652
  algorithm=hashes.SHA512(),
612
653
  length=32,
613
- salt=b'Pair-Setup-Controller-Sign-Salt',
614
- info=b'Pair-Setup-Controller-Sign-Info',
654
+ salt=b"Pair-Setup-Controller-Sign-Salt",
655
+ info=b"Pair-Setup-Controller-Sign-Info",
615
656
  ).derive(self.encryption_key)
616
657
 
617
658
  signbuf += self.identifier.encode()
@@ -620,41 +661,45 @@ class RemotePairingProtocol(StartTcpTunnel):
620
661
  self.signature = self.ed25519_private_key.sign(signbuf)
621
662
 
622
663
  device_info = dumps({
623
- 'altIRK': b'\xe9\xe8-\xc0jIykVoT\x00\x19\xb1\xc7{',
624
- 'btAddr': '11:22:33:44:55:66',
625
- 'mac': b'\x11\x22\x33\x44\x55\x66',
626
- 'remotepairing_serial_number': 'AAAAAAAAAAAA',
627
- 'accountID': self.identifier,
628
- 'model': 'computer-model',
629
- 'name': platform.node()
664
+ "altIRK": b"\xe9\xe8-\xc0jIykVoT\x00\x19\xb1\xc7{",
665
+ "btAddr": "11:22:33:44:55:66",
666
+ "mac": b"\x11\x22\x33\x44\x55\x66",
667
+ "remotepairing_serial_number": "AAAAAAAAAAAA",
668
+ "accountID": self.identifier,
669
+ "model": "computer-model",
670
+ "name": platform.node(),
630
671
  })
631
672
 
632
673
  tlv = PairingDataComponentTLVBuf.build([
633
- {'type': PairingDataComponentType.IDENTIFIER, 'data': self.identifier.encode()},
634
- {'type': PairingDataComponentType.PUBLIC_KEY,
635
- 'data': self.ed25519_private_key.public_key().public_bytes_raw()},
636
- {'type': PairingDataComponentType.SIGNATURE, 'data': self.signature},
637
- {'type': PairingDataComponentType.INFO, 'data': device_info},
674
+ {"type": PairingDataComponentType.IDENTIFIER, "data": self.identifier.encode()},
675
+ {
676
+ "type": PairingDataComponentType.PUBLIC_KEY,
677
+ "data": self.ed25519_private_key.public_key().public_bytes_raw(),
678
+ },
679
+ {"type": PairingDataComponentType.SIGNATURE, "data": self.signature},
680
+ {"type": PairingDataComponentType.INFO, "data": device_info},
638
681
  ])
639
682
 
640
683
  cip = ChaCha20Poly1305(setup_encryption_key)
641
- encrypted_data = cip.encrypt(b'\x00\x00\x00\x00PS-Msg05', tlv, b'')
684
+ encrypted_data = cip.encrypt(b"\x00\x00\x00\x00PS-Msg05", tlv, b"")
642
685
 
643
686
  tlv = PairingDataComponentTLVBuf.build([
644
- {'type': PairingDataComponentType.ENCRYPTED_DATA, 'data': encrypted_data[:255]},
645
- {'type': PairingDataComponentType.ENCRYPTED_DATA, 'data': encrypted_data[255:]},
646
- {'type': PairingDataComponentType.STATE, 'data': b'\x05'},
687
+ {"type": PairingDataComponentType.ENCRYPTED_DATA, "data": encrypted_data[:255]},
688
+ {"type": PairingDataComponentType.ENCRYPTED_DATA, "data": encrypted_data[255:]},
689
+ {"type": PairingDataComponentType.STATE, "data": b"\x05"},
647
690
  ])
648
691
 
649
692
  response = await self._send_receive_pairing_data({
650
- 'data': tlv,
651
- 'kind': 'setupManualPairing',
652
- 'sendingHost': platform.node(),
653
- 'startNewSession': False})
693
+ "data": tlv,
694
+ "kind": "setupManualPairing",
695
+ "sendingHost": platform.node(),
696
+ "startNewSession": False,
697
+ })
654
698
  data = self.decode_tlv(PairingDataComponentTLVBuf.parse(response))
655
699
 
656
- tlv = PairingDataComponentTLVBuf.parse(cip.decrypt(
657
- b'\x00\x00\x00\x00PS-Msg06', data[PairingDataComponentType.ENCRYPTED_DATA], b''))
700
+ tlv = PairingDataComponentTLVBuf.parse(
701
+ cip.decrypt(b"\x00\x00\x00\x00PS-Msg06", data[PairingDataComponentType.ENCRYPTED_DATA], b"")
702
+ )
658
703
 
659
704
  return tlv
660
705
 
@@ -663,7 +708,7 @@ class RemotePairingProtocol(StartTcpTunnel):
663
708
  algorithm=hashes.SHA512(),
664
709
  length=32,
665
710
  salt=None,
666
- info=b'ClientEncrypt-main',
711
+ info=b"ClientEncrypt-main",
667
712
  ).derive(self.encryption_key)
668
713
  self.client_cip = ChaCha20Poly1305(client_key)
669
714
 
@@ -671,22 +716,23 @@ class RemotePairingProtocol(StartTcpTunnel):
671
716
  algorithm=hashes.SHA512(),
672
717
  length=32,
673
718
  salt=None,
674
- info=b'ServerEncrypt-main',
719
+ info=b"ServerEncrypt-main",
675
720
  ).derive(self.encryption_key)
676
721
  self.server_cip = ChaCha20Poly1305(server_key)
677
722
 
678
723
  async def _create_remote_unlock(self) -> None:
679
724
  try:
680
- response = await self._send_receive_encrypted_request({'request': {'_0': {'createRemoteUnlockKey': {}}}})
681
- self.remote_unlock_host_key = response['createRemoteUnlockKey']['hostKey']
725
+ response = await self._send_receive_encrypted_request({"request": {"_0": {"createRemoteUnlockKey": {}}}})
726
+ self.remote_unlock_host_key = response["createRemoteUnlockKey"]["hostKey"]
682
727
  except PyMobileDevice3Exception:
683
728
  # tvOS does not support remote unlock.
684
- self.remote_unlock_host_key = ''
729
+ self.remote_unlock_host_key = ""
685
730
 
686
731
  async def _attempt_pair_verify(self) -> None:
687
732
  self.handshake_info = await self._send_receive_handshake({
688
- 'hostOptions': {'attemptPairVerify': True},
689
- 'wireProtocolVersion': XpcInt64Type(self.WIRE_PROTOCOL_VERSION)})
733
+ "hostOptions": {"attemptPairVerify": True},
734
+ "wireProtocolVersion": XpcInt64Type(self.WIRE_PROTOCOL_VERSION),
735
+ })
690
736
 
691
737
  @staticmethod
692
738
  def _decode_bytes_if_needed(data: bytes) -> bytes:
@@ -694,13 +740,17 @@ class RemotePairingProtocol(StartTcpTunnel):
694
740
 
695
741
  async def _validate_pairing(self) -> bool:
696
742
  pairing_data = PairingDataComponentTLVBuf.build([
697
- {'type': PairingDataComponentType.STATE, 'data': b'\x01'},
698
- {'type': PairingDataComponentType.PUBLIC_KEY,
699
- 'data': self.x25519_private_key.public_key().public_bytes_raw()},
743
+ {"type": PairingDataComponentType.STATE, "data": b"\x01"},
744
+ {
745
+ "type": PairingDataComponentType.PUBLIC_KEY,
746
+ "data": self.x25519_private_key.public_key().public_bytes_raw(),
747
+ },
700
748
  ])
701
- response = await self._send_receive_pairing_data({'data': pairing_data,
702
- 'kind': 'verifyManualPairing',
703
- 'startNewSession': True})
749
+ response = await self._send_receive_pairing_data({
750
+ "data": pairing_data,
751
+ "kind": "verifyManualPairing",
752
+ "startNewSession": True,
753
+ })
704
754
  data = self.decode_tlv(PairingDataComponentTLVBuf.parse(response))
705
755
 
706
756
  if PairingDataComponentType.ERROR in data:
@@ -713,8 +763,8 @@ class RemotePairingProtocol(StartTcpTunnel):
713
763
  derived_key = HKDF(
714
764
  algorithm=hashes.SHA512(),
715
765
  length=32,
716
- salt=b'Pair-Verify-Encrypt-Salt',
717
- info=b'Pair-Verify-Encrypt-Info',
766
+ salt=b"Pair-Verify-Encrypt-Salt",
767
+ info=b"Pair-Verify-Encrypt-Info",
718
768
  ).derive(self.encryption_key)
719
769
  cip = ChaCha20Poly1305(derived_key)
720
770
 
@@ -723,31 +773,36 @@ class RemotePairingProtocol(StartTcpTunnel):
723
773
  # do so. instead, we verify using the next stage
724
774
 
725
775
  if self.pair_record is None:
726
- private_key = Ed25519PrivateKey.from_private_bytes(b'\x00' * 0x20)
776
+ private_key = Ed25519PrivateKey.from_private_bytes(b"\x00" * 0x20)
727
777
  else:
728
- private_key = Ed25519PrivateKey.from_private_bytes(self.pair_record['private_key'])
778
+ private_key = Ed25519PrivateKey.from_private_bytes(self.pair_record["private_key"])
729
779
 
730
- signbuf = b''
780
+ signbuf = b""
731
781
  signbuf += self.x25519_private_key.public_key().public_bytes_raw()
732
782
  signbuf += self.identifier.encode()
733
783
  signbuf += peer_public_key.public_bytes_raw()
734
784
 
735
785
  signature = private_key.sign(signbuf)
736
786
 
737
- encrypted_data = cip.encrypt(b'\x00\x00\x00\x00PV-Msg03', PairingDataComponentTLVBuf.build([
738
- {'type': PairingDataComponentType.IDENTIFIER, 'data': self.identifier.encode()},
739
- {'type': PairingDataComponentType.SIGNATURE, 'data': signature},
740
- ]), b'')
787
+ encrypted_data = cip.encrypt(
788
+ b"\x00\x00\x00\x00PV-Msg03",
789
+ PairingDataComponentTLVBuf.build([
790
+ {"type": PairingDataComponentType.IDENTIFIER, "data": self.identifier.encode()},
791
+ {"type": PairingDataComponentType.SIGNATURE, "data": signature},
792
+ ]),
793
+ b"",
794
+ )
741
795
 
742
796
  pairing_data = PairingDataComponentTLVBuf.build([
743
- {'type': PairingDataComponentType.STATE, 'data': b'\x03'},
744
- {'type': PairingDataComponentType.ENCRYPTED_DATA, 'data': encrypted_data},
797
+ {"type": PairingDataComponentType.STATE, "data": b"\x03"},
798
+ {"type": PairingDataComponentType.ENCRYPTED_DATA, "data": encrypted_data},
745
799
  ])
746
800
 
747
801
  response = await self._send_receive_pairing_data({
748
- 'data': pairing_data,
749
- 'kind': 'verifyManualPairing',
750
- 'startNewSession': False})
802
+ "data": pairing_data,
803
+ "kind": "verifyManualPairing",
804
+ "startNewSession": False,
805
+ })
751
806
  data = self.decode_tlv(PairingDataComponentTLVBuf.parse(response))
752
807
 
753
808
  if PairingDataComponentType.ERROR in data:
@@ -757,66 +812,68 @@ class RemotePairingProtocol(StartTcpTunnel):
757
812
  return True
758
813
 
759
814
  async def _send_pair_verify_failed(self) -> None:
760
- await self._send_plain_request({'event': {'_0': {'pairVerifyFailed': {}}}})
815
+ await self._send_plain_request({"event": {"_0": {"pairVerifyFailed": {}}}})
761
816
 
762
817
  async def _send_receive_encrypted_request(self, request: dict) -> dict:
763
- nonce = Int64ul.build(self._encrypted_sequence_number) + b'\x00' * 4
764
- encrypted_data = self.client_cip.encrypt(
765
- nonce,
766
- json.dumps(request).encode(),
767
- b'')
768
-
769
- response = await self.send_receive_request({'message': {
770
- 'streamEncrypted': {'_0': encrypted_data}},
771
- 'originatedBy': 'host',
772
- 'sequenceNumber': XpcUInt64Type(self._sequence_number)})
818
+ nonce = Int64ul.build(self._encrypted_sequence_number) + b"\x00" * 4
819
+ encrypted_data = self.client_cip.encrypt(nonce, json.dumps(request).encode(), b"")
820
+
821
+ response = await self.send_receive_request({
822
+ "message": {"streamEncrypted": {"_0": encrypted_data}},
823
+ "originatedBy": "host",
824
+ "sequenceNumber": XpcUInt64Type(self._sequence_number),
825
+ })
773
826
  self._encrypted_sequence_number += 1
774
827
 
775
- encrypted_data = self._decode_bytes_if_needed(response['message']['streamEncrypted']['_0'])
828
+ encrypted_data = self._decode_bytes_if_needed(response["message"]["streamEncrypted"]["_0"])
776
829
  plaintext = self.server_cip.decrypt(nonce, encrypted_data, None)
777
- response = json.loads(plaintext)['response']['_1']
830
+ response = json.loads(plaintext)["response"]["_1"]
778
831
 
779
- if 'errorExtended' in response:
780
- raise PyMobileDevice3Exception(response['errorExtended']['_0']['userInfo']['NSLocalizedDescription'])
832
+ if "errorExtended" in response:
833
+ raise PyMobileDevice3Exception(response["errorExtended"]["_0"]["userInfo"]["NSLocalizedDescription"])
781
834
 
782
835
  return response
783
836
 
784
837
  async def _send_receive_handshake(self, handshake_data: dict) -> dict:
785
- response = await self._send_receive_plain_request({'request': {'_0': {'handshake': {'_0': handshake_data}}}})
786
- return response['response']['_1']['handshake']['_0']
838
+ response = await self._send_receive_plain_request({"request": {"_0": {"handshake": {"_0": handshake_data}}}})
839
+ return response["response"]["_1"]["handshake"]["_0"]
787
840
 
788
841
  async def _send_receive_pairing_data(self, pairing_data: dict) -> bytes:
789
842
  await self._send_pairing_data(pairing_data)
790
843
  return await self._receive_pairing_data()
791
844
 
792
845
  async def _send_pairing_data(self, pairing_data: dict) -> None:
793
- await self._send_plain_request({'event': {'_0': {'pairingData': {'_0': pairing_data}}}})
846
+ await self._send_plain_request({"event": {"_0": {"pairingData": {"_0": pairing_data}}}})
794
847
 
795
848
  async def _receive_pairing_data(self) -> bytes:
796
849
  response = await self._receive_plain_response()
797
- response = response['event']['_0']
798
- if 'pairingData' in response:
799
- return self._decode_bytes_if_needed(response['pairingData']['_0']['data'])
800
- if 'pairingRejectedWithError' in response:
801
- raise UserDeniedPairingError(response['pairingRejectedWithError']
802
- .get('wrappedError', {})
803
- .get('userInfo', {})
804
- .get('NSLocalizedDescription'))
805
- raise PyMobileDevice3Exception(f'Got an unknown state message: {response}')
850
+ response = response["event"]["_0"]
851
+ if "pairingData" in response:
852
+ return self._decode_bytes_if_needed(response["pairingData"]["_0"]["data"])
853
+ if "pairingRejectedWithError" in response:
854
+ raise UserDeniedPairingError(
855
+ response["pairingRejectedWithError"]
856
+ .get("wrappedError", {})
857
+ .get("userInfo", {})
858
+ .get("NSLocalizedDescription")
859
+ )
860
+ raise PyMobileDevice3Exception(f"Got an unknown state message: {response}")
806
861
 
807
862
  async def _send_receive_plain_request(self, plain_request: dict):
808
863
  await self._send_plain_request(plain_request)
809
864
  return await self._receive_plain_response()
810
865
 
811
866
  async def _send_plain_request(self, plain_request: dict) -> None:
812
- await self.send_request({'message': {'plain': {'_0': plain_request}},
813
- 'originatedBy': 'host',
814
- 'sequenceNumber': XpcUInt64Type(self._sequence_number)})
867
+ await self.send_request({
868
+ "message": {"plain": {"_0": plain_request}},
869
+ "originatedBy": "host",
870
+ "sequenceNumber": XpcUInt64Type(self._sequence_number),
871
+ })
815
872
  self._sequence_number += 1
816
873
 
817
874
  async def _receive_plain_response(self) -> dict:
818
875
  response = await self.receive_response()
819
- return response['message']['plain']['_0']
876
+ return response["message"]["plain"]["_0"]
820
877
 
821
878
  @staticmethod
822
879
  def decode_tlv(tlv_list: list[Container]) -> dict:
@@ -828,7 +885,7 @@ class RemotePairingProtocol(StartTcpTunnel):
828
885
  result[tlv.type] = tlv.data
829
886
  return result
830
887
 
831
- async def __aenter__(self) -> 'CoreDeviceTunnelService':
888
+ async def __aenter__(self) -> "CoreDeviceTunnelService":
832
889
  return self
833
890
 
834
891
  async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
@@ -836,7 +893,7 @@ class RemotePairingProtocol(StartTcpTunnel):
836
893
 
837
894
 
838
895
  class CoreDeviceTunnelService(RemotePairingProtocol, RemoteService):
839
- SERVICE_NAME = 'com.apple.internal.dt.coredevice.untrusted.tunnelservice'
896
+ SERVICE_NAME = "com.apple.internal.dt.coredevice.untrusted.tunnelservice"
840
897
 
841
898
  def __init__(self, rsd: RemoteServiceDiscoveryService):
842
899
  RemoteService.__init__(self, rsd, self.SERVICE_NAME)
@@ -848,12 +905,12 @@ class CoreDeviceTunnelService(RemotePairingProtocol, RemoteService):
848
905
  await RemoteService.connect(self)
849
906
  try:
850
907
  response = await self.service.receive_response()
851
- self.version = response['ServiceVersion']
908
+ self.version = response["ServiceVersion"]
852
909
 
853
910
  # Perform pairing if necessary and start a trusted RemoteXPC connection
854
911
  await RemotePairingProtocol.connect(self, autopair=autopair)
855
912
  self.hostname = self.service.address[0]
856
- except Exception: # noqa: E722
913
+ except Exception:
857
914
  await self.service.close()
858
915
  raise
859
916
 
@@ -863,11 +920,13 @@ class CoreDeviceTunnelService(RemotePairingProtocol, RemoteService):
863
920
 
864
921
  async def receive_response(self) -> dict:
865
922
  response = await self.service.receive_response()
866
- return response['value']
923
+ return response["value"]
867
924
 
868
925
  async def send_request(self, data: dict) -> None:
869
926
  return await self.service.send_request({
870
- 'mangledTypeName': 'RemotePairing.ControlChannelMessageEnvelope', 'value': data})
927
+ "mangledTypeName": "RemotePairing.ControlChannelMessageEnvelope",
928
+ "value": data,
929
+ })
871
930
 
872
931
 
873
932
  class RemotePairingTunnelService(RemotePairingProtocol):
@@ -892,7 +951,7 @@ class RemotePairingTunnelService(RemotePairingProtocol):
892
951
  if not await self._validate_pairing():
893
952
  raise ConnectionAbortedError()
894
953
  self._init_client_server_main_encryption_keys()
895
- except: # noqa: E722
954
+ except Exception:
896
955
  await self.close()
897
956
  raise
898
957
 
@@ -900,21 +959,20 @@ class RemotePairingTunnelService(RemotePairingProtocol):
900
959
  if self._writer is None:
901
960
  return
902
961
  self._writer.close()
903
- try:
962
+ with suppress(ssl.SSLError):
904
963
  await self._writer.wait_closed()
905
- except ssl.SSLError:
906
- pass
907
964
  self._writer = None
908
965
  self._reader = None
909
966
 
910
967
  async def receive_response(self) -> dict:
911
968
  await self._reader.readexactly(len(REPAIRING_PACKET_MAGIC))
912
- size = struct.unpack('>H', await self._reader.readexactly(2))[0]
969
+ size = struct.unpack(">H", await self._reader.readexactly(2))[0]
913
970
  return json.loads(await self._reader.readexactly(size))
914
971
 
915
972
  async def send_request(self, data: dict) -> None:
916
973
  self._writer.write(
917
- RPPairingPacket.build({'body': json.dumps(data, default=self._default_json_encoder).encode()}))
974
+ RPPairingPacket.build({"body": json.dumps(data, default=self._default_json_encoder).encode()})
975
+ )
918
976
  await self._writer.drain()
919
977
 
920
978
  @staticmethod
@@ -928,8 +986,9 @@ class RemotePairingTunnelService(RemotePairingProtocol):
928
986
  return base64.b64decode(data)
929
987
 
930
988
  def __repr__(self) -> str:
931
- return (f'<{self.__class__.__name__} IDENTIFIER:{self.remote_identifier} HOSTNAME:{self.hostname} '
932
- f'PORT:{self.port}>')
989
+ return (
990
+ f"<{self.__class__.__name__} IDENTIFIER:{self.remote_identifier} HOSTNAME:{self.hostname} PORT:{self.port}>"
991
+ )
933
992
 
934
993
 
935
994
  class RemotePairingManualPairingService(RemotePairingTunnelService):
@@ -940,10 +999,10 @@ class RemotePairingManualPairingService(RemotePairingTunnelService):
940
999
 
941
1000
 
942
1001
  class CoreDeviceTunnelProxy(StartTcpTunnel):
943
- SERVICE_NAME = 'com.apple.internal.devicecompute.CoreDeviceProxy'
1002
+ SERVICE_NAME = "com.apple.internal.devicecompute.CoreDeviceProxy"
944
1003
 
945
1004
  @classmethod
946
- async def create(cls, lockdown: LockdownServiceProvider) -> 'CoreDeviceTunnelProxy':
1005
+ async def create(cls, lockdown: LockdownServiceProvider) -> "CoreDeviceTunnelProxy":
947
1006
  return cls(await lockdown.aio_start_lockdown_service(cls.SERVICE_NAME), lockdown.udid)
948
1007
 
949
1008
  def __init__(self, service: ServiceConnection, remote_identifier: str) -> None:
@@ -955,17 +1014,23 @@ class CoreDeviceTunnelProxy(StartTcpTunnel):
955
1014
  return self._remote_identifier
956
1015
 
957
1016
  @asynccontextmanager
958
- async def start_tcp_tunnel(self) -> AsyncGenerator['TunnelResult', None]:
959
- assert self._service is not None, 'service must be connected first'
1017
+ async def start_tcp_tunnel(self) -> AsyncGenerator["TunnelResult", None]:
1018
+ assert self._service is not None, "service must be connected first"
960
1019
  tunnel = RemotePairingTcpTunnel(self._service.reader, self._service.writer)
961
1020
  handshake_response = await tunnel.request_tunnel_establish()
962
- tunnel.start_tunnel(handshake_response['clientParameters']['address'],
963
- handshake_response['clientParameters']['mtu'],
964
- interface_name=f'{DEFAULT_INTERFACE_NAME}-{self.remote_identifier}')
1021
+ tunnel.start_tunnel(
1022
+ handshake_response["clientParameters"]["address"],
1023
+ handshake_response["clientParameters"]["mtu"],
1024
+ interface_name=f"{DEFAULT_INTERFACE_NAME}-{self.remote_identifier}",
1025
+ )
965
1026
  try:
966
1027
  yield TunnelResult(
967
- tunnel.tun.name, handshake_response['serverAddress'], handshake_response['serverRSDPort'],
968
- TunnelProtocol.TCP, tunnel)
1028
+ tunnel.tun.name,
1029
+ handshake_response["serverAddress"],
1030
+ handshake_response["serverRSDPort"],
1031
+ TunnelProtocol.TCP,
1032
+ tunnel,
1033
+ )
969
1034
  finally:
970
1035
  await tunnel.stop_tunnel()
971
1036
 
@@ -975,7 +1040,8 @@ class CoreDeviceTunnelProxy(StartTcpTunnel):
975
1040
 
976
1041
 
977
1042
  async def create_core_device_tunnel_service_using_rsd(
978
- rsd: RemoteServiceDiscoveryService, autopair: bool = True) -> CoreDeviceTunnelService:
1043
+ rsd: RemoteServiceDiscoveryService, autopair: bool = True
1044
+ ) -> CoreDeviceTunnelService:
979
1045
  service = CoreDeviceTunnelService(rsd)
980
1046
  try:
981
1047
  await service.connect(autopair=autopair)
@@ -984,21 +1050,23 @@ async def create_core_device_tunnel_service_using_rsd(
984
1050
  await service.close()
985
1051
  service = CoreDeviceTunnelService(rsd)
986
1052
  await service.connect(autopair=autopair)
987
- except Exception: # noqa: E722
1053
+ except Exception:
988
1054
  await service.close()
989
1055
  raise
990
1056
  return service
991
1057
 
992
1058
 
993
1059
  async def create_core_device_tunnel_service_using_remotepairing(
994
- remote_identifier: str, hostname: str, port: int, autopair: bool = True) -> RemotePairingTunnelService:
1060
+ remote_identifier: str, hostname: str, port: int, autopair: bool = True
1061
+ ) -> RemotePairingTunnelService:
995
1062
  service = RemotePairingTunnelService(remote_identifier, hostname, port)
996
1063
  await service.connect(autopair=autopair)
997
1064
  return service
998
1065
 
999
1066
 
1000
1067
  async def create_core_device_service_using_remotepairing_manual_pairing(
1001
- remote_identifier: str, hostname: str, port: int, autopair: bool = True) -> RemotePairingTunnelService:
1068
+ remote_identifier: str, hostname: str, port: int, autopair: bool = True
1069
+ ) -> RemotePairingTunnelService:
1002
1070
  service = RemotePairingManualPairingService(remote_identifier, hostname, port)
1003
1071
  await service.connect(autopair=autopair)
1004
1072
  return service
@@ -1006,14 +1074,16 @@ async def create_core_device_service_using_remotepairing_manual_pairing(
1006
1074
 
1007
1075
  @asynccontextmanager
1008
1076
  async def start_tunnel_over_remotepairing(
1009
- remote_pairing: RemotePairingTunnelService, secrets: Optional[TextIO] = None,
1010
- max_idle_timeout: float = RemotePairingQuicTunnel.MAX_IDLE_TIMEOUT,
1011
- protocol: TunnelProtocol = TunnelProtocol.QUIC) \
1012
- -> AsyncGenerator[TunnelResult, None]:
1077
+ remote_pairing: RemotePairingTunnelService,
1078
+ secrets: Optional[TextIO] = None,
1079
+ max_idle_timeout: float = RemotePairingQuicTunnel.MAX_IDLE_TIMEOUT,
1080
+ protocol: TunnelProtocol = TunnelProtocol.QUIC,
1081
+ ) -> AsyncGenerator[TunnelResult, None]:
1013
1082
  async with remote_pairing:
1014
1083
  if protocol == TunnelProtocol.QUIC:
1015
1084
  async with remote_pairing.start_quic_tunnel(
1016
- secrets_log_file=secrets, max_idle_timeout=max_idle_timeout) as tunnel_result:
1085
+ secrets_log_file=secrets, max_idle_timeout=max_idle_timeout
1086
+ ) as tunnel_result:
1017
1087
  yield tunnel_result
1018
1088
  elif protocol == TunnelProtocol.TCP:
1019
1089
  async with remote_pairing.start_tcp_tunnel() as tunnel_result:
@@ -1022,15 +1092,17 @@ async def start_tunnel_over_remotepairing(
1022
1092
 
1023
1093
  @asynccontextmanager
1024
1094
  async def start_tunnel_over_core_device(
1025
- service_provider: CoreDeviceTunnelService, secrets: Optional[TextIO] = None,
1026
- max_idle_timeout: float = RemotePairingQuicTunnel.MAX_IDLE_TIMEOUT,
1027
- protocol: TunnelProtocol = TunnelProtocol.QUIC) \
1028
- -> AsyncGenerator[TunnelResult, None]:
1095
+ service_provider: CoreDeviceTunnelService,
1096
+ secrets: Optional[TextIO] = None,
1097
+ max_idle_timeout: float = RemotePairingQuicTunnel.MAX_IDLE_TIMEOUT,
1098
+ protocol: TunnelProtocol = TunnelProtocol.QUIC,
1099
+ ) -> AsyncGenerator[TunnelResult, None]:
1029
1100
  stop_remoted_if_required()
1030
1101
  async with service_provider:
1031
1102
  if protocol == TunnelProtocol.QUIC:
1032
1103
  async with service_provider.start_quic_tunnel(
1033
- secrets_log_file=secrets, max_idle_timeout=max_idle_timeout) as tunnel_result:
1104
+ secrets_log_file=secrets, max_idle_timeout=max_idle_timeout
1105
+ ) as tunnel_result:
1034
1106
  resume_remoted_if_required()
1035
1107
  yield tunnel_result
1036
1108
  elif protocol == TunnelProtocol.TCP:
@@ -1041,48 +1113,53 @@ async def start_tunnel_over_core_device(
1041
1113
 
1042
1114
  @asynccontextmanager
1043
1115
  async def start_tunnel(
1044
- protocol_handler: RemotePairingProtocol, secrets: Optional[TextIO] = None,
1045
- max_idle_timeout: float = RemotePairingQuicTunnel.MAX_IDLE_TIMEOUT,
1046
- protocol: TunnelProtocol = TunnelProtocol.QUIC) -> AsyncGenerator[TunnelResult, None]:
1116
+ protocol_handler: RemotePairingProtocol,
1117
+ secrets: Optional[TextIO] = None,
1118
+ max_idle_timeout: float = RemotePairingQuicTunnel.MAX_IDLE_TIMEOUT,
1119
+ protocol: TunnelProtocol = TunnelProtocol.QUIC,
1120
+ ) -> AsyncGenerator[TunnelResult, None]:
1047
1121
  if isinstance(protocol_handler, CoreDeviceTunnelService):
1048
1122
  async with start_tunnel_over_core_device(
1049
- protocol_handler, secrets=secrets, max_idle_timeout=max_idle_timeout, protocol=protocol) as service:
1123
+ protocol_handler, secrets=secrets, max_idle_timeout=max_idle_timeout, protocol=protocol
1124
+ ) as service:
1050
1125
  yield service
1051
1126
  elif isinstance(protocol_handler, RemotePairingTunnelService):
1052
1127
  async with start_tunnel_over_remotepairing(
1053
- protocol_handler, secrets=secrets, max_idle_timeout=max_idle_timeout, protocol=protocol) as service:
1128
+ protocol_handler, secrets=secrets, max_idle_timeout=max_idle_timeout, protocol=protocol
1129
+ ) as service:
1054
1130
  yield service
1055
1131
  elif isinstance(protocol_handler, CoreDeviceTunnelProxy):
1056
1132
  if protocol != TunnelProtocol.TCP:
1057
- raise ValueError('CoreDeviceTunnelProxy protocol can only be TCP')
1133
+ raise ValueError("CoreDeviceTunnelProxy protocol can only be TCP")
1058
1134
  async with protocol_handler.start_tcp_tunnel() as service:
1059
1135
  yield service
1060
1136
  else:
1061
- raise Exception(f'Bad value for protocol_handler: {protocol_handler}')
1137
+ raise TypeError(f"Bad value for protocol_handler: {protocol_handler}")
1062
1138
 
1063
1139
 
1064
1140
  async def get_core_device_tunnel_services(
1065
- bonjour_timeout: float = DEFAULT_BONJOUR_TIMEOUT,
1066
- udid: Optional[str] = None) -> list[CoreDeviceTunnelService]:
1141
+ bonjour_timeout: float = DEFAULT_BONJOUR_TIMEOUT, udid: Optional[str] = None
1142
+ ) -> list[CoreDeviceTunnelService]:
1067
1143
  result = []
1068
1144
  for rsd in await get_rsds(bonjour_timeout=bonjour_timeout, udid=udid):
1069
- if udid is None and ((Version(rsd.product_version) < Version('17.0'))
1070
- and not rsd.product_type.startswith('RealityDevice')):
1071
- logger.debug(f'Skipping {rsd.udid}:, iOS {rsd.product_version} < 17.0')
1145
+ if udid is None and (
1146
+ (Version(rsd.product_version) < Version("17.0")) and not rsd.product_type.startswith("RealityDevice")
1147
+ ):
1148
+ logger.debug(f"Skipping {rsd.udid}:, iOS {rsd.product_version} < 17.0")
1072
1149
  await rsd.close()
1073
1150
  continue
1074
1151
  try:
1075
1152
  result.append(await create_core_device_tunnel_service_using_rsd(rsd))
1076
- except Exception as e:
1077
- logger.error(f'Failed to start service: {rsd}: {e}')
1153
+ except Exception:
1154
+ logger.exception(f"Failed to start service: {rsd}")
1078
1155
  await rsd.close()
1079
1156
  raise
1080
1157
  return result
1081
1158
 
1082
1159
 
1083
1160
  async def get_remote_pairing_tunnel_services(
1084
- bonjour_timeout: float = DEFAULT_BONJOUR_TIMEOUT,
1085
- udid: Optional[str] = None) -> list[RemotePairingTunnelService]:
1161
+ bonjour_timeout: float = DEFAULT_BONJOUR_TIMEOUT, udid: Optional[str] = None
1162
+ ) -> list[RemotePairingTunnelService]:
1086
1163
  result = []
1087
1164
  for answer in await browse_remotepairing(timeout=bonjour_timeout):
1088
1165
  for address in answer.addresses:
@@ -1092,7 +1169,8 @@ async def get_remote_pairing_tunnel_services(
1092
1169
  conn = None
1093
1170
  try:
1094
1171
  conn = await create_core_device_tunnel_service_using_remotepairing(
1095
- identifier, address.full_ip, answer.port)
1172
+ identifier, address.full_ip, answer.port
1173
+ )
1096
1174
  result.append(conn)
1097
1175
  break
1098
1176
  except ConnectionAbortedError: