pymobiledevice3 4.27.4__py3-none-any.whl → 5.1.2__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.
Files changed (143) hide show
  1. misc/plist_sniffer.py +15 -15
  2. misc/remotexpc_sniffer.py +29 -28
  3. pymobiledevice3/__main__.py +123 -98
  4. pymobiledevice3/_version.py +2 -2
  5. pymobiledevice3/bonjour.py +351 -117
  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 +27 -20
  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 +601 -519
  18. pymobiledevice3/cli/diagnostics.py +38 -33
  19. pymobiledevice3/cli/lockdown.py +82 -72
  20. pymobiledevice3/cli/mounter.py +84 -67
  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 +188 -111
  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 +156 -78
  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 +400 -251
  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 +383 -297
  53. pymobiledevice3/remote/utils.py +14 -13
  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 +587 -0
  65. pymobiledevice3/restore/recovery.py +125 -135
  66. pymobiledevice3/restore/restore.py +535 -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 +352 -292
  73. pymobiledevice3/services/amfi.py +21 -18
  74. pymobiledevice3/services/companion.py +23 -19
  75. pymobiledevice3/services/crash_reports.py +61 -47
  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 +466 -384
  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 +331 -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 +128 -74
  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 +142 -116
  132. pymobiledevice3/tcp_forwarder.py +35 -22
  133. pymobiledevice3/tunneld/api.py +20 -15
  134. pymobiledevice3/tunneld/server.py +310 -193
  135. pymobiledevice3/usbmux.py +197 -148
  136. pymobiledevice3/utils.py +14 -11
  137. {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/METADATA +1 -2
  138. pymobiledevice3-5.1.2.dist-info/RECORD +173 -0
  139. pymobiledevice3-4.27.4.dist-info/RECORD +0 -172
  140. {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/WHEEL +0 -0
  141. {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/entry_points.txt +0 -0
  142. {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/licenses/LICENSE +0 -0
  143. {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/top_level.txt +0 -0
@@ -6,7 +6,6 @@ import plistlib
6
6
  import struct
7
7
  import tempfile
8
8
  import time
9
- import traceback
10
9
  import typing
11
10
  import zipfile
12
11
  from concurrent.futures import ThreadPoolExecutor
@@ -17,34 +16,40 @@ from tqdm import tqdm, trange
17
16
 
18
17
  from pymobiledevice3.exceptions import ConnectionFailedError, NoDeviceConnectedError, PyMobileDevice3Exception
19
18
  from pymobiledevice3.restore.asr import DEFAULT_ASR_SYNC_PORT, ASRClient
20
- from pymobiledevice3.restore.base_restore import RESTORE_VARIANT_ERASE_INSTALL, RESTORE_VARIANT_MACOS_RECOVERY_OS, \
21
- RESTORE_VARIANT_UPGRADE_INSTALL, BaseRestore
19
+ from pymobiledevice3.restore.base_restore import (
20
+ RESTORE_VARIANT_ERASE_INSTALL,
21
+ RESTORE_VARIANT_MACOS_RECOVERY_OS,
22
+ RESTORE_VARIANT_UPGRADE_INSTALL,
23
+ BaseRestore,
24
+ )
22
25
  from pymobiledevice3.restore.consts import PROGRESS_BAR_OPERATIONS, lpol_file
23
26
  from pymobiledevice3.restore.device import Device
24
27
  from pymobiledevice3.restore.fdr import FDRClient, fdr_type, start_fdr_thread
25
28
  from pymobiledevice3.restore.ftab import Ftab
29
+ from pymobiledevice3.restore.mbn import mbn_mav25_stitch, mbn_stitch
26
30
  from pymobiledevice3.restore.recovery import Behavior, Recovery
27
31
  from pymobiledevice3.restore.restore_options import RestoreOptions
28
32
  from pymobiledevice3.restore.restored_client import RestoredClient
29
33
  from pymobiledevice3.restore.tss import TSSRequest, TSSResponse
30
34
  from pymobiledevice3.service_connection import ServiceConnection
31
- from pymobiledevice3.utils import plist_access_path
35
+ from pymobiledevice3.utils import asyncio_print_traceback, plist_access_path
32
36
 
33
37
  known_errors = {
34
- 0xFFFFFFFFFFFFFFFF: 'verification error',
35
- 6: 'disk failure',
36
- 14: 'fail',
37
- 27: 'failed to mount filesystems',
38
- 50: 'failed to load SEP firmware',
39
- 51: 'failed to load SEP firmware',
40
- 53: 'failed to recover FDR data',
41
- 1015: 'X-Gold Baseband Update Failed. Defective Unit?',
38
+ 0xFFFFFFFFFFFFFFFF: "verification error",
39
+ 6: "disk failure",
40
+ 14: "fail",
41
+ 27: "failed to mount filesystems",
42
+ 50: "failed to load SEP firmware",
43
+ 51: "failed to load SEP firmware",
44
+ 53: "failed to recover FDR data",
45
+ 1015: "X-Gold Baseband Update Failed. Defective Unit?",
42
46
  }
43
47
 
44
48
 
45
49
  class Restore(BaseRestore):
46
- def __init__(self, ipsw: zipfile.ZipFile, device: Device, tss=None, behavior: Behavior = Behavior.Update,
47
- ignore_fdr=False):
50
+ def __init__(
51
+ self, ipsw: zipfile.ZipFile, device: Device, tss=None, behavior: Behavior = Behavior.Update, ignore_fdr=False
52
+ ):
48
53
  super().__init__(ipsw, device, tss, behavior)
49
54
  self.recovery = Recovery(ipsw, device, tss=tss, behavior=behavior)
50
55
  self.bbtss: Optional[TSSResponse] = None
@@ -73,85 +78,72 @@ class Restore(BaseRestore):
73
78
  # data request messages are sent by restored whenever it requires
74
79
  # files sent to the server by the client. these data requests include
75
80
  # SystemImageData, RootTicket, KernelCache, NORData and BasebandData requests
76
- 'DataRequestMsg': self.handle_data_request_msg,
77
- 'AsyncDataRequestMsg': self.handle_async_data_request_msg,
78
-
81
+ "DataRequestMsg": self.handle_data_request_msg,
82
+ "AsyncDataRequestMsg": self.handle_async_data_request_msg,
79
83
  # restore logs are available if a previous restore failed
80
- 'PreviousRestoreLogMsg': self.handle_previous_restore_log_msg,
81
-
84
+ "PreviousRestoreLogMsg": self.handle_previous_restore_log_msg,
82
85
  # progress notification messages sent by the restored inform the client
83
86
  # of it's current operation and sometimes percent of progress is complete
84
- 'ProgressMsg': self.handle_progress_msg,
85
-
87
+ "ProgressMsg": self.handle_progress_msg,
86
88
  # status messages usually indicate the current state of the restored
87
89
  # process or often to signal an error has been encountered
88
- 'StatusMsg': self.handle_status_msg,
89
-
90
+ "StatusMsg": self.handle_status_msg,
90
91
  # checkpoint notifications
91
- 'CheckpointMsg': self.handle_checkpoint_msg,
92
-
92
+ "CheckpointMsg": self.handle_checkpoint_msg,
93
93
  # baseband update message
94
- 'BBUpdateStatusMsg': self.handle_bb_update_status_msg,
95
-
94
+ "BBUpdateStatusMsg": self.handle_bb_update_status_msg,
96
95
  # baseband updater output data request
97
- 'BasebandUpdaterOutputData': self.handle_baseband_updater_output_data,
98
-
96
+ "BasebandUpdaterOutputData": self.handle_baseband_updater_output_data,
99
97
  # report backtrace from restored crash
100
- 'RestoredCrash': self.handle_restored_crash,
101
-
98
+ "RestoredCrash": self.handle_restored_crash,
102
99
  # report new async contexts
103
- 'AsyncWait': self.handle_async_wait,
104
-
100
+ "AsyncWait": self.handle_async_wait,
105
101
  # handle attestation
106
- 'RestoreAttestation': self.handle_restore_attestation,
102
+ "RestoreAttestation": self.handle_restore_attestation,
107
103
  }
108
104
 
109
105
  self._data_request_handlers = {
110
106
  # this request is sent when restored is ready to receive the filesystem
111
- 'SystemImageData': self.send_filesystem,
112
-
113
- 'BuildIdentityDict': self.send_buildidentity,
114
- 'PersonalizedBootObjectV3': self.send_personalized_boot_object_v3,
115
- 'SourceBootObjectV4': self.send_source_boot_object_v4,
116
- 'RecoveryOSLocalPolicy': self.send_restore_local_policy,
117
-
107
+ "SystemImageData": self.send_filesystem,
108
+ "BuildIdentityDict": self.send_buildidentity,
109
+ "PersonalizedBootObjectV3": self.send_personalized_boot_object_v3,
110
+ "SourceBootObjectV4": self.send_source_boot_object_v4,
111
+ "RecoveryOSLocalPolicy": self.send_restore_local_policy,
118
112
  # this request is sent when restored is ready to receive the filesystem
119
- 'RecoveryOSASRImage': self.send_filesystem,
120
-
113
+ "RecoveryOSASRImage": self.send_filesystem,
121
114
  # Send RecoveryOS RTD
122
- 'RecoveryOSRootTicketData': self.send_recovery_os_root_ticket,
123
-
115
+ "RecoveryOSRootTicketData": self.send_recovery_os_root_ticket,
124
116
  # send RootTicket (== APTicket from the TSS request)
125
- 'RootTicket': self.send_root_ticket,
126
-
127
- 'NORData': self.send_nor,
128
- 'BasebandData': self.send_baseband_data,
129
- 'FDRTrustData': self.send_fdr_trust_data,
130
- 'FirmwareUpdaterData': self.send_firmware_updater_data,
131
-
117
+ "RootTicket": self.send_root_ticket,
118
+ "NORData": self.send_nor,
119
+ "BasebandData": self.send_baseband_data,
120
+ "FDRTrustData": self.send_fdr_trust_data,
121
+ "FirmwareUpdaterData": self.send_firmware_updater_data,
132
122
  # TODO: verify
133
- 'FirmwareUpdaterPreflight': self.send_firmware_updater_preflight,
134
-
123
+ "FirmwareUpdaterPreflight": self.send_firmware_updater_preflight,
135
124
  # Added on iOS 18.0 beta1
136
- 'URLAsset': self.send_url_asset,
137
- 'StreamedImageDecryptionKey': self.send_streamed_image_decryption_key,
125
+ "URLAsset": self.send_url_asset,
126
+ "StreamedImageDecryptionKey": self.send_streamed_image_decryption_key,
138
127
  }
139
128
 
140
129
  self._data_request_components = {
141
- 'KernelCache': self.send_component,
142
- 'DeviceTree': self.send_component,
130
+ "KernelCache": self.send_component,
131
+ "DeviceTree": self.send_component,
143
132
  }
144
133
 
145
134
  def handle_async_data_request_msg(self, message: dict) -> typing.Coroutine:
146
- self._tasks.append(asyncio.create_task(self.handle_data_request_msg(message),
147
- name=f'AsyncDataRequestMsg-{message["DataType"]}'))
135
+ self._tasks.append(
136
+ asyncio.create_task(
137
+ self.handle_data_request_msg(message), name=f"AsyncDataRequestMsg-{message['DataType']}"
138
+ )
139
+ )
148
140
  return asyncio.sleep(0)
149
141
 
150
142
  async def send_filesystem(self, message: dict) -> None:
151
- self.logger.info('about to send filesystem...')
143
+ self.logger.info("about to send filesystem...")
152
144
 
153
- asr_port = message.get('DataPort', DEFAULT_ASR_SYNC_PORT)
154
- self.logger.info(f'connecting to ASR on port {asr_port}')
145
+ asr_port = message.get("DataPort", DEFAULT_ASR_SYNC_PORT)
146
+ self.logger.info(f"connecting to ASR on port {asr_port}")
155
147
  asr = ASRClient(self._restored.udid)
156
148
  while True:
157
149
  try:
@@ -160,46 +152,46 @@ class Restore(BaseRestore):
160
152
  except ConnectionFailedError:
161
153
  pass
162
154
 
163
- self.logger.info('connected to ASR')
155
+ self.logger.info("connected to ASR")
164
156
 
165
157
  # this step sends requested chunks of data from various offsets to asr, so
166
158
  # it can validate the filesystem before installing it
167
- self.logger.info('validating the filesystem')
159
+ self.logger.info("validating the filesystem")
168
160
 
169
- with self.ipsw.open_path(self.build_identity.get_component_path('OS')) as filesystem:
161
+ with self.ipsw.open_path(self.build_identity.get_component_path("OS")) as filesystem:
170
162
  await asr.perform_validation(filesystem)
171
- self.logger.info('filesystem validated')
163
+ self.logger.info("filesystem validated")
172
164
 
173
165
  # once the target filesystem has been validated, ASR then requests the
174
166
  # entire filesystem to be sent.
175
- self.logger.info('sending filesystem now...')
167
+ self.logger.info("sending filesystem now...")
176
168
  await asr.send_payload(filesystem)
177
169
 
178
170
  await asr.close()
179
171
 
180
172
  def get_build_identity_from_request(self, msg):
181
- return self.get_build_identity(msg['Arguments'].get('IsRecoveryOS', False))
173
+ return self.get_build_identity(msg["Arguments"].get("IsRecoveryOS", False))
182
174
 
183
175
  async def send_buildidentity(self, message: dict) -> None:
184
- self.logger.info('About to send BuildIdentity Dict...')
176
+ self.logger.info("About to send BuildIdentity Dict...")
185
177
  service = await self._get_service_for_data_request(message)
186
- req = {'BuildIdentityDict': dict(self.get_build_identity_from_request(message))}
187
- arguments = message['Arguments']
188
- variant = arguments.get('Variant', 'Erase')
189
- req['Variant'] = variant
190
- self.logger.info('Sending BuildIdentityDict now...')
178
+ req = {"BuildIdentityDict": dict(self.get_build_identity_from_request(message))}
179
+ arguments = message["Arguments"]
180
+ variant = arguments.get("Variant", "Erase")
181
+ req["Variant"] = variant
182
+ self.logger.info("Sending BuildIdentityDict now...")
191
183
  await service.aio_send_plist(req)
192
184
 
193
185
  async def extract_global_manifest(self) -> dict:
194
- build_info = self.build_identity.get('Info')
186
+ build_info = self.build_identity.get("Info")
195
187
  if build_info is None:
196
188
  raise PyMobileDevice3Exception('build identity does not contain an "Info" element')
197
189
 
198
- device_class = build_info.get('DeviceClass')
190
+ device_class = build_info.get("DeviceClass")
199
191
  if device_class is None:
200
192
  raise PyMobileDevice3Exception('build identity does not contain an "DeviceClass" element')
201
193
 
202
- macos_variant = build_info.get('MacOSVariant')
194
+ macos_variant = build_info.get("MacOSVariant")
203
195
  if macos_variant is None:
204
196
  raise PyMobileDevice3Exception('build identity does not contain an "MacOSVariant" element')
205
197
 
@@ -207,67 +199,68 @@ class Restore(BaseRestore):
207
199
  return self.ipsw.get_global_manifest(macos_variant, device_class)
208
200
 
209
201
  async def send_personalized_boot_object_v3(self, message: dict) -> None:
210
- self.logger.debug('send_personalized_boot_object_v3')
202
+ self.logger.debug("send_personalized_boot_object_v3")
211
203
  service = await self._get_service_for_data_request(message)
212
- image_name = message['Arguments']['ImageName']
204
+ image_name = message["Arguments"]["ImageName"]
213
205
  component_name = image_name
214
- self.logger.info(f'About to send {component_name}...')
206
+ self.logger.info(f"About to send {component_name}...")
215
207
 
216
- if image_name == '__GlobalManifest__':
208
+ if image_name == "__GlobalManifest__":
217
209
  data = self.extract_global_manifest()
218
- elif image_name == '__RestoreVersion__':
210
+ elif image_name == "__RestoreVersion__":
219
211
  data = self.ipsw.restore_version
220
- elif image_name == '__SystemVersion__':
212
+ elif image_name == "__SystemVersion__":
221
213
  data = self.ipsw.system_version
222
214
  else:
223
215
  data = self.get_personalized_data(component_name, tss=self.recovery.tss)
224
216
 
225
- self.logger.info(f'Sending {component_name} now...')
217
+ self.logger.info(f"Sending {component_name} now...")
226
218
  chunk_size = 8192
227
219
  for i in trange(0, len(data), chunk_size, dynamic_ncols=True):
228
- await service.aio_send_plist({'FileData': data[i:i + chunk_size]})
220
+ await service.aio_send_plist({"FileData": data[i : i + chunk_size]})
229
221
 
230
222
  # Send FileDataDone
231
- await service.aio_send_plist({'FileDataDone': True})
223
+ await service.aio_send_plist({"FileDataDone": True})
232
224
 
233
- self.logger.info(f'Done sending {component_name}')
225
+ self.logger.info(f"Done sending {component_name}")
234
226
 
235
227
  async def send_source_boot_object_v4(self, message: dict) -> None:
236
- self.logger.debug('send_source_boot_object_v4')
228
+ self.logger.debug("send_source_boot_object_v4")
237
229
  service = await self._get_service_for_data_request(message)
238
- image_name = message['Arguments']['ImageName']
230
+ image_name = message["Arguments"]["ImageName"]
239
231
  component_name = image_name
240
- self.logger.info(f'About to send {component_name}...')
232
+ self.logger.info(f"About to send {component_name}...")
241
233
 
242
- if image_name == '__GlobalManifest__':
234
+ if image_name == "__GlobalManifest__":
243
235
  data = self.extract_global_manifest()
244
- elif image_name == '__RestoreVersion__':
236
+ elif image_name == "__RestoreVersion__":
245
237
  data = self.ipsw.restore_version
246
- elif image_name == '__SystemVersion__':
238
+ elif image_name == "__SystemVersion__":
247
239
  data = self.ipsw.system_version
248
240
  else:
249
- data = (self.get_build_identity_from_request(message)
250
- .get_component(component_name, tss=self.recovery.tss).data)
241
+ data = (
242
+ self.get_build_identity_from_request(message).get_component(component_name, tss=self.recovery.tss).data
243
+ )
251
244
 
252
- self.logger.info(f'Sending {component_name} now...')
245
+ self.logger.info(f"Sending {component_name} now...")
253
246
  chunk_size = 8192
254
247
  for i in trange(0, len(data), chunk_size, dynamic_ncols=True):
255
- chunk = data[i:i + chunk_size]
256
- await service.aio_send_plist({'FileData': chunk})
257
- if i == 0 and chunk.startswith(b'AEA1'):
258
- self.logger.debug('First chunk in a AEA')
248
+ chunk = data[i : i + chunk_size]
249
+ await service.aio_send_plist({"FileData": chunk})
250
+ if i == 0 and chunk.startswith(b"AEA1"):
251
+ self.logger.debug("First chunk in a AEA")
259
252
  try:
260
253
  message = await asyncio.wait_for(service.aio_recv_plist(), timeout=3)
261
- if message['MsgType'] != 'URLAsset':
254
+ if message["MsgType"] != "URLAsset":
262
255
  raise asyncio.exceptions.TimeoutError()
263
256
  await self.send_url_asset(message)
264
257
  except asyncio.exceptions.TimeoutError:
265
- self.logger.debug('No URLAsset was requested. Assuming it is not necessary')
258
+ self.logger.debug("No URLAsset was requested. Assuming it is not necessary")
266
259
 
267
260
  # Send FileDataDone
268
- await service.aio_send_plist({'FileDataDone': True})
261
+ await service.aio_send_plist({"FileDataDone": True})
269
262
 
270
- self.logger.info(f'Done sending {component_name}')
263
+ self.logger.info(f"Done sending {component_name}")
271
264
 
272
265
  async def get_recovery_os_local_policy_tss_response(self, args, build_identity=None):
273
266
  if build_identity is None:
@@ -275,29 +268,29 @@ class Restore(BaseRestore):
275
268
 
276
269
  # populate parameters
277
270
  parameters = {
278
- 'ApECID': self.device.ecid,
279
- 'Ap,LocalBoot': True,
280
- 'ApProductionMode': True,
281
- 'ApSecurityMode': True,
282
- 'ApSupportsImg4': True,
271
+ "ApECID": self.device.ecid,
272
+ "Ap,LocalBoot": True,
273
+ "ApProductionMode": True,
274
+ "ApSecurityMode": True,
275
+ "ApSupportsImg4": True,
283
276
  }
284
277
 
285
278
  build_identity.populate_tss_request_parameters(parameters)
286
279
 
287
280
  # Add Ap,LocalPolicy
288
281
  lpol = {
289
- 'Digest': hashlib.sha384(lpol_file).digest(),
290
- 'Trusted': True,
282
+ "Digest": hashlib.sha384(lpol_file).digest(),
283
+ "Trusted": True,
291
284
  }
292
285
 
293
- parameters['Ap,LocalPolicy'] = lpol
286
+ parameters["Ap,LocalPolicy"] = lpol
294
287
 
295
- parameters['Ap,NextStageIM4MHash'] = args['Ap,NextStageIM4MHash']
296
- parameters['Ap,RecoveryOSPolicyNonceHash'] = args['Ap,RecoveryOSPolicyNonceHash']
288
+ parameters["Ap,NextStageIM4MHash"] = args["Ap,NextStageIM4MHash"]
289
+ parameters["Ap,RecoveryOSPolicyNonceHash"] = args["Ap,RecoveryOSPolicyNonceHash"]
297
290
 
298
- vol_uuid = args['Ap,VolumeUUID']
299
- vol_uuid = binascii.unhexlify(vol_uuid.replace('-', ''))
300
- parameters['Ap,VolumeUUID'] = vol_uuid
291
+ vol_uuid = args["Ap,VolumeUUID"]
292
+ vol_uuid = binascii.unhexlify(vol_uuid.replace("-", ""))
293
+ parameters["Ap,VolumeUUID"] = vol_uuid
301
294
 
302
295
  # create basic request
303
296
  request = TSSRequest()
@@ -305,7 +298,7 @@ class Restore(BaseRestore):
305
298
  # add common tags from manifest
306
299
  request.add_local_policy_tags(parameters)
307
300
 
308
- self.logger.info('Requesting SHSH blobs...')
301
+ self.logger.info("Requesting SHSH blobs...")
309
302
  return await request.send_receive()
310
303
 
311
304
  def get_build_identity(self, is_recovery_os: bool):
@@ -319,24 +312,25 @@ class Restore(BaseRestore):
319
312
  return self.ipsw.build_manifest.get_build_identity(self.device.hardware_model, variant=variant)
320
313
 
321
314
  async def send_restore_local_policy(self, message: dict) -> None:
322
- component = 'Ap,LocalPolicy'
315
+ component = "Ap,LocalPolicy"
323
316
  service = await self._get_service_for_data_request(message)
324
317
 
325
318
  # The Update mode does not have a specific build identity for the recovery os.
326
319
  build_identity = self.get_build_identity(self.build_identity.restore_behavior == Behavior.Erase.value)
327
- tss_localpolicy = await self.get_recovery_os_local_policy_tss_response(message['Arguments'],
328
- build_identity=build_identity)
320
+ tss_localpolicy = await self.get_recovery_os_local_policy_tss_response(
321
+ message["Arguments"], build_identity=build_identity
322
+ )
329
323
 
330
324
  await service.aio_send_plist({
331
- 'Ap,LocalPolicy':
332
- self.get_personalized_data(component, data=lpol_file, tss=tss_localpolicy)})
325
+ "Ap,LocalPolicy": self.get_personalized_data(component, data=lpol_file, tss=tss_localpolicy)
326
+ })
333
327
 
334
328
  async def send_recovery_os_root_ticket(self, message: dict) -> None:
335
- self.logger.info('About to send RecoveryOSRootTicket...')
329
+ self.logger.info("About to send RecoveryOSRootTicket...")
336
330
  service = await self._get_service_for_data_request(message)
337
331
 
338
332
  if self.recovery.tss_recoveryos_root_ticket is None:
339
- raise PyMobileDevice3Exception('Cannot send RootTicket without TSS')
333
+ raise PyMobileDevice3Exception("Cannot send RootTicket without TSS")
340
334
 
341
335
  if self.device.is_image4_supported:
342
336
  data = self.recovery.tss_recoveryos_root_ticket.ap_img4_ticket
@@ -345,73 +339,70 @@ class Restore(BaseRestore):
345
339
 
346
340
  req = {}
347
341
  if data:
348
- req['RootTicketData'] = data
342
+ req["RootTicketData"] = data
349
343
  else:
350
- self.logger.warning('not sending RootTicketData (no data present)')
344
+ self.logger.warning("not sending RootTicketData (no data present)")
351
345
 
352
- self.logger.info('Sending RecoveryOSRootTicket now...')
346
+ self.logger.info("Sending RecoveryOSRootTicket now...")
353
347
  await service.aio_send_plist(req)
354
348
 
355
349
  async def send_root_ticket(self, message: dict) -> None:
356
- self.logger.info('About to send RootTicket...')
350
+ self.logger.info("About to send RootTicket...")
357
351
  service = await self._get_service_for_data_request(message)
358
352
 
359
353
  if self.recovery.tss is None:
360
- raise PyMobileDevice3Exception('Cannot send RootTicket without TSS')
354
+ raise PyMobileDevice3Exception("Cannot send RootTicket without TSS")
361
355
 
362
- self.logger.info('Sending RootTicket now...')
363
- await service.aio_send_plist({'RootTicketData': self.recovery.tss.ap_img4_ticket})
356
+ self.logger.info("Sending RootTicket now...")
357
+ await service.aio_send_plist({"RootTicketData": self.recovery.tss.ap_img4_ticket})
364
358
 
365
359
  async def send_nor(self, message: dict):
366
- self.logger.info('About to send NORData...')
360
+ self.logger.info("About to send NORData...")
367
361
  service = await self._get_service_for_data_request(message)
368
362
 
369
363
  flash_version_1 = False
370
- llb_path = self.build_identity.get_component('LLB', tss=self.recovery.tss).path
371
- llb_filename_offset = llb_path.find('LLB')
364
+ llb_path = self.build_identity.get_component("LLB", tss=self.recovery.tss).path
365
+ llb_filename_offset = llb_path.find("LLB")
372
366
 
373
- arguments = message.get('Arguments')
367
+ arguments = message.get("Arguments")
374
368
  if arguments:
375
- flash_version_1 = arguments.get('FlashVersion1', False)
369
+ flash_version_1 = arguments.get("FlashVersion1", False)
376
370
 
377
371
  if llb_filename_offset == -1:
378
- raise PyMobileDevice3Exception('Unable to extract firmware path from LLB filename')
372
+ raise PyMobileDevice3Exception("Unable to extract firmware path from LLB filename")
379
373
 
380
- firmware_path = llb_path[:llb_filename_offset - 1]
381
- self.logger.info(f'Found firmware path: {firmware_path}')
374
+ firmware_path = llb_path[: llb_filename_offset - 1]
375
+ self.logger.info(f"Found firmware path: {firmware_path}")
382
376
 
383
- firmware_files = dict()
377
+ firmware_files = {}
384
378
  try:
385
379
  firmware = self.ipsw.get_firmware(firmware_path)
386
380
  firmware_files = firmware.get_files()
387
381
  except KeyError:
388
- self.logger.info('Getting firmware manifest from build identity')
389
- build_id_manifest = self.build_identity['Manifest']
382
+ self.logger.info("Getting firmware manifest from build identity")
383
+ build_id_manifest = self.build_identity["Manifest"]
390
384
  for component, manifest_entry in build_id_manifest.items():
391
385
  if isinstance(manifest_entry, dict):
392
- is_fw = plist_access_path(manifest_entry, ('Info', 'IsFirmwarePayload'), bool)
393
- loaded_by_iboot = plist_access_path(manifest_entry, ('Info', 'IsLoadedByiBoot'), bool)
394
- is_secondary_fw = plist_access_path(manifest_entry, ('Info', 'IsSecondaryFirmwarePayload'), bool)
386
+ is_fw = plist_access_path(manifest_entry, ("Info", "IsFirmwarePayload"), bool)
387
+ loaded_by_iboot = plist_access_path(manifest_entry, ("Info", "IsLoadedByiBoot"), bool)
388
+ is_secondary_fw = plist_access_path(manifest_entry, ("Info", "IsSecondaryFirmwarePayload"), bool)
395
389
 
396
390
  if is_fw or (is_secondary_fw and loaded_by_iboot):
397
- comp_path = plist_access_path(manifest_entry, ('Info', 'Path'))
391
+ comp_path = plist_access_path(manifest_entry, ("Info", "Path"))
398
392
  if comp_path:
399
393
  firmware_files[component] = comp_path
400
394
 
401
395
  if not firmware_files:
402
- raise PyMobileDevice3Exception('Unable to get list of firmware files.')
396
+ raise PyMobileDevice3Exception("Unable to get list of firmware files.")
403
397
 
404
- component = 'LLB'
398
+ component = "LLB"
405
399
  llb_data = self.get_personalized_data(component, tss=self.recovery.tss, path=llb_path)
406
- req = {'LlbImageData': llb_data}
400
+ req = {"LlbImageData": llb_data}
407
401
 
408
- if flash_version_1:
409
- norimage = {}
410
- else:
411
- norimage = []
402
+ norimage = {} if flash_version_1 else []
412
403
 
413
404
  for component, comppath in firmware_files.items():
414
- if component in ('LLB', 'RestoreSEP'):
405
+ if component in ("LLB", "RestoreSEP"):
415
406
  # skip LLB, it's already passed in LlbImageData
416
407
  # skip RestoreSEP, it's passed in RestoreSEPImageData
417
408
  continue
@@ -422,50 +413,59 @@ class Restore(BaseRestore):
422
413
  norimage[component] = nor_data
423
414
  else:
424
415
  # make sure iBoot is the first entry in the array
425
- if component.startswith('iBoot'):
426
- norimage = [nor_data] + norimage
416
+ if component.startswith("iBoot"):
417
+ norimage = [nor_data, *norimage]
427
418
  else:
428
419
  norimage.append(nor_data)
429
420
 
430
- req['NorImageData'] = norimage
421
+ req["NorImageData"] = norimage
431
422
 
432
- for component in ('RestoreSEP', 'SEP', 'SepStage1'):
423
+ for component in ("RestoreSEP", "SEP", "SepStage1"):
433
424
  if not self.build_identity.has_component(component):
434
425
  continue
435
426
  comp = self.build_identity.get_component(component, tss=self.recovery.tss)
436
427
  if comp.path:
437
- if component == 'SepStage1':
438
- component = 'SEPPatch'
439
- req[f'{component}ImageData'] = self.get_personalized_data(comp.name, comp.data, self.recovery.tss)
428
+ if component == "SepStage1":
429
+ component = "SEPPatch"
430
+ req[f"{component}ImageData"] = self.get_personalized_data(comp.name, comp.data, self.recovery.tss)
440
431
 
441
- self.logger.info('Sending NORData now...')
432
+ self.logger.info("Sending NORData now...")
442
433
  await service.aio_send_plist(req)
443
434
 
444
435
  @staticmethod
445
- def get_bbfw_fn_for_element(elem):
436
+ def get_bbfw_fn_for_element(elem: str, bb_chip_id: Optional[int] = None) -> str:
446
437
  bbfw_fn_elem = {
447
438
  # ICE3 firmware files
448
- 'RamPSI': 'psi_ram.fls',
449
- 'FlashPSI': 'psi_flash.fls',
439
+ "RamPSI": "psi_ram.fls",
440
+ "FlashPSI": "psi_flash.fls",
450
441
  # Trek firmware files
451
- 'eDBL': 'dbl.mbn',
452
- 'RestoreDBL': 'restoredbl.mbn',
442
+ "eDBL": "dbl.mbn",
443
+ "RestoreDBL": "restoredbl.mbn",
453
444
  # Phoenix/Mav4 firmware files
454
- 'DBL': 'dbl.mbn',
455
- 'ENANDPRG': 'ENPRG.mbn',
445
+ "DBL": "dbl.mbn",
446
+ "ENANDPRG": "ENPRG.mbn",
456
447
  # Mav5 firmware files
457
- 'RestoreSBL1': 'restoresbl1.mbn',
458
- 'SBL1': 'sbl1.mbn',
448
+ "RestoreSBL1": "restoresbl1.mbn",
449
+ "SBL1": "sbl1.mbn",
459
450
  # ICE16 firmware files
460
- 'RestorePSI': 'restorepsi.bin',
461
- 'PSI': 'psi_ram.bin',
451
+ "RestorePSI": "restorepsi.bin",
452
+ "PSI": "psi_ram.bin",
462
453
  # ICE19 firmware files
463
- 'RestorePSI2': 'restorepsi2.bin',
464
- 'PSI2': 'psi_ram2.bin',
454
+ "RestorePSI2": "restorepsi2.bin",
455
+ "PSI2": "psi_ram2.bin",
465
456
  # Mav20 Firmware file
466
- 'Misc': 'multi_image.mbn',
457
+ "Misc": "multi_image.mbn",
467
458
  }
468
- return bbfw_fn_elem.get(elem)
459
+
460
+ bbfw_fn_elem_mav25 = {
461
+ # Mav25 Firmware files
462
+ "Misc": "multi_image.mbn",
463
+ "RestoreSBL1": "restorexbl_sc.elf",
464
+ "SBL1": "xbl_sc.elf",
465
+ "TME": "signed_firmware_soc_view.elf",
466
+ }
467
+
468
+ return bbfw_fn_elem_mav25.get(elem) if bb_chip_id == 0x1F30E1 else bbfw_fn_elem.get(elem)
469
469
 
470
470
  def fls_parse(self, buffer):
471
471
  raise NotImplementedError()
@@ -476,10 +476,12 @@ class Restore(BaseRestore):
476
476
  def fls_insert_ticket(self, fls, bbticket):
477
477
  raise NotImplementedError()
478
478
 
479
- def sign_bbfw(self, bbfw_orig, bbtss, bb_nonce):
479
+ def sign_bbfw(
480
+ self, bbfw_orig: bytes, bbtss: TSSResponse, bb_nonce: Optional[bytes], bb_chip_id: Optional[int] = None
481
+ ) -> bytes:
480
482
  # check for BBTicket in result
481
483
  bbticket = bbtss.bb_ticket
482
- bbfw_dict = bbtss.get('BasebandFirmware')
484
+ bbfw_dict = bbtss.get("BasebandFirmware")
483
485
  is_fls = False
484
486
  signed_file = []
485
487
 
@@ -488,99 +490,100 @@ class Restore(BaseRestore):
488
490
  tmp_zip_read_name = tmp_zip_read.name
489
491
 
490
492
  try:
491
- with zipfile.ZipFile(tmp_zip_read_name, 'r') as bbfw_orig:
492
- with tempfile.NamedTemporaryFile() as tmp_zip_write:
493
- bbfw_patched = zipfile.ZipFile(tmp_zip_write, 'w')
493
+ with zipfile.ZipFile(tmp_zip_read_name, "r") as bbfw_orig, tempfile.NamedTemporaryFile() as tmp_zip_write:
494
+ bbfw_patched = zipfile.ZipFile(tmp_zip_write, "w")
494
495
 
495
- for key, blob in bbfw_dict.items():
496
- if key.endswith('-Blob') and isinstance(blob, bytes):
497
- key = key.split('-', 1)[0]
498
- signfn = self.get_bbfw_fn_for_element(key)
496
+ for key, blob in bbfw_dict.items():
497
+ if key.endswith("-Blob") and isinstance(blob, bytes):
498
+ key = key.split("-", 1)[0]
499
+ signfn = self.get_bbfw_fn_for_element(key, bb_chip_id)
499
500
 
500
- if signfn is None:
501
- raise PyMobileDevice3Exception(
502
- f'can\'t match element name \'{key}\' to baseband firmware file name.')
501
+ if signfn is None:
502
+ raise PyMobileDevice3Exception(
503
+ f"can't match element name '{key}' to baseband firmware file name."
504
+ )
503
505
 
504
- if signfn.endswith('.fls'):
505
- is_fls = True
506
+ if signfn.endswith(".fls"):
507
+ is_fls = True
506
508
 
507
- buffer = bbfw_orig.read(signfn)
509
+ buffer = bbfw_orig.read(signfn)
508
510
 
509
- if is_fls:
510
- fls = self.fls_parse(buffer)
511
- data = self.fls_update_sig_blob(fls, blob)
512
- else:
513
- parsed_sig_offset = len(buffer) - len(blob)
514
- data = buffer[:parsed_sig_offset] + blob
511
+ if is_fls:
512
+ raise NotImplementedError("is_fls")
513
+ elif bb_chip_id == 0x1F30E1: # Mav25 - Qualcomm Snapdragon X80 5G Modem
514
+ data = mbn_mav25_stitch(buffer, blob)
515
+ else:
516
+ data = mbn_stitch(buffer, blob)
515
517
 
516
- bbfw_patched.writestr(bbfw_orig.getinfo(signfn), data)
518
+ bbfw_patched.writestr(bbfw_orig.getinfo(signfn), data)
517
519
 
518
- if is_fls and (bb_nonce is None):
519
- if key == 'RamPSI':
520
- signed_file.append(signfn)
521
- else:
520
+ if is_fls and (bb_nonce is None):
521
+ if key == "RamPSI":
522
522
  signed_file.append(signfn)
523
-
524
- # remove everything but required files
525
- for entry in bbfw_orig.filelist:
526
- keep = False
527
- filename = entry.filename
528
-
529
- if filename in signed_file:
530
- keep = True
531
-
532
- # check for anything but .mbn and .fls if bb_nonce is set
533
- if bb_nonce and not keep:
534
- ext = os.path.splitext(filename)[1]
535
- keep |= ext in ('.fls', '.mbn', '.elf', '.bin')
536
-
537
- if keep and (filename not in signed_file):
538
- bbfw_patched.writestr(bbfw_orig.getinfo(filename), bbfw_orig.read(filename))
539
-
540
- if bb_nonce:
541
- if is_fls:
542
- # add BBTicket to file ebl.fls
543
- buffer = bbfw_orig.read('ebl.fls')
544
- fls = self.fls_parse(buffer)
545
- data = self.fls_insert_ticket(fls, bbticket)
546
- bbfw_patched.writestr('ebl.fls', data)
547
523
  else:
548
- # add BBTicket as bbticket.der
549
- zname = zipfile.ZipInfo('bbticket.der')
550
- zname.filename = 'bbticket.der'
551
- ZIP_EXT_ATTR_FILE = 0o100000
552
- zname.external_attr = (0o644 | ZIP_EXT_ATTR_FILE) << 16
553
- bbfw_patched.writestr(zname, bbticket)
554
-
555
- bbfw_patched.close()
556
- tmp_zip_write.seek(0)
557
- return tmp_zip_write.read()
524
+ signed_file.append(signfn)
525
+
526
+ # remove everything but required files
527
+ for entry in bbfw_orig.filelist:
528
+ keep = False
529
+ filename = entry.filename
530
+
531
+ if filename in signed_file:
532
+ keep = True
533
+
534
+ # check for anything but .mbn and .fls if bb_nonce is set
535
+ if bb_nonce and not keep:
536
+ ext = os.path.splitext(filename)[1]
537
+ keep |= ext in (".fls", ".mbn", ".elf", ".bin")
538
+
539
+ if keep and (filename not in signed_file):
540
+ bbfw_patched.writestr(bbfw_orig.getinfo(filename), bbfw_orig.read(filename))
541
+
542
+ if bb_nonce:
543
+ if is_fls:
544
+ # add BBTicket to file ebl.fls
545
+ buffer = bbfw_orig.read("ebl.fls")
546
+ fls = self.fls_parse(buffer)
547
+ data = self.fls_insert_ticket(fls, bbticket)
548
+ bbfw_patched.writestr("ebl.fls", data)
549
+ else:
550
+ # add BBTicket as bbticket.der
551
+ zname = zipfile.ZipInfo("bbticket.der")
552
+ zname.filename = "bbticket.der"
553
+ ZIP_EXT_ATTR_FILE = 0o100000
554
+ zname.external_attr = (0o644 | ZIP_EXT_ATTR_FILE) << 16
555
+ bbfw_patched.writestr(zname, bbticket)
556
+
557
+ bbfw_patched.close()
558
+ tmp_zip_write.seek(0)
559
+ return tmp_zip_write.read()
558
560
  finally:
559
561
  if tmp_zip_read_name:
560
562
  os.remove(tmp_zip_read_name)
561
563
 
564
+ @asyncio_print_traceback
562
565
  async def send_baseband_data(self, message: dict):
563
- self.logger.info(f'About to send BasebandData: {message}')
566
+ self.logger.info(f"About to send BasebandData: {message}")
564
567
  service = await self._get_service_for_data_request(message)
565
568
 
566
569
  # NOTE: this function is called 2 or 3 times!
567
570
 
568
571
  # setup request data
569
- arguments = message['Arguments']
570
- bb_chip_id = arguments.get('ChipID')
571
- bb_cert_id = arguments.get('CertID')
572
- bb_snum = arguments.get('ChipSerialNo')
573
- bb_nonce = arguments.get('Nonce')
572
+ arguments = message["Arguments"]
573
+ bb_chip_id = arguments.get("ChipID")
574
+ bb_cert_id = arguments.get("CertID")
575
+ bb_snum = arguments.get("ChipSerialNo")
576
+ bb_nonce = arguments.get("Nonce")
574
577
  bbtss = self.bbtss
575
578
 
576
579
  if (bb_nonce is None) or (self.bbtss is None):
577
580
  # populate parameters
578
- parameters = {'ApECID': self.device.ecid}
581
+ parameters = {"ApECID": self.device.ecid}
579
582
  if bb_nonce:
580
- parameters['BbNonce'] = bb_nonce
581
- parameters['BbChipID'] = bb_chip_id
582
- parameters['BbGoldCertId'] = bb_cert_id
583
- parameters['BbSNUM'] = bb_snum
583
+ parameters["BbNonce"] = bb_nonce
584
+ parameters["BbChipID"] = bb_chip_id
585
+ parameters["BbGoldCertId"] = bb_cert_id
586
+ parameters["BbSNUM"] = bb_snum
584
587
 
585
588
  self.populate_tss_request_from_manifest(parameters)
586
589
 
@@ -591,11 +594,11 @@ class Restore(BaseRestore):
591
594
  request.add_common_tags(parameters)
592
595
  request.add_baseband_tags(parameters)
593
596
 
594
- fdr_support = self.build_identity['Info'].get('FDRSupport', False)
597
+ fdr_support = self.build_identity["Info"].get("FDRSupport", False)
595
598
  if fdr_support:
596
- request.update({'ApProductionMode': True, 'ApSecurityMode': True})
599
+ request.update({"ApProductionMode": True, "ApSecurityMode": True})
597
600
 
598
- self.logger.info('Sending Baseband TSS request...')
601
+ self.logger.info("Sending Baseband TSS request...")
599
602
  bbtss = await request.send_receive()
600
603
 
601
604
  if bb_nonce:
@@ -603,124 +606,127 @@ class Restore(BaseRestore):
603
606
  self.bbtss = bbtss
604
607
 
605
608
  # get baseband firmware file path from build identity
606
- bbfwpath = self.build_identity['Manifest']['BasebandFirmware']['Info']['Path']
609
+ bbfwpath = self.build_identity["Manifest"]["BasebandFirmware"]["Info"]["Path"]
607
610
 
608
611
  # extract baseband firmware to temp file
609
612
  bbfw = self.ipsw.read(bbfwpath)
610
613
 
611
- buffer = self.sign_bbfw(bbfw, bbtss, bb_nonce)
614
+ buffer = self.sign_bbfw(bbfw, bbtss, bb_nonce, bb_chip_id)
612
615
 
613
- self.logger.info('Sending BasebandData now...')
614
- await service.aio_send_plist({'BasebandData': buffer})
616
+ self.logger.info("Sending BasebandData now...")
617
+ await service.aio_send_plist({"BasebandData": buffer})
615
618
 
616
619
  async def send_fdr_trust_data(self, message: dict) -> None:
617
- self.logger.info('About to send FDR Trust data...')
620
+ self.logger.info("About to send FDR Trust data...")
618
621
  service = await self._get_service_for_data_request(message)
619
622
 
620
623
  # FIXME: What should we send here?
621
624
  # Sending an empty dict makes it continue with FDR
622
625
  # and this is what iTunes seems to be doing too
623
- self.logger.info('Sending FDR Trust data now...')
626
+ self.logger.info("Sending FDR Trust data now...")
624
627
  await service.aio_send_plist({})
625
628
 
626
629
  async def send_image_data(
627
- self, message: dict, image_list_k: Optional[str], image_type_k: Optional[str],
628
- image_data_k: Optional[str]) -> None:
629
- self.logger.debug(f'send_image_data: {message}')
630
- arguments = message['Arguments']
630
+ self, message: dict, image_list_k: Optional[str], image_type_k: Optional[str], image_data_k: Optional[str]
631
+ ) -> None:
632
+ self.logger.debug(f"send_image_data: {message}")
633
+ arguments = message["Arguments"]
631
634
  want_image_list = arguments.get(image_list_k)
632
- image_name = arguments.get('ImageName')
633
- build_id_manifest = self.build_identity['Manifest']
634
-
635
- if not want_image_list and image_name is not None:
635
+ image_name = arguments.get("ImageName")
636
+ build_id_manifest = self.build_identity["Manifest"]
637
+
638
+ if (
639
+ (not want_image_list)
640
+ and (image_name is not None)
641
+ and (image_name not in build_id_manifest)
642
+ and (image_name.startswith("Ap"))
643
+ ):
644
+ image_name = image_name.replace("Ap", "Ap,")
636
645
  if image_name not in build_id_manifest:
637
- if image_name.startswith('Ap'):
638
- image_name = image_name.replace('Ap', 'Ap,')
639
- if image_name not in build_id_manifest:
640
- raise PyMobileDevice3Exception(f'{image_name} not in build_id_manifest')
646
+ raise PyMobileDevice3Exception(f"{image_name} not in build_id_manifest")
641
647
 
642
648
  if image_type_k is None:
643
- image_type_k = arguments['ImageType']
649
+ image_type_k = arguments["ImageType"]
644
650
 
645
651
  if image_type_k is None:
646
- raise PyMobileDevice3Exception('missing ImageType')
652
+ raise PyMobileDevice3Exception("missing ImageType")
647
653
 
648
654
  if want_image_list is None and image_name is None:
649
- self.logger.info(f'About to send {image_data_k}...')
655
+ self.logger.info(f"About to send {image_data_k}...")
650
656
 
651
657
  matched_images = []
652
- data_dict = dict()
658
+ data_dict = {}
653
659
 
654
660
  for component, manifest_entry in build_id_manifest.items():
655
661
  if not isinstance(manifest_entry, dict):
656
662
  continue
657
663
 
658
- is_image_type = manifest_entry['Info'].get(image_type_k)
664
+ is_image_type = manifest_entry["Info"].get(image_type_k)
659
665
  if is_image_type:
660
666
  if want_image_list:
661
- self.logger.info(f'found {component} component')
667
+ self.logger.info(f"found {component} component")
662
668
  matched_images.append(component)
663
669
  elif image_name is None or image_name == component:
664
670
  if image_name is None:
665
- self.logger.info(f'found {image_type_k} component \'{component}\'')
671
+ self.logger.info(f"found {image_type_k} component '{component}'")
666
672
  else:
667
- self.logger.info(f'found component \'{component}\'')
673
+ self.logger.info(f"found component '{component}'")
668
674
 
669
675
  data_dict[component] = self.get_personalized_data(component, tss=self.recovery.tss)
670
676
 
671
- req = dict()
677
+ req = {}
672
678
  if want_image_list:
673
679
  req[image_list_k] = matched_images
674
- self.logger.info(f'Sending {image_type_k} image list')
680
+ self.logger.info(f"Sending {image_type_k} image list")
675
681
  else:
676
682
  if image_name:
677
683
  if image_name in data_dict:
678
684
  req[image_data_k] = data_dict[image_name]
679
- req['ImageName'] = image_name
680
- self.logger.info(f'Sending {image_type_k} for {image_name}...')
685
+ req["ImageName"] = image_name
686
+ self.logger.info(f"Sending {image_type_k} for {image_name}...")
681
687
  else:
682
688
  req[image_data_k] = data_dict
683
- self.logger.info(f'Sending {image_type_k} now...')
689
+ self.logger.info(f"Sending {image_type_k} now...")
684
690
 
685
691
  await self._restored.send(req)
686
692
 
687
693
  async def send_bootability_bundle_data(self, message: dict) -> None:
688
- self.logger.debug(f'send_bootability_bundle_data: {message}')
694
+ self.logger.debug(f"send_bootability_bundle_data: {message}")
689
695
  service = await self._get_service_for_data_request(message)
690
696
  await service.aio_sendall(self.ipsw.bootability)
691
697
  await service.aio_close()
692
698
 
693
699
  async def send_manifest(self) -> None:
694
- self.logger.debug('send_manifest')
695
- await self._restored.send({'ReceiptManifest': self.build_identity.manifest})
700
+ self.logger.debug("send_manifest")
701
+ await self._restored.send({"ReceiptManifest": self.build_identity.manifest})
696
702
 
697
703
  async def get_se_firmware_data(self, updater_name: str, info: dict, arguments: dict) -> dict:
698
- chip_id = info.get('SE,ChipID')
704
+ chip_id = info.get("SE,ChipID")
699
705
  if chip_id is None:
700
- chip_id = self.build_identity['Manifest']['SE,ChipID']
706
+ chip_id = self.build_identity["Manifest"]["SE,ChipID"]
701
707
 
702
708
  if chip_id == 0x20211:
703
- comp_name = 'SE,Firmware'
704
- elif chip_id in (0x73, 0x64, 0xC8, 0xD2, 0x2c, 0x36, 0x37):
705
- comp_name = 'SE,UpdatePayload'
709
+ comp_name = "SE,Firmware"
710
+ elif chip_id in (0x73, 0x64, 0xC8, 0xD2, 0x2C, 0x36, 0x37):
711
+ comp_name = "SE,UpdatePayload"
706
712
  else:
707
- self.logger.warning(f'Unknown SE,ChipID {chip_id} detected. Restore might fail.')
713
+ self.logger.warning(f"Unknown SE,ChipID {chip_id} detected. Restore might fail.")
708
714
 
709
- if self.build_identity.has_component('SE,UpdatePayload'):
710
- comp_name = 'SE,UpdatePayload'
711
- elif self.build_identity.has_component('SE,Firmware'):
712
- comp_name = 'SE,Firmware'
715
+ if self.build_identity.has_component("SE,UpdatePayload"):
716
+ comp_name = "SE,UpdatePayload"
717
+ elif self.build_identity.has_component("SE,Firmware"):
718
+ comp_name = "SE,Firmware"
713
719
  else:
714
- raise NotImplementedError('Neither \'SE,Firmware\' nor \'SE,UpdatePayload\' found in build identity.')
720
+ raise NotImplementedError("Neither 'SE,Firmware' nor 'SE,UpdatePayload' found in build identity.")
715
721
 
716
722
  component_data = self.build_identity.get_component(comp_name).data
717
723
 
718
- if 'DeviceGeneratedTags' in arguments:
724
+ if "DeviceGeneratedTags" in arguments:
719
725
  response = self.get_device_generated_firmware_data(updater_name, info, arguments)
720
726
  else:
721
727
  # create SE request
722
728
  request = TSSRequest()
723
- parameters = dict()
729
+ parameters = {}
724
730
 
725
731
  # add manifest for current build_identity to parameters
726
732
  self.populate_tss_request_from_manifest(parameters)
@@ -731,22 +737,22 @@ class Restore(BaseRestore):
731
737
  # add required tags for SE TSS request
732
738
  request.add_se_tags(parameters, None)
733
739
 
734
- self.logger.info('Sending SE TSS request...')
740
+ self.logger.info("Sending SE TSS request...")
735
741
  response = await request.send_receive()
736
742
 
737
- if 'SE,Ticket' in response:
738
- self.logger.info('Received SE ticket')
743
+ if "SE,Ticket" in response:
744
+ self.logger.info("Received SE ticket")
739
745
  else:
740
- raise PyMobileDevice3Exception('No \'SE,Ticket\' in TSS response, this might not work')
746
+ raise PyMobileDevice3Exception("No 'SE,Ticket' in TSS response, this might not work")
741
747
 
742
- response['FirmwareData'] = component_data
748
+ response["FirmwareData"] = component_data
743
749
 
744
750
  return response
745
751
 
746
752
  async def get_yonkers_firmware_data(self, info: dict):
747
753
  # create Yonkers request
748
754
  request = TSSRequest()
749
- parameters = dict()
755
+ parameters = {}
750
756
 
751
757
  # add manifest for current build_identity to parameters
752
758
  self.populate_tss_request_from_manifest(parameters)
@@ -758,33 +764,33 @@ class Restore(BaseRestore):
758
764
  comp_name = request.add_yonkers_tags(parameters, None)
759
765
 
760
766
  if comp_name is None:
761
- raise PyMobileDevice3Exception('Could not determine Yonkers firmware component')
767
+ raise PyMobileDevice3Exception("Could not determine Yonkers firmware component")
762
768
 
763
- self.logger.debug(f'restore_get_yonkers_firmware_data: using {comp_name}')
769
+ self.logger.debug(f"restore_get_yonkers_firmware_data: using {comp_name}")
764
770
 
765
- self.logger.info('Sending SE Yonkers request...')
771
+ self.logger.info("Sending SE Yonkers request...")
766
772
  response = await request.send_receive()
767
773
 
768
- if 'Yonkers,Ticket' in response:
769
- self.logger.info('Received SE ticket')
774
+ if "Yonkers,Ticket" in response:
775
+ self.logger.info("Received SE ticket")
770
776
  else:
771
- raise PyMobileDevice3Exception('No \'Yonkers,Ticket\' in TSS response, this might not work')
777
+ raise PyMobileDevice3Exception("No 'Yonkers,Ticket' in TSS response, this might not work")
772
778
 
773
779
  # now get actual component data
774
780
  component_data = self.build_identity.get_component(comp_name).data
775
781
 
776
782
  firmware_data = {
777
- 'YonkersFirmware': component_data,
783
+ "YonkersFirmware": component_data,
778
784
  }
779
785
 
780
- response['FirmwareData'] = firmware_data
786
+ response["FirmwareData"] = firmware_data
781
787
 
782
788
  return response
783
789
 
784
790
  async def get_savage_firmware_data(self, info: dict):
785
791
  # create Savage request
786
792
  request = TSSRequest()
787
- parameters = dict()
793
+ parameters = {}
788
794
 
789
795
  # add manifest for current build_identity to parameters
790
796
  self.populate_tss_request_from_manifest(parameters)
@@ -796,47 +802,47 @@ class Restore(BaseRestore):
796
802
  comp_name = request.add_savage_tags(parameters, None)
797
803
 
798
804
  if comp_name is None:
799
- raise PyMobileDevice3Exception('Could not determine Savage firmware component')
805
+ raise PyMobileDevice3Exception("Could not determine Savage firmware component")
800
806
 
801
- self.logger.debug(f'restore_get_savage_firmware_data: using {comp_name}')
807
+ self.logger.debug(f"restore_get_savage_firmware_data: using {comp_name}")
802
808
 
803
- self.logger.info('Sending SE Savage request...')
809
+ self.logger.info("Sending SE Savage request...")
804
810
  response = await request.send_receive()
805
811
 
806
- if 'Savage,Ticket' in response:
807
- self.logger.info('Received SE ticket')
812
+ if "Savage,Ticket" in response:
813
+ self.logger.info("Received SE ticket")
808
814
  else:
809
- raise PyMobileDevice3Exception('No \'Savage,Ticket\' in TSS response, this might not work')
815
+ raise PyMobileDevice3Exception("No 'Savage,Ticket' in TSS response, this might not work")
810
816
 
811
817
  # now get actual component data
812
818
  component_data = self.build_identity.get_component(comp_name).data
813
- component_data = struct.pack('<L', len(component_data)) + b'\x00' * 12
819
+ component_data = struct.pack("<L", len(component_data)) + b"\x00" * 12
814
820
 
815
- response['FirmwareData'] = component_data
821
+ response["FirmwareData"] = component_data
816
822
 
817
823
  return response
818
824
 
819
825
  async def get_rose_firmware_data(self, updater_name: str, info: dict, arguments: dict):
820
- self.logger.info(f'get_rose_firmware_data: {info}')
826
+ self.logger.info(f"get_rose_firmware_data: {info}")
821
827
 
822
- if 'DeviceGeneratedTags' in arguments:
828
+ if "DeviceGeneratedTags" in arguments:
823
829
  response = self.get_device_generated_firmware_data(updater_name, info, arguments)
824
830
  return response
825
831
  else:
826
832
  # create Rose request
827
833
  request = TSSRequest()
828
- parameters = dict()
834
+ parameters = {}
829
835
 
830
836
  # add manifest for current build_identity to parameters
831
837
  self.populate_tss_request_from_manifest(parameters)
832
838
 
833
- parameters['ApProductionMode'] = True
839
+ parameters["ApProductionMode"] = True
834
840
 
835
841
  if self.device.is_image4_supported:
836
- parameters['ApSecurityMode'] = True
837
- parameters['ApSupportsImg4'] = True
842
+ parameters["ApSecurityMode"] = True
843
+ parameters["ApSupportsImg4"] = True
838
844
  else:
839
- parameters['ApSupportsImg4'] = False
845
+ parameters["ApSupportsImg4"] = False
840
846
 
841
847
  # add Rap,* tags from info dictionary to parameters
842
848
  parameters.update(info)
@@ -844,42 +850,42 @@ class Restore(BaseRestore):
844
850
  # add required tags for Rose TSS request
845
851
  request.add_rose_tags(parameters, None)
846
852
 
847
- self.logger.info('Sending Rose TSS request...')
853
+ self.logger.info("Sending Rose TSS request...")
848
854
  response = await request.send_receive()
849
855
 
850
- rose_ticket = response.get('Rap,Ticket')
856
+ rose_ticket = response.get("Rap,Ticket")
851
857
  if rose_ticket is None:
852
858
  self.logger.error('No "Rap,Ticket" in TSS response, this might not work')
853
859
 
854
- comp_name = 'Rap,RTKitOS'
860
+ comp_name = "Rap,RTKitOS"
855
861
  component_data = self.build_identity.get_component(comp_name).data
856
862
 
857
863
  ftab = Ftab(component_data)
858
864
 
859
- comp_name = 'Rap,RestoreRTKitOS'
865
+ comp_name = "Rap,RestoreRTKitOS"
860
866
  if self.build_identity.has_component(comp_name):
861
867
  rftab = Ftab(self.build_identity.get_component(comp_name).data)
862
868
 
863
- component_data = rftab.get_entry_data(b'rrko')
869
+ component_data = rftab.get_entry_data(b"rrko")
864
870
  if component_data is None:
865
871
  self.logger.error('Could not find "rrko" entry in ftab. This will probably break things')
866
872
  else:
867
- ftab.add_entry(b'rrko', component_data)
873
+ ftab.add_entry(b"rrko", component_data)
868
874
 
869
- response['FirmwareData'] = ftab.data
875
+ response["FirmwareData"] = ftab.data
870
876
 
871
877
  return response
872
878
 
873
879
  async def get_veridian_firmware_data(self, updater_name: str, info: dict, arguments: dict):
874
- self.logger.info(f'get_veridian_firmware_data: {info}')
875
- comp_name = 'BMU,FirmwareMap'
880
+ self.logger.info(f"get_veridian_firmware_data: {info}")
881
+ comp_name = "BMU,FirmwareMap"
876
882
 
877
- if 'DeviceGeneratedTags' in arguments:
883
+ if "DeviceGeneratedTags" in arguments:
878
884
  response = self.get_device_generated_firmware_data(updater_name, info, arguments)
879
885
  else:
880
886
  # create Veridian request
881
887
  request = TSSRequest()
882
- parameters = dict()
888
+ parameters = {}
883
889
 
884
890
  # add manifest for current build_identity to parameters
885
891
  self.populate_tss_request_from_manifest(parameters)
@@ -890,29 +896,29 @@ class Restore(BaseRestore):
890
896
  # add required tags for Veridian TSS request
891
897
  request.add_veridian_tags(parameters, None)
892
898
 
893
- self.logger.info('Sending Veridian TSS request...')
899
+ self.logger.info("Sending Veridian TSS request...")
894
900
  response = await request.send_receive()
895
901
 
896
- ticket = response.get('BMU,Ticket')
902
+ ticket = response.get("BMU,Ticket")
897
903
  if ticket is None:
898
904
  self.logger.warning('No "BMU,Ticket" in TSS response, this might not work')
899
905
 
900
906
  component_data = self.build_identity.get_component(comp_name).data
901
907
  fw_map = plistlib.loads(component_data)
902
- fw_map['fw_map_digest'] = self.build_identity['Manifest'][comp_name]['Digest']
908
+ fw_map["fw_map_digest"] = self.build_identity["Manifest"][comp_name]["Digest"]
903
909
 
904
910
  bin_plist = plistlib.dumps(fw_map, fmt=plistlib.PlistFormat.FMT_BINARY)
905
- response['FirmwareData'] = bin_plist
911
+ response["FirmwareData"] = bin_plist
906
912
 
907
913
  return response
908
914
 
909
915
  async def get_tcon_firmware_data(self, info: dict):
910
- self.logger.info(f'restore_get_tcon_firmware_data: {info}')
911
- comp_name = 'Baobab,TCON'
916
+ self.logger.info(f"restore_get_tcon_firmware_data: {info}")
917
+ comp_name = "Baobab,TCON"
912
918
 
913
919
  # create Baobab request
914
920
  request = TSSRequest()
915
- parameters = dict()
921
+ parameters = {}
916
922
 
917
923
  # add manifest for current build_identity to parameters
918
924
  self.populate_tss_request_from_manifest(parameters)
@@ -923,47 +929,46 @@ class Restore(BaseRestore):
923
929
  # add required tags for Baobab TSS request
924
930
  request.add_tcon_tags(parameters, None)
925
931
 
926
- self.logger.info('Sending Baobab TSS request...')
932
+ self.logger.info("Sending Baobab TSS request...")
927
933
  response = await request.send_receive()
928
934
 
929
- ticket = response.get('Baobab,Ticket')
935
+ ticket = response.get("Baobab,Ticket")
930
936
  if ticket is None:
931
937
  self.logger.warning('No "Baobab,Ticket" in TSS response, this might not work')
932
938
 
933
- response['FirmwareData'] = self.build_identity.get_component(comp_name).data
939
+ response["FirmwareData"] = self.build_identity.get_component(comp_name).data
934
940
 
935
941
  return response
936
942
 
937
943
  async def get_device_generated_firmware_data(self, updater_name: str, info: dict, arguments: dict) -> dict:
938
- self.logger.info(f'get_device_generated_firmware_data ({updater_name}): {arguments}')
944
+ self.logger.info(f"get_device_generated_firmware_data ({updater_name}): {arguments}")
939
945
  request = TSSRequest()
940
- parameters = dict()
946
+ parameters = {}
941
947
 
942
948
  # add manifest for current build_identity to parameters
943
- self.populate_tss_request_from_manifest(
944
- parameters, arguments['DeviceGeneratedTags']['BuildIdentityTags'])
949
+ self.populate_tss_request_from_manifest(parameters, arguments["DeviceGeneratedTags"]["BuildIdentityTags"])
945
950
 
946
- parameters['@BBTicket'] = True
947
- parameters['ApSecurityMode'] = True
951
+ parameters["@BBTicket"] = True
952
+ parameters["ApSecurityMode"] = True
948
953
 
949
954
  # by default, set it to True
950
- parameters['ApProductionMode'] = True
955
+ parameters["ApProductionMode"] = True
951
956
 
952
- for k, v in arguments['MessageArgInfo'].items():
953
- if k.endswith('ProductionMode'):
957
+ for k, v in arguments["MessageArgInfo"].items():
958
+ if k.endswith("ProductionMode"):
954
959
  # if ApProductionMode should be overridden
955
- parameters['ApProductionMode'] = bool(v)
960
+ parameters["ApProductionMode"] = bool(v)
956
961
 
957
- response_ticket = arguments['DeviceGeneratedTags']['ResponseTags'][0]
962
+ response_ticket = arguments["DeviceGeneratedTags"]["ResponseTags"][0]
958
963
 
959
- parameters.update(arguments['DeviceGeneratedRequest'])
964
+ parameters.update(arguments["DeviceGeneratedRequest"])
960
965
  request.add_common_tags(info)
961
966
  request.update(parameters)
962
967
 
963
- for redacted_field in ('RequiresUIDMode',):
968
+ for redacted_field in ("RequiresUIDMode",):
964
969
  request.remove_key(redacted_field)
965
970
 
966
- self.logger.info(f'Sending {updater_name} TSS request...')
971
+ self.logger.info(f"Sending {updater_name} TSS request...")
967
972
  response = await request.send_receive()
968
973
 
969
974
  ticket = response.get(response_ticket)
@@ -974,198 +979,201 @@ class Restore(BaseRestore):
974
979
  return response
975
980
 
976
981
  async def get_timer_firmware_data(self, info: dict):
977
- self.logger.info(f'get_timer_firmware_data: {info}')
982
+ self.logger.info(f"get_timer_firmware_data: {info}")
978
983
 
979
984
  ftab = None
980
985
 
981
986
  # create Timer request
982
987
  request = TSSRequest()
983
- parameters = dict()
988
+ parameters = {}
984
989
 
985
990
  # add manifest for current build_identity to parameters
986
991
  self.populate_tss_request_from_manifest(parameters)
987
992
 
988
- parameters['ApProductionMode'] = True
993
+ parameters["ApProductionMode"] = True
989
994
  if self.device.is_image4_supported:
990
- parameters['ApSecurityMode'] = True
991
- parameters['ApSupportsImg4'] = True
995
+ parameters["ApSecurityMode"] = True
996
+ parameters["ApSupportsImg4"] = True
992
997
  else:
993
- parameters['ApSupportsImg4'] = False
998
+ parameters["ApSupportsImg4"] = False
994
999
 
995
1000
  # add Timer,* tags from info dictionary to parameters
996
- info_array = info['InfoArray']
1001
+ info_array = info["InfoArray"]
997
1002
  info_dict = info_array[0]
998
- hwid = info_dict['HardwareID']
999
- tag = info_dict['TagNumber']
1000
- parameters['TagNumber'] = tag
1001
- ticket_name = info_dict['TicketName']
1002
- parameters['TicketName'] = ticket_name
1003
- parameters[f'Timer,ChipID,{tag}'] = hwid['ChipID']
1004
- parameters[f'Timer,BoardID,{tag}'] = hwid['BoardID']
1005
- parameters[f'Timer,ECID,{tag}'] = hwid['ECID']
1006
- parameters[f'Timer,Nonce,{tag}'] = hwid['Nonce']
1007
- parameters[f'Timer,SecurityMode,{tag}'] = hwid['SecurityMode']
1008
- parameters[f'Timer,SecurityDomain,{tag}'] = hwid['SecurityDomain']
1009
- parameters[f'Timer,ProductionMode,{tag}'] = hwid['ProductionMode']
1010
-
1011
- ap_info = info['APInfo']
1003
+ hwid = info_dict["HardwareID"]
1004
+ tag = info_dict["TagNumber"]
1005
+ parameters["TagNumber"] = tag
1006
+ ticket_name = info_dict["TicketName"]
1007
+ parameters["TicketName"] = ticket_name
1008
+ parameters[f"Timer,ChipID,{tag}"] = hwid["ChipID"]
1009
+ parameters[f"Timer,BoardID,{tag}"] = hwid["BoardID"]
1010
+ parameters[f"Timer,ECID,{tag}"] = hwid["ECID"]
1011
+ parameters[f"Timer,Nonce,{tag}"] = hwid["Nonce"]
1012
+ parameters[f"Timer,SecurityMode,{tag}"] = hwid["SecurityMode"]
1013
+ parameters[f"Timer,SecurityDomain,{tag}"] = hwid["SecurityDomain"]
1014
+ parameters[f"Timer,ProductionMode,{tag}"] = hwid["ProductionMode"]
1015
+
1016
+ ap_info = info["APInfo"]
1012
1017
  parameters.update(ap_info)
1013
1018
 
1014
1019
  # add required tags for Timer TSS request
1015
1020
  request.add_timer_tags(parameters, None)
1016
1021
 
1017
- self.logger.info(f'Sending {ticket_name} TSS request...')
1022
+ self.logger.info(f"Sending {ticket_name} TSS request...")
1018
1023
  response = await request.send_receive()
1019
1024
 
1020
1025
  ticket = response.get(ticket_name)
1021
1026
  if ticket is None:
1022
1027
  self.logger.warning(f'No "{ticket_name}" in TSS response, this might not work')
1023
1028
 
1024
- comp_name = f'Timer,RTKitOS,{tag}'
1029
+ comp_name = f"Timer,RTKitOS,{tag}"
1025
1030
  if self.build_identity.has_component(comp_name):
1026
1031
  ftab = Ftab(self.build_identity.get_component(comp_name).data)
1027
- if ftab.tag != b'rkos':
1028
- self.logger.warning(f'Unexpected tag {ftab.tag}. continuing anyway.')
1032
+ if ftab.tag != b"rkos":
1033
+ self.logger.warning(f"Unexpected tag {ftab.tag}. continuing anyway.")
1029
1034
  else:
1030
1035
  self.logger.info(f'NOTE: Build identity does not have a "{comp_name}" component.')
1031
1036
 
1032
- comp_name = f'Timer,RestoreRTKitOS,{tag}'
1037
+ comp_name = f"Timer,RestoreRTKitOS,{tag}"
1033
1038
  if self.build_identity.has_component(comp_name):
1034
1039
  rftab = Ftab(self.build_identity.get_component(comp_name).data)
1035
1040
 
1036
- component_data = rftab.get_entry_data(b'rrko')
1041
+ component_data = rftab.get_entry_data(b"rrko")
1037
1042
  if component_data is None:
1038
1043
  self.logger.error('Could not find "rrko" entry in ftab. This will probably break things')
1039
1044
  else:
1040
1045
  if ftab is None:
1041
- raise PyMobileDevice3Exception('ftab is None')
1042
- ftab.add_entry(b'rrko', component_data)
1046
+ raise PyMobileDevice3Exception("ftab is None")
1047
+ ftab.add_entry(b"rrko", component_data)
1043
1048
  else:
1044
1049
  self.logger.info(f'NOTE: Build identity does not have a "{comp_name}" component.')
1045
1050
 
1046
- response['FirmwareData'] = ftab.data
1051
+ response["FirmwareData"] = ftab.data
1047
1052
 
1048
1053
  return response
1049
1054
 
1050
1055
  async def send_firmware_updater_data(self, message: dict):
1051
- self.logger.debug(f'got FirmwareUpdaterData request: {message}')
1056
+ self.logger.debug(f"got FirmwareUpdaterData request: {message}")
1052
1057
  service = await self._get_service_for_data_request(message)
1053
- arguments = message['Arguments']
1054
- s_type = arguments['MessageArgType']
1055
- updater_name = arguments['MessageArgUpdaterName']
1056
- device_generated_request = arguments.get('DeviceGeneratedRequest')
1058
+ arguments = message["Arguments"]
1059
+ s_type = arguments["MessageArgType"]
1060
+ updater_name = arguments["MessageArgUpdaterName"]
1061
+ device_generated_request = arguments.get("DeviceGeneratedRequest")
1057
1062
 
1058
- if s_type not in ('FirmwareResponseData',):
1059
- raise PyMobileDevice3Exception(f'MessageArgType has unexpected value \'{s_type}\'')
1063
+ if s_type not in ("FirmwareResponseData",):
1064
+ raise PyMobileDevice3Exception(f"MessageArgType has unexpected value '{s_type}'")
1060
1065
 
1061
- info = arguments['MessageArgInfo']
1066
+ info = arguments["MessageArgInfo"]
1062
1067
 
1063
1068
  if device_generated_request is not None:
1064
1069
  fwdict = await self.get_device_generated_firmware_data(updater_name, info, arguments)
1065
1070
  if fwdict is None:
1066
- raise PyMobileDevice3Exception(f'Couldn\'t get {updater_name} firmware data')
1071
+ raise PyMobileDevice3Exception(f"Couldn't get {updater_name} firmware data")
1067
1072
 
1068
- elif updater_name == 'SE':
1073
+ elif updater_name == "SE":
1069
1074
  fwdict = await self.get_se_firmware_data(updater_name, info, arguments)
1070
1075
  if fwdict is None:
1071
- raise PyMobileDevice3Exception('Couldn\'t get SE firmware data')
1076
+ raise PyMobileDevice3Exception("Couldn't get SE firmware data")
1072
1077
 
1073
- elif updater_name == 'Savage':
1074
- fwtype = 'Savage'
1075
- info2 = info.get('YonkersDeviceInfo')
1078
+ elif updater_name == "Savage":
1079
+ fwtype = "Savage"
1080
+ info2 = info.get("YonkersDeviceInfo")
1076
1081
  if info2:
1077
- fwtype = 'Yonkers'
1082
+ fwtype = "Yonkers"
1078
1083
  fwdict = await self.get_yonkers_firmware_data(info2)
1079
1084
  else:
1080
1085
  fwdict = await self.get_savage_firmware_data(info)
1081
1086
  if fwdict is None:
1082
- raise PyMobileDevice3Exception(f'Couldn\'t get {fwtype} firmware data')
1087
+ raise PyMobileDevice3Exception(f"Couldn't get {fwtype} firmware data")
1083
1088
 
1084
- elif updater_name == 'Rose':
1089
+ elif updater_name == "Rose":
1085
1090
  fwdict = await self.get_rose_firmware_data(updater_name, info, arguments)
1086
1091
  if fwdict is None:
1087
- raise PyMobileDevice3Exception('Couldn\'t get Rose firmware data')
1092
+ raise PyMobileDevice3Exception("Couldn't get Rose firmware data")
1088
1093
 
1089
- elif updater_name == 'T200':
1094
+ elif updater_name == "T200":
1090
1095
  fwdict = await self.get_veridian_firmware_data(updater_name, info, arguments)
1091
1096
  if fwdict is None:
1092
- raise PyMobileDevice3Exception('Couldn\'t get Veridian firmware data')
1097
+ raise PyMobileDevice3Exception("Couldn't get Veridian firmware data")
1093
1098
 
1094
- elif updater_name == 'AppleTCON':
1099
+ elif updater_name == "AppleTCON":
1095
1100
  fwdict = await self.get_tcon_firmware_data(info)
1096
1101
  if fwdict is None:
1097
- raise PyMobileDevice3Exception('Couldn\'t get TCON firmware data')
1102
+ raise PyMobileDevice3Exception("Couldn't get TCON firmware data")
1098
1103
 
1099
- elif updater_name == 'AppleTypeCRetimer':
1104
+ elif updater_name == "AppleTypeCRetimer":
1100
1105
  fwdict = await self.get_timer_firmware_data(info)
1101
1106
  if fwdict is None:
1102
- raise PyMobileDevice3Exception('Couldn\'t get AppleTypeCRetimer firmware data')
1107
+ raise PyMobileDevice3Exception("Couldn't get AppleTypeCRetimer firmware data")
1103
1108
 
1104
1109
  else:
1105
- raise PyMobileDevice3Exception(f'Got unknown updater name: {updater_name}')
1110
+ raise PyMobileDevice3Exception(f"Got unknown updater name: {updater_name}")
1106
1111
 
1107
- self.logger.info('Sending FirmwareResponse data now...')
1108
- await service.aio_send_plist({'FirmwareResponseData': fwdict})
1112
+ self.logger.info("Sending FirmwareResponse data now...")
1113
+ await service.aio_send_plist({"FirmwareResponseData": fwdict})
1109
1114
 
1110
1115
  async def send_firmware_updater_preflight(self, message: dict) -> None:
1111
- self.logger.warning(f'send_firmware_updater_preflight: {message}')
1116
+ self.logger.warning(f"send_firmware_updater_preflight: {message}")
1112
1117
  service = await self._get_service_for_data_request(message)
1113
1118
  await service.aio_send_plist({})
1114
1119
 
1115
1120
  async def send_url_asset(self, message: dict) -> None:
1116
- self.logger.info(f'send_url_asset: {message}')
1121
+ self.logger.info(f"send_url_asset: {message}")
1117
1122
  service = await self._get_service_for_data_request(message)
1118
- arguments = message['Arguments']
1119
- assert arguments['RequestMethod'] == 'GET'
1120
- url = arguments['RequestURL']
1123
+ arguments = message["Arguments"]
1124
+ assert arguments["RequestMethod"] == "GET"
1125
+ url = arguments["RequestURL"]
1121
1126
 
1122
1127
  if url in self._url_assets_cache:
1123
- self.logger.debug('Using cached URLAsset')
1128
+ self.logger.debug("Using cached URLAsset")
1124
1129
  response = self._url_assets_cache[url]
1125
1130
  else:
1126
1131
  loop = asyncio.get_event_loop()
1127
1132
  with ThreadPoolExecutor() as executor:
1128
1133
  response = await loop.run_in_executor(executor, requests.get, url)
1129
1134
  self._url_assets_cache[url] = response
1130
- await service.aio_send_plist({
1131
- 'ResponseBody': response.content,
1132
- 'ResponseBodyDone': True,
1133
- 'ResponseHeaders': dict(response.headers),
1134
- 'ResponseStatus': response.status_code,
1135
- }, fmt=plistlib.FMT_BINARY)
1135
+ await service.aio_send_plist(
1136
+ {
1137
+ "ResponseBody": response.content,
1138
+ "ResponseBodyDone": True,
1139
+ "ResponseHeaders": dict(response.headers),
1140
+ "ResponseStatus": response.status_code,
1141
+ },
1142
+ fmt=plistlib.FMT_BINARY,
1143
+ )
1136
1144
  await service.aio_close()
1137
1145
 
1138
1146
  async def send_streamed_image_decryption_key(self, message: dict) -> None:
1139
- self.logger.info(f'send_streamed_image_decryption_key: {message}')
1147
+ self.logger.info(f"send_streamed_image_decryption_key: {message}")
1140
1148
  service = await self._get_service_for_data_request(message)
1141
- arguments = message['Arguments']
1142
- assert arguments['RequestMethod'] == 'POST'
1149
+ arguments = message["Arguments"]
1150
+ assert arguments["RequestMethod"] == "POST"
1143
1151
 
1144
1152
  response = requests.post(
1145
- arguments['RequestURL'],
1146
- headers=arguments['RequestAdditionalHeaders'],
1147
- data=arguments['RequestBody'])
1148
- self.logger.info(f'response {response} {response.content}')
1153
+ arguments["RequestURL"], headers=arguments["RequestAdditionalHeaders"], data=arguments["RequestBody"]
1154
+ )
1155
+ self.logger.info(f"response {response} {response.content}")
1149
1156
  await service.aio_send_plist({
1150
- 'ResponseBody': response.content,
1151
- 'ResponseBodyDone': True,
1152
- 'ResponseHeaders': dict(response.headers),
1153
- 'ResponseStatus': response.status_code,
1157
+ "ResponseBody": response.content,
1158
+ "ResponseBodyDone": True,
1159
+ "ResponseHeaders": dict(response.headers),
1160
+ "ResponseStatus": response.status_code,
1154
1161
  })
1155
1162
 
1156
1163
  async def send_component(self, component: str, component_name: Optional[str] = None) -> None:
1157
1164
  if component_name is None:
1158
1165
  component_name = component
1159
1166
 
1160
- self.logger.info(f'Sending now {component_name}...')
1161
- await self._restored.send(
1162
- {f'{component_name}File': self.get_personalized_data(component, tss=self.recovery.tss)})
1167
+ self.logger.info(f"Sending now {component_name}...")
1168
+ await self._restored.send({
1169
+ f"{component_name}File": self.get_personalized_data(component, tss=self.recovery.tss)
1170
+ })
1163
1171
 
1164
1172
  async def handle_data_request_msg(self, message: dict):
1165
- self.logger.debug(f'handle_data_request_msg: {message}')
1173
+ self.logger.debug(f"handle_data_request_msg: {message}")
1166
1174
 
1167
1175
  # checks and see what kind of data restored is requests and pass the request to its own handler
1168
- data_type = message.get('DataType')
1176
+ data_type = message.get("DataType")
1169
1177
 
1170
1178
  if not isinstance(data_type, str):
1171
1179
  return
@@ -1174,41 +1182,41 @@ class Restore(BaseRestore):
1174
1182
  await self._data_request_handlers[data_type](message)
1175
1183
  elif data_type in self._data_request_components:
1176
1184
  await self._data_request_components[data_type](data_type)
1177
- elif data_type == 'SystemImageRootHash':
1178
- await self.send_component('SystemVolume', data_type)
1179
- elif data_type == 'SystemImageCanonicalMetadata':
1180
- await self.send_component('Ap,SystemVolumeCanonicalMetadata', data_type)
1181
- elif data_type == 'FUDData':
1182
- await self.send_image_data(message, 'FUDImageList', 'IsFUDFirmware', 'FUDImageData')
1183
- elif data_type == 'PersonalizedData':
1184
- await self.send_image_data(message, 'ImageList', None, 'ImageData')
1185
- elif data_type == 'EANData':
1186
- await self.send_image_data(message, 'EANImageList', 'IsEarlyAccessFirmware', 'EANData')
1187
- elif data_type == 'BootabilityBundle':
1185
+ elif data_type == "SystemImageRootHash":
1186
+ await self.send_component("SystemVolume", data_type)
1187
+ elif data_type == "SystemImageCanonicalMetadata":
1188
+ await self.send_component("Ap,SystemVolumeCanonicalMetadata", data_type)
1189
+ elif data_type == "FUDData":
1190
+ await self.send_image_data(message, "FUDImageList", "IsFUDFirmware", "FUDImageData")
1191
+ elif data_type == "PersonalizedData":
1192
+ await self.send_image_data(message, "ImageList", None, "ImageData")
1193
+ elif data_type == "EANData":
1194
+ await self.send_image_data(message, "EANImageList", "IsEarlyAccessFirmware", "EANData")
1195
+ elif data_type == "BootabilityBundle":
1188
1196
  await self.send_bootability_bundle_data(message)
1189
- elif data_type == 'ReceiptManifest':
1197
+ elif data_type == "ReceiptManifest":
1190
1198
  await self.send_manifest()
1191
- elif data_type == 'BasebandUpdaterOutputData':
1199
+ elif data_type == "BasebandUpdaterOutputData":
1192
1200
  await self.handle_baseband_updater_output_data(message)
1193
- elif data_type == 'HostSystemTime':
1201
+ elif data_type == "HostSystemTime":
1194
1202
  await self.handle_host_system_time(message)
1195
1203
  else:
1196
- self.logger.error(f'unknown data request: {message}')
1204
+ self.logger.error(f"unknown data request: {message}")
1197
1205
 
1198
1206
  async def handle_previous_restore_log_msg(self, message: dict):
1199
- restorelog = message['PreviousRestoreLog']
1200
- self.logger.debug(f'PreviousRestoreLog: {restorelog}')
1207
+ restorelog = message["PreviousRestoreLog"]
1208
+ self.logger.debug(f"PreviousRestoreLog: {restorelog}")
1201
1209
 
1202
1210
  async def handle_progress_msg(self, message: dict) -> None:
1203
- operation = message['Operation']
1211
+ operation = message["Operation"]
1204
1212
  if operation in PROGRESS_BAR_OPERATIONS:
1205
- message['Operation'] = PROGRESS_BAR_OPERATIONS[operation]
1213
+ message["Operation"] = PROGRESS_BAR_OPERATIONS[operation]
1206
1214
 
1207
- if message['Operation'] == 'VERIFY_RESTORE':
1208
- progress = message['Progress']
1215
+ if message["Operation"] == "VERIFY_RESTORE":
1216
+ progress = message["Progress"]
1209
1217
 
1210
1218
  if self._pb_verify_restore is None:
1211
- self._pb_verify_restore = tqdm(total=100, desc='verify-restore', dynamic_ncols=True)
1219
+ self._pb_verify_restore = tqdm(total=100, desc="verify-restore", dynamic_ncols=True)
1212
1220
  self._pb_verify_restore_old_value = 0
1213
1221
 
1214
1222
  self._pb_verify_restore.update(progress - self._pb_verify_restore_old_value)
@@ -1220,79 +1228,78 @@ class Restore(BaseRestore):
1220
1228
 
1221
1229
  return
1222
1230
 
1223
- self.logger.debug(f'progress-bar: {message}')
1231
+ self.logger.debug(f"progress-bar: {message}")
1224
1232
 
1225
1233
  async def handle_status_msg(self, message: dict):
1226
- self.logger.debug(f'status message: {message}')
1227
- status = message['Status']
1228
- log = message.get('Log')
1234
+ self.logger.debug(f"status message: {message}")
1235
+ status = message["Status"]
1236
+ log = message.get("Log")
1229
1237
 
1230
1238
  if log:
1231
1239
  # this is the true device log that may inform us for anything that went wrong
1232
- self.logger.debug(f'log:\n{log}\n')
1240
+ self.logger.debug(f"log:\n{log}\n")
1233
1241
 
1234
1242
  if status == 0:
1235
1243
  self._restore_finished = True
1236
- await self._restored.send({'MsgType': 'ReceivedFinalStatusMsg'})
1244
+ await self._restored.send({"MsgType": "ReceivedFinalStatusMsg"})
1237
1245
  else:
1238
1246
  if status in known_errors:
1239
1247
  self.logger.error(known_errors[status])
1240
1248
  else:
1241
- self.logger.error('unknown error')
1249
+ self.logger.error("unknown error")
1242
1250
 
1243
1251
  async def handle_checkpoint_msg(self, message: dict):
1244
- self.logger.debug(f'checkpoint: {message}')
1252
+ self.logger.debug(f"checkpoint: {message}")
1245
1253
 
1246
1254
  async def handle_bb_update_status_msg(self, message: dict):
1247
- self.logger.debug(f'bb_update_status_msg: {message}')
1248
- if not message['Accepted']:
1255
+ self.logger.debug(f"bb_update_status_msg: {message}")
1256
+ if not message["Accepted"]:
1249
1257
  raise PyMobileDevice3Exception(str(message))
1250
1258
 
1251
1259
  async def handle_baseband_updater_output_data(self, message: dict) -> None:
1252
- self.logger.debug(f'restore_handle_baseband_updater_output_data: {message}')
1253
- data_port = message['DataPort']
1260
+ self.logger.debug(f"restore_handle_baseband_updater_output_data: {message}")
1261
+ data_port = message["DataPort"]
1254
1262
 
1255
- self.logger.info('Connecting to baseband updater data port')
1263
+ self.logger.info("Connecting to baseband updater data port")
1256
1264
 
1257
1265
  while True:
1258
1266
  try:
1259
- client = ServiceConnection.create_using_usbmux(self._restored.udid, data_port,
1260
- connection_type='USB')
1267
+ client = ServiceConnection.create_using_usbmux(self._restored.udid, data_port, connection_type="USB")
1261
1268
  break
1262
1269
  except ConnectionFailedError:
1263
- self.logger.debug('Retrying connection...')
1270
+ self.logger.debug("Retrying connection...")
1264
1271
 
1265
1272
  if not client:
1266
- raise ConnectionFailedError(f'failed to establish connection to {data_port}')
1273
+ raise ConnectionFailedError(f"failed to establish connection to {data_port}")
1267
1274
 
1268
- self.logger.info('Connected to BasebandUpdaterOutputData data port')
1275
+ self.logger.info("Connected to BasebandUpdaterOutputData data port")
1269
1276
 
1270
- filename = f'updater_output-{self._restored.udid}.cpio'
1271
- self.logger.info(f'Writing updater output into: {filename}')
1277
+ filename = f"updater_output-{self._restored.udid}.cpio"
1278
+ self.logger.info(f"Writing updater output into: {filename}")
1272
1279
 
1273
- with open(filename, 'wb') as f:
1280
+ with open(filename, "wb") as f:
1274
1281
  while True:
1275
1282
  buf = client.recv()
1276
1283
  if not buf:
1277
1284
  break
1278
1285
  f.write(buf)
1279
1286
 
1280
- self.logger.debug('Closing connection of BasebandUpdaterOutputData data port')
1287
+ self.logger.debug("Closing connection of BasebandUpdaterOutputData data port")
1281
1288
  client.close()
1282
1289
 
1283
1290
  async def handle_host_system_time(self, message: dict) -> None:
1284
- await self._restored.send({'SetHostTimeOnDevice': time.time()})
1291
+ await self._restored.send({"SetHostTimeOnDevice": time.time()})
1285
1292
 
1286
1293
  async def handle_restored_crash(self, message: dict) -> None:
1287
- backtrace = '\n'.join(message['RestoredBacktrace'])
1288
- self.logger.info(f'restored crashed. backtrace:\n{backtrace}')
1294
+ backtrace = "\n".join(message["RestoredBacktrace"])
1295
+ self.logger.info(f"restored crashed. backtrace:\n{backtrace}")
1289
1296
 
1290
1297
  async def handle_async_wait(self, message: dict) -> None:
1291
1298
  self.logger.debug(message)
1292
1299
 
1293
1300
  async def handle_restore_attestation(self, message: dict) -> None:
1294
1301
  self.logger.debug(message)
1295
- await self._restored.send({'RestoreShouldAttest': False})
1302
+ await self._restored.send({"RestoreShouldAttest": False})
1296
1303
 
1297
1304
  async def _connect_to_restored_service(self):
1298
1305
  while True:
@@ -1303,33 +1310,38 @@ class Restore(BaseRestore):
1303
1310
  await asyncio.sleep(1)
1304
1311
 
1305
1312
  async def restore_device(self) -> None:
1306
- self.logger.debug('waiting for device to connect for restored service')
1313
+ self.logger.debug("waiting for device to connect for restored service")
1307
1314
  await self._connect_to_restored_service()
1308
1315
 
1309
- self.logger.info(f'hardware info: {self._restored.hardware_info}')
1310
- self.logger.info(f'version: {self._restored.version}')
1311
- self.logger.info(f'saved_debug_info: {self._restored.saved_debug_info}')
1316
+ self.logger.info(f"hardware info: {self._restored.hardware_info}")
1317
+ self.logger.info(f"version: {self._restored.version}")
1318
+ self.logger.info(f"saved_debug_info: {self._restored.saved_debug_info}")
1312
1319
 
1313
1320
  if self.recovery.tss.bb_ticket is not None:
1314
1321
  # initial TSS response contains a baseband ticket
1315
1322
  self.bbtss = self.recovery.tss
1316
1323
 
1317
1324
  if self._ignore_fdr:
1318
- self.logger.info('Establishing a mock FDR listener')
1325
+ self.logger.info("Establishing a mock FDR listener")
1319
1326
  self._fdr = ServiceConnection.create_using_usbmux(
1320
- self._restored.udid, FDRClient.SERVICE_PORT, connection_type='USB'
1327
+ self._restored.udid, FDRClient.SERVICE_PORT, connection_type="USB"
1321
1328
  )
1322
1329
  else:
1323
- self.logger.info('Starting FDR listener thread')
1330
+ self.logger.info("Starting FDR listener thread")
1324
1331
  start_fdr_thread(fdr_type.FDR_CTRL)
1325
1332
 
1326
- sep = self.build_identity['Manifest']['SEP'].get('Info')
1327
- spp = self.build_identity['Info'].get('SystemPartitionPadding')
1328
- opts = RestoreOptions(firmware_preflight_info=self._firmware_preflight_info, sep=sep,
1329
- macos_variant=self.macos_variant,
1330
- build_identity=self.build_identity, restore_boot_args=self.recovery.restore_boot_args,
1331
- spp=spp, restore_behavior=self.build_identity.restore_behavior,
1332
- msp=self.build_identity.minimum_system_partition)
1333
+ sep = self.build_identity["Manifest"]["SEP"].get("Info")
1334
+ spp = self.build_identity["Info"].get("SystemPartitionPadding")
1335
+ opts = RestoreOptions(
1336
+ firmware_preflight_info=self._firmware_preflight_info,
1337
+ sep=sep,
1338
+ macos_variant=self.macos_variant,
1339
+ build_identity=self.build_identity,
1340
+ restore_boot_args=self.recovery.restore_boot_args,
1341
+ spp=spp,
1342
+ restore_behavior=self.build_identity.restore_behavior,
1343
+ msp=self.build_identity.minimum_system_partition,
1344
+ )
1333
1345
 
1334
1346
  # start the restore process
1335
1347
  await self._restored.start_restore(opts)
@@ -1342,17 +1354,17 @@ class Restore(BaseRestore):
1342
1354
  message = await self._restored.recv()
1343
1355
 
1344
1356
  # discover what kind of message has been received
1345
- message_type = message.get('MsgType')
1357
+ message_type = message.get("MsgType")
1346
1358
 
1347
1359
  if message_type in self._handlers:
1348
1360
  try:
1349
1361
  await self._handlers[message_type](message)
1350
1362
  except Exception:
1351
- self.logger.error(traceback.format_exc())
1363
+ self.logger.exception(f"Failed to handle {message_type}")
1352
1364
  else:
1353
1365
  # there might be some other message types i'm not aware of, but I think
1354
1366
  # at least the "previous error logs" messages usually end up here
1355
- self.logger.debug(f'unhandled message type received: {message}')
1367
+ self.logger.debug(f"unhandled message type received: {message}")
1356
1368
 
1357
1369
  async def update(self):
1358
1370
  await self.recovery.boot_ramdisk()
@@ -1361,24 +1373,24 @@ class Restore(BaseRestore):
1361
1373
  await self.restore_device()
1362
1374
 
1363
1375
  async def _get_service_for_data_request(self, message: dict) -> ServiceConnection:
1364
- data_port = message.get('DataPort')
1376
+ data_port = message.get("DataPort")
1365
1377
  if data_port is None:
1366
1378
  return self._restored.service
1367
- data_type = message['DataType']
1368
- data_port = message['DataPort']
1379
+ data_type = message["DataType"]
1380
+ data_port = message["DataPort"]
1369
1381
 
1370
- self.logger.info(f'Connecting to {data_type} data port ({data_port})')
1382
+ self.logger.info(f"Connecting to {data_type} data port ({data_port})")
1371
1383
 
1372
1384
  while True:
1373
1385
  try:
1374
- service = ServiceConnection.create_using_usbmux(self._restored.udid, data_port, connection_type='USB')
1386
+ service = ServiceConnection.create_using_usbmux(self._restored.udid, data_port, connection_type="USB")
1375
1387
  break
1376
1388
  except ConnectionFailedError:
1377
- self.logger.debug('Retrying connection...')
1389
+ self.logger.debug("Retrying connection...")
1378
1390
 
1379
1391
  if not service:
1380
- raise ConnectionFailedError(f'failed to establish connection to {data_port}')
1392
+ raise ConnectionFailedError(f"failed to establish connection to {data_port}")
1381
1393
 
1382
- self.logger.info(f'Connected to {data_type} data port ({data_port})')
1394
+ self.logger.info(f"Connected to {data_type} data port ({data_port})")
1383
1395
  await service.aio_start()
1384
1396
  return service