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