pymobiledevice3 4.27.4__py3-none-any.whl → 5.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. misc/plist_sniffer.py +15 -15
  2. misc/remotexpc_sniffer.py +29 -28
  3. pymobiledevice3/__main__.py +123 -98
  4. pymobiledevice3/_version.py +2 -2
  5. pymobiledevice3/bonjour.py +351 -117
  6. pymobiledevice3/ca.py +32 -24
  7. pymobiledevice3/cli/activation.py +7 -7
  8. pymobiledevice3/cli/afc.py +19 -19
  9. pymobiledevice3/cli/amfi.py +4 -4
  10. pymobiledevice3/cli/apps.py +51 -39
  11. pymobiledevice3/cli/backup.py +58 -32
  12. pymobiledevice3/cli/bonjour.py +27 -20
  13. pymobiledevice3/cli/cli_common.py +112 -81
  14. pymobiledevice3/cli/companion_proxy.py +4 -4
  15. pymobiledevice3/cli/completions.py +10 -10
  16. pymobiledevice3/cli/crash.py +37 -31
  17. pymobiledevice3/cli/developer.py +601 -519
  18. pymobiledevice3/cli/diagnostics.py +38 -33
  19. pymobiledevice3/cli/lockdown.py +82 -72
  20. pymobiledevice3/cli/mounter.py +84 -67
  21. pymobiledevice3/cli/notification.py +10 -10
  22. pymobiledevice3/cli/pcap.py +19 -14
  23. pymobiledevice3/cli/power_assertion.py +12 -10
  24. pymobiledevice3/cli/processes.py +10 -10
  25. pymobiledevice3/cli/profile.py +88 -77
  26. pymobiledevice3/cli/provision.py +17 -17
  27. pymobiledevice3/cli/remote.py +188 -111
  28. pymobiledevice3/cli/restore.py +43 -40
  29. pymobiledevice3/cli/springboard.py +30 -28
  30. pymobiledevice3/cli/syslog.py +85 -58
  31. pymobiledevice3/cli/usbmux.py +21 -20
  32. pymobiledevice3/cli/version.py +3 -2
  33. pymobiledevice3/cli/webinspector.py +156 -78
  34. pymobiledevice3/common.py +1 -1
  35. pymobiledevice3/exceptions.py +154 -60
  36. pymobiledevice3/irecv.py +49 -53
  37. pymobiledevice3/irecv_devices.py +1489 -492
  38. pymobiledevice3/lockdown.py +400 -251
  39. pymobiledevice3/lockdown_service_provider.py +5 -7
  40. pymobiledevice3/osu/os_utils.py +18 -9
  41. pymobiledevice3/osu/posix_util.py +28 -15
  42. pymobiledevice3/osu/win_util.py +14 -8
  43. pymobiledevice3/pair_records.py +19 -19
  44. pymobiledevice3/remote/common.py +4 -4
  45. pymobiledevice3/remote/core_device/app_service.py +94 -67
  46. pymobiledevice3/remote/core_device/core_device_service.py +17 -14
  47. pymobiledevice3/remote/core_device/device_info.py +5 -5
  48. pymobiledevice3/remote/core_device/diagnostics_service.py +10 -8
  49. pymobiledevice3/remote/core_device/file_service.py +47 -33
  50. pymobiledevice3/remote/remote_service_discovery.py +53 -35
  51. pymobiledevice3/remote/remotexpc.py +64 -42
  52. pymobiledevice3/remote/tunnel_service.py +383 -297
  53. pymobiledevice3/remote/utils.py +14 -13
  54. pymobiledevice3/remote/xpc_message.py +145 -125
  55. pymobiledevice3/resources/dsc_uuid_map.py +19 -19
  56. pymobiledevice3/resources/firmware_notifications.py +16 -16
  57. pymobiledevice3/restore/asr.py +27 -27
  58. pymobiledevice3/restore/base_restore.py +90 -47
  59. pymobiledevice3/restore/consts.py +87 -66
  60. pymobiledevice3/restore/device.py +11 -11
  61. pymobiledevice3/restore/fdr.py +46 -46
  62. pymobiledevice3/restore/ftab.py +19 -19
  63. pymobiledevice3/restore/img4.py +130 -133
  64. pymobiledevice3/restore/mbn.py +587 -0
  65. pymobiledevice3/restore/recovery.py +125 -135
  66. pymobiledevice3/restore/restore.py +535 -523
  67. pymobiledevice3/restore/restore_options.py +122 -115
  68. pymobiledevice3/restore/restored_client.py +25 -22
  69. pymobiledevice3/restore/tss.py +378 -270
  70. pymobiledevice3/service_connection.py +50 -46
  71. pymobiledevice3/services/accessibilityaudit.py +137 -127
  72. pymobiledevice3/services/afc.py +352 -292
  73. pymobiledevice3/services/amfi.py +21 -18
  74. pymobiledevice3/services/companion.py +23 -19
  75. pymobiledevice3/services/crash_reports.py +61 -47
  76. pymobiledevice3/services/debugserver_applist.py +3 -3
  77. pymobiledevice3/services/device_arbitration.py +8 -8
  78. pymobiledevice3/services/device_link.py +56 -48
  79. pymobiledevice3/services/diagnostics.py +971 -968
  80. pymobiledevice3/services/dtfetchsymbols.py +8 -8
  81. pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py +4 -4
  82. pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py +4 -4
  83. pymobiledevice3/services/dvt/instruments/activity_trace_tap.py +85 -74
  84. pymobiledevice3/services/dvt/instruments/application_listing.py +2 -3
  85. pymobiledevice3/services/dvt/instruments/condition_inducer.py +7 -6
  86. pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +466 -384
  87. pymobiledevice3/services/dvt/instruments/device_info.py +11 -11
  88. pymobiledevice3/services/dvt/instruments/energy_monitor.py +1 -1
  89. pymobiledevice3/services/dvt/instruments/graphics.py +1 -1
  90. pymobiledevice3/services/dvt/instruments/location_simulation.py +1 -1
  91. pymobiledevice3/services/dvt/instruments/location_simulation_base.py +10 -10
  92. pymobiledevice3/services/dvt/instruments/network_monitor.py +17 -17
  93. pymobiledevice3/services/dvt/instruments/notifications.py +1 -1
  94. pymobiledevice3/services/dvt/instruments/process_control.py +25 -10
  95. pymobiledevice3/services/dvt/instruments/screenshot.py +2 -2
  96. pymobiledevice3/services/dvt/instruments/sysmontap.py +15 -15
  97. pymobiledevice3/services/dvt/testmanaged/xcuitest.py +42 -52
  98. pymobiledevice3/services/file_relay.py +10 -10
  99. pymobiledevice3/services/heartbeat.py +8 -7
  100. pymobiledevice3/services/house_arrest.py +12 -15
  101. pymobiledevice3/services/installation_proxy.py +119 -100
  102. pymobiledevice3/services/lockdown_service.py +12 -5
  103. pymobiledevice3/services/misagent.py +22 -19
  104. pymobiledevice3/services/mobile_activation.py +84 -72
  105. pymobiledevice3/services/mobile_config.py +331 -301
  106. pymobiledevice3/services/mobile_image_mounter.py +137 -116
  107. pymobiledevice3/services/mobilebackup2.py +188 -150
  108. pymobiledevice3/services/notification_proxy.py +11 -11
  109. pymobiledevice3/services/os_trace.py +128 -74
  110. pymobiledevice3/services/pcapd.py +306 -306
  111. pymobiledevice3/services/power_assertion.py +10 -9
  112. pymobiledevice3/services/preboard.py +4 -4
  113. pymobiledevice3/services/remote_fetch_symbols.py +16 -14
  114. pymobiledevice3/services/remote_server.py +176 -146
  115. pymobiledevice3/services/restore_service.py +16 -16
  116. pymobiledevice3/services/screenshot.py +13 -10
  117. pymobiledevice3/services/simulate_location.py +7 -7
  118. pymobiledevice3/services/springboard.py +15 -15
  119. pymobiledevice3/services/syslog.py +5 -5
  120. pymobiledevice3/services/web_protocol/alert.py +3 -3
  121. pymobiledevice3/services/web_protocol/automation_session.py +183 -179
  122. pymobiledevice3/services/web_protocol/cdp_screencast.py +44 -36
  123. pymobiledevice3/services/web_protocol/cdp_server.py +19 -19
  124. pymobiledevice3/services/web_protocol/cdp_target.py +411 -373
  125. pymobiledevice3/services/web_protocol/driver.py +47 -45
  126. pymobiledevice3/services/web_protocol/element.py +74 -63
  127. pymobiledevice3/services/web_protocol/inspector_session.py +106 -102
  128. pymobiledevice3/services/web_protocol/selenium_api.py +3 -3
  129. pymobiledevice3/services/web_protocol/session_protocol.py +15 -10
  130. pymobiledevice3/services/web_protocol/switch_to.py +11 -12
  131. pymobiledevice3/services/webinspector.py +142 -116
  132. pymobiledevice3/tcp_forwarder.py +35 -22
  133. pymobiledevice3/tunneld/api.py +20 -15
  134. pymobiledevice3/tunneld/server.py +310 -193
  135. pymobiledevice3/usbmux.py +197 -148
  136. pymobiledevice3/utils.py +14 -11
  137. {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/METADATA +1 -2
  138. pymobiledevice3-5.1.2.dist-info/RECORD +173 -0
  139. pymobiledevice3-4.27.4.dist-info/RECORD +0 -172
  140. {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/WHEEL +0 -0
  141. {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/entry_points.txt +0 -0
  142. {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/licenses/LICENSE +0 -0
  143. {pymobiledevice3-4.27.4.dist-info → pymobiledevice3-5.1.2.dist-info}/top_level.txt +0 -0
@@ -8,48 +8,54 @@ from uuid import uuid4
8
8
  import asn1
9
9
  import requests
10
10
 
11
- from pymobiledevice3.exceptions import PyMobileDevice3Exception
11
+ from pymobiledevice3.exceptions import PyMobileDevice3Exception, TSSError
12
12
  from pymobiledevice3.restore.img4 import COMPONENT_FOURCC
13
13
  from pymobiledevice3.utils import bytes_to_uint, plist_access_path
14
14
 
15
- TSS_CONTROLLER_ACTION_URL = 'http://gs.apple.com/TSS/controller?action=2'
15
+ TSS_CONTROLLER_ACTION_URL = "http://gs.apple.com/TSS/controller?action=2"
16
16
 
17
- TSS_CLIENT_VERSION_STRING = 'libauthinstall-1104.0.9'
17
+ TSS_CLIENT_VERSION_STRING = "libauthinstall-1104.0.9"
18
18
 
19
19
  logger = logging.getLogger(__name__)
20
20
 
21
21
 
22
22
  def get_with_or_without_comma(obj: dict, k: str, default=None):
23
- val = obj.get(k, obj.get(k.replace(',', '')))
23
+ val = obj.get(k, obj.get(k.replace(",", "")))
24
24
  if val is None and default is not None:
25
25
  val = default
26
26
  return val
27
27
 
28
28
 
29
29
  def is_fw_payload(info: dict[str, typing.Any]) -> bool:
30
- return (info.get('IsFirmwarePayload') or info.get('IsSecondaryFirmwarePayload') or info.get('IsFUDFirmware') or
31
- info.get('IsLoadedByiBoot') or info.get('IsEarlyAccessFirmware') or info.get('IsiBootEANFirmware') or
32
- info.get('IsiBootNonEssentialFirmware'))
30
+ return (
31
+ info.get("IsFirmwarePayload")
32
+ or info.get("IsSecondaryFirmwarePayload")
33
+ or info.get("IsFUDFirmware")
34
+ or info.get("IsLoadedByiBoot")
35
+ or info.get("IsEarlyAccessFirmware")
36
+ or info.get("IsiBootEANFirmware")
37
+ or info.get("IsiBootNonEssentialFirmware")
38
+ )
33
39
 
34
40
 
35
41
  class TSSResponse(dict):
36
42
  @property
37
43
  def ap_img4_ticket(self):
38
- ticket = self.get('ApImg4Ticket')
44
+ ticket = self.get("ApImg4Ticket")
39
45
 
40
46
  if ticket is None:
41
- raise PyMobileDevice3Exception('TSS response doesn\'t contain a ApImg4Ticket')
47
+ raise PyMobileDevice3Exception("TSS response doesn't contain a ApImg4Ticket")
42
48
 
43
49
  return ticket
44
50
 
45
51
  @property
46
52
  def bb_ticket(self):
47
- return self.get('BBTicket')
53
+ return self.get("BBTicket")
48
54
 
49
55
  def get_path_by_entry(self, component: str):
50
56
  node = self.get(component)
51
57
  if node is not None:
52
- return node.get('Path')
58
+ return node.get("Path")
53
59
 
54
60
  return None
55
61
 
@@ -57,62 +63,57 @@ class TSSResponse(dict):
57
63
  class TSSRequest:
58
64
  def __init__(self):
59
65
  self._request: dict[str, typing.Any] = {
60
- '@HostPlatformInfo': 'mac',
61
- '@VersionInfo': TSS_CLIENT_VERSION_STRING,
62
- '@UUID': str(uuid4()).upper(),
66
+ "@HostPlatformInfo": "mac",
67
+ "@VersionInfo": TSS_CLIENT_VERSION_STRING,
68
+ "@UUID": str(uuid4()).upper(),
63
69
  }
64
70
 
65
71
  @staticmethod
66
72
  def apply_restore_request_rules(tss_entry: dict, parameters: dict, rules: list) -> dict:
67
73
  for rule in rules:
68
74
  conditions_fulfilled = True
69
- conditions = rule['Conditions']
75
+ conditions = rule["Conditions"]
70
76
  for key, value in conditions.items():
71
77
  if not conditions_fulfilled:
72
78
  break
73
79
 
74
- if key == 'ApRawProductionMode':
75
- value2 = parameters.get('ApProductionMode')
76
- elif key == 'ApCurrentProductionMode':
77
- value2 = parameters.get('ApProductionMode')
78
- elif key == 'ApRawSecurityMode':
79
- value2 = parameters.get('ApSecurityMode')
80
- elif key == 'ApRequiresImage4':
81
- value2 = parameters.get('ApSupportsImg4')
82
- elif key == 'ApDemotionPolicyOverride':
83
- value2 = parameters.get('DemotionPolicy')
84
- elif key == 'ApInRomDFU':
85
- value2 = parameters.get('ApInRomDFU')
80
+ if key == "ApRawProductionMode" or key == "ApCurrentProductionMode":
81
+ value2 = parameters.get("ApProductionMode")
82
+ elif key == "ApRawSecurityMode":
83
+ value2 = parameters.get("ApSecurityMode")
84
+ elif key == "ApRequiresImage4":
85
+ value2 = parameters.get("ApSupportsImg4")
86
+ elif key == "ApDemotionPolicyOverride":
87
+ value2 = parameters.get("DemotionPolicy")
88
+ elif key == "ApInRomDFU":
89
+ value2 = parameters.get("ApInRomDFU")
86
90
  else:
87
- logger.error(f'Unhandled condition {key} while parsing RestoreRequestRules')
91
+ logger.error(f"Unhandled condition {key} while parsing RestoreRequestRules")
88
92
  value2 = None
89
93
 
90
- if value2:
91
- conditions_fulfilled = value == value2
92
- else:
93
- conditions_fulfilled = False
94
+ conditions_fulfilled = value == value2 if value2 else False
94
95
 
95
96
  if not conditions_fulfilled:
96
97
  continue
97
98
 
98
- actions = rule['Actions']
99
+ actions = rule["Actions"]
99
100
  for key, value in actions.items():
100
101
  if value != 255:
101
102
  value2 = tss_entry.get(key)
102
103
  if value2:
103
104
  tss_entry.pop(key)
104
- logger.debug(f'Adding {key}={value} to TSS entry')
105
+ logger.debug(f"Adding {key}={value} to TSS entry")
105
106
  tss_entry[key] = value
106
107
  return tss_entry
107
108
 
108
109
  def add_tags(self, parameters: dict):
109
110
  for key, value in parameters.items():
110
- if isinstance(value, str) and value.startswith('0x'):
111
+ if isinstance(value, str) and value.startswith("0x"):
111
112
  value = int(value, 16)
112
113
  self._request[key] = value
113
114
 
114
115
  def add_common_tags(self, parameters: dict, overrides=None):
115
- keys = ('ApECID', 'UniqueBuildID', 'ApChipID', 'ApBoardID', 'ApSecurityDomain')
116
+ keys = ("ApECID", "UniqueBuildID", "ApChipID", "ApBoardID", "ApSecurityDomain")
116
117
  for k in keys:
117
118
  if k in parameters:
118
119
  self._request[k] = parameters[k]
@@ -120,47 +121,73 @@ class TSSRequest:
120
121
  self._request.update(overrides)
121
122
 
122
123
  def add_ap_recovery_tags(self, parameters: dict, overrides=None):
123
- skip_keys = ('BasebandFirmware', 'SE,UpdatePayload', 'BaseSystem', 'ANS', 'Ap,AudioBootChime', 'Ap,CIO',
124
- 'Ap,RestoreCIO', 'Ap,RestoreTMU', 'Ap,TMU', 'Ap,rOSLogo1', 'Ap,rOSLogo2', 'AppleLogo', 'DCP',
125
- 'LLB', 'RecoveryMode', 'RestoreANS', 'RestoreDCP', 'RestoreDeviceTree', 'RestoreKernelCache',
126
- 'RestoreLogo', 'RestoreRamDisk', 'RestoreSEP', 'SEP', 'ftap', 'ftsp', 'iBEC', 'iBSS', 'rfta',
127
- 'rfts', 'Diags')
124
+ skip_keys = (
125
+ "BasebandFirmware",
126
+ "SE,UpdatePayload",
127
+ "BaseSystem",
128
+ "ANS",
129
+ "Ap,AudioBootChime",
130
+ "Ap,CIO",
131
+ "Ap,RestoreCIO",
132
+ "Ap,RestoreTMU",
133
+ "Ap,TMU",
134
+ "Ap,rOSLogo1",
135
+ "Ap,rOSLogo2",
136
+ "AppleLogo",
137
+ "DCP",
138
+ "LLB",
139
+ "RecoveryMode",
140
+ "RestoreANS",
141
+ "RestoreDCP",
142
+ "RestoreDeviceTree",
143
+ "RestoreKernelCache",
144
+ "RestoreLogo",
145
+ "RestoreRamDisk",
146
+ "RestoreSEP",
147
+ "SEP",
148
+ "ftap",
149
+ "ftsp",
150
+ "iBEC",
151
+ "iBSS",
152
+ "rfta",
153
+ "rfts",
154
+ "Diags",
155
+ )
128
156
 
129
157
  # add components to request
130
- manifest = parameters['Manifest']
158
+ manifest = parameters["Manifest"]
131
159
  for key, manifest_entry in manifest.items():
132
160
  if key in skip_keys:
133
161
  continue
134
- info = manifest_entry.get('Info')
162
+ info = manifest_entry.get("Info")
135
163
  if not info:
136
164
  continue
137
- if parameters.get('_OnlyFWComponents', False):
138
- if not manifest_entry.get('Trusted', False):
139
- logger.debug(f'skipping {key} as it is not trusted')
165
+ if parameters.get("_OnlyFWComponents", False):
166
+ if not manifest_entry.get("Trusted", False):
167
+ logger.debug(f"skipping {key} as it is not trusted")
140
168
  continue
141
- info = manifest_entry['Info']
169
+ info = manifest_entry["Info"]
142
170
  if not is_fw_payload(info):
143
- logger.debug(f'skipping {key} as it is not a firmware payload')
171
+ logger.debug(f"skipping {key} as it is not a firmware payload")
144
172
  continue
145
173
 
146
174
  # copy this entry
147
175
  tss_entry = dict(manifest_entry)
148
176
 
149
177
  # remove obsolete Info node
150
- tss_entry.pop('Info')
178
+ tss_entry.pop("Info")
151
179
 
152
180
  # handle RestoreRequestRules
153
- if 'Info' in manifest_entry and 'RestoreRequestRules' in manifest_entry['Info']:
154
- rules = manifest_entry['Info']['RestoreRequestRules']
181
+ if "Info" in manifest_entry and "RestoreRequestRules" in manifest_entry["Info"]:
182
+ rules = manifest_entry["Info"]["RestoreRequestRules"]
155
183
  if rules:
156
- logger.debug(f'Applying restore request rules for entry {key}')
184
+ logger.debug(f"Applying restore request rules for entry {key}")
157
185
  tss_entry = self.apply_restore_request_rules(tss_entry, parameters, rules)
158
186
 
159
187
  # Make sure we have a Digest key for Trusted items even if empty
160
- node = manifest_entry.get('Trusted', False)
161
- if node:
162
- if manifest_entry.get('Digest') is None:
163
- tss_entry['Digest'] = b''
188
+ node = manifest_entry.get("Trusted", False)
189
+ if node and manifest_entry.get("Digest") is None:
190
+ tss_entry["Digest"] = b""
164
191
 
165
192
  self._request[key] = tss_entry
166
193
 
@@ -168,18 +195,22 @@ class TSSRequest:
168
195
  self._request.update(overrides)
169
196
 
170
197
  def add_timer_tags(self, parameters: dict, overrides=None):
171
- manifest = parameters['Manifest']
198
+ manifest = parameters["Manifest"]
172
199
 
173
200
  # add tags indicating we want to get the Timer ticket
174
- self._request['@BBTicket'] = True
201
+ self._request["@BBTicket"] = True
175
202
 
176
- key = f'@{parameters["TicketName"]}'
203
+ key = f"@{parameters['TicketName']}"
177
204
  self._request[key] = True
178
205
 
179
- tag = parameters['TagNumber']
206
+ tag = parameters["TagNumber"]
180
207
 
181
- keys_to_copy_uint = (f'Timer,BoardID,{tag}', f'Timer,ChipID,{tag}', f'Timer,SecurityDomain,{tag}',
182
- f'Timer,ECID,{tag}',)
208
+ keys_to_copy_uint = (
209
+ f"Timer,BoardID,{tag}",
210
+ f"Timer,ChipID,{tag}",
211
+ f"Timer,SecurityDomain,{tag}",
212
+ f"Timer,ECID,{tag}",
213
+ )
183
214
 
184
215
  for key in keys_to_copy_uint:
185
216
  value = parameters.get(key)
@@ -189,38 +220,41 @@ class TSSRequest:
189
220
  else:
190
221
  self._request[key] = value
191
222
 
192
- keys_to_copy_bool = (f'Timer,ProductionMode,{tag}', f'Timer,SecurityMode,{tag}',)
223
+ keys_to_copy_bool = (
224
+ f"Timer,ProductionMode,{tag}",
225
+ f"Timer,SecurityMode,{tag}",
226
+ )
193
227
 
194
228
  for key in keys_to_copy_bool:
195
229
  value = parameters.get(key)
196
230
  self._request[key] = bytes_to_uint(value) == 1
197
231
 
198
- nonce = parameters.get(parameters, f'Timer,Nonce,{tag}')
232
+ nonce = parameters.get(parameters, f"Timer,Nonce,{tag}")
199
233
 
200
234
  if nonce is not None:
201
- self._request[f'Timer,Nonce,{tag}'] = nonce
235
+ self._request[f"Timer,Nonce,{tag}"] = nonce
202
236
 
203
237
  for comp_name, node in manifest.items():
204
- if not comp_name.startswith('Timer,'):
238
+ if not comp_name.startswith("Timer,"):
205
239
  continue
206
240
 
207
241
  manifest_entry = dict(node)
208
242
 
209
243
  # handle RestoreRequestRules
210
- rules = manifest_entry['Info'].get('RestoreRequestRules')
244
+ rules = manifest_entry["Info"].get("RestoreRequestRules")
211
245
  if rules is not None:
212
246
  self.apply_restore_request_rules(manifest_entry, parameters, rules)
213
247
 
214
248
  # Make sure we have a Digest key for Trusted items even if empty
215
- trusted = manifest_entry.get('Trusted', False)
249
+ trusted = manifest_entry.get("Trusted", False)
216
250
 
217
251
  if trusted:
218
- digest = manifest_entry.get('Digest')
252
+ digest = manifest_entry.get("Digest")
219
253
  if digest is None:
220
- logger.debug(f'No Digest data, using empty value for entry {comp_name}')
221
- manifest_entry['Digest'] = b''
254
+ logger.debug(f"No Digest data, using empty value for entry {comp_name}")
255
+ manifest_entry["Digest"] = b""
222
256
 
223
- manifest_entry.pop('Info')
257
+ manifest_entry.pop("Info")
224
258
 
225
259
  # finally add entry to request
226
260
  self._request[comp_name] = manifest_entry
@@ -229,111 +263,122 @@ class TSSRequest:
229
263
  self._request.update(overrides)
230
264
 
231
265
  def add_local_policy_tags(self, parameters: dict):
232
- self._request['@ApImg4Ticket'] = True
266
+ self._request["@ApImg4Ticket"] = True
233
267
 
234
268
  keys_to_copy = (
235
- 'Ap,LocalBoot', 'Ap,LocalPolicy', 'Ap,NextStageIM4MHash', 'Ap,RecoveryOSPolicyNonceHash', 'Ap,VolumeUUID',
236
- 'ApECID', 'ApChipID', 'ApBoardID', 'ApSecurityDomain', 'ApNonce', 'ApSecurityMode', 'ApProductionMode')
269
+ "Ap,LocalBoot",
270
+ "Ap,LocalPolicy",
271
+ "Ap,NextStageIM4MHash",
272
+ "Ap,RecoveryOSPolicyNonceHash",
273
+ "Ap,VolumeUUID",
274
+ "ApECID",
275
+ "ApChipID",
276
+ "ApBoardID",
277
+ "ApSecurityDomain",
278
+ "ApNonce",
279
+ "ApSecurityMode",
280
+ "ApProductionMode",
281
+ )
237
282
 
238
283
  for k in keys_to_copy:
239
284
  if k in parameters:
240
285
  v = parameters[k]
241
- if isinstance(v, str) and v.startswith('0x'):
286
+ if isinstance(v, str) and v.startswith("0x"):
242
287
  v = int(v, 16)
243
288
  self._request[k] = v
244
289
 
245
290
  def add_vinyl_tags(self, parameters: dict, overrides=None):
246
- self._request['@BBTicket'] = True
247
- self._request['@eUICC,Ticket'] = True
291
+ self._request["@BBTicket"] = True
292
+ self._request["@eUICC,Ticket"] = True
248
293
 
249
- self._request['eUICC,ApProductionMode'] = parameters.get('eUICC,ApProductionMode',
250
- parameters.get('ApProductionMode'))
294
+ self._request["eUICC,ApProductionMode"] = parameters.get(
295
+ "eUICC,ApProductionMode", parameters.get("ApProductionMode")
296
+ )
251
297
 
252
- keys = ('eUICC,ChipID', 'eUICC,EID', 'eUICC,RootKeyIdentifier')
298
+ keys = ("eUICC,ChipID", "eUICC,EID", "eUICC,RootKeyIdentifier")
253
299
  for k in keys:
254
300
  if k in parameters:
255
301
  self._request[k] = parameters[k]
256
302
 
257
- if self._request.get('eUICC,Gold') is None:
258
- n = plist_access_path(parameters, ('Manifest', 'eUICC,Gold'))
303
+ if self._request.get("eUICC,Gold") is None:
304
+ n = plist_access_path(parameters, ("Manifest", "eUICC,Gold"))
259
305
  if n:
260
- self._request['eUICC,Gold'] = {'Digest': n['Digest']}
306
+ self._request["eUICC,Gold"] = {"Digest": n["Digest"]}
261
307
 
262
- if self._request.get('eUICC,Main') is None:
263
- n = plist_access_path(parameters, ('Manifest', 'eUICC,Main'))
308
+ if self._request.get("eUICC,Main") is None:
309
+ n = plist_access_path(parameters, ("Manifest", "eUICC,Main"))
264
310
  if n:
265
- self._request['eUICC,Main'] = {'Digest': n['Digest']}
311
+ self._request["eUICC,Main"] = {"Digest": n["Digest"]}
266
312
 
267
313
  # set Nonce for eUICC,Gold component
268
- node = parameters.get('EUICCGoldNonce')
314
+ node = parameters.get("EUICCGoldNonce")
269
315
  if node is not None:
270
- n = self._request.get('eUICC,Gold')
316
+ n = self._request.get("eUICC,Gold")
271
317
  if n is not None:
272
- n['Nonce'] = node
318
+ n["Nonce"] = node
273
319
 
274
320
  # set Nonce for eUICC,Main component
275
- node = parameters.get('EUICCMainNonce')
321
+ node = parameters.get("EUICCMainNonce")
276
322
  if node is not None:
277
- n = self._request.get('eUICC,Main')
323
+ n = self._request.get("eUICC,Main")
278
324
  if n is not None:
279
- n['Nonce'] = node
325
+ n["Nonce"] = node
280
326
 
281
327
  if overrides is not None:
282
328
  self._request.update(overrides)
283
329
 
284
330
  def add_ap_tags(self, parameters: dict, overrides=None):
285
- """ loop over components from build manifest """
331
+ """loop over components from build manifest"""
286
332
 
287
- manifest_node = parameters['Manifest']
333
+ manifest_node = parameters["Manifest"]
288
334
 
289
335
  # add components to request
290
- skipped_keys = ('BasebandFirmware', 'SE,UpdatePayload', 'BaseSystem', 'Diags', 'Ap,ExclaveOS')
336
+ skipped_keys = ("BasebandFirmware", "SE,UpdatePayload", "BaseSystem", "Diags", "Ap,ExclaveOS")
291
337
  for key, manifest_entry in manifest_node.items():
292
338
  if key in skipped_keys:
293
339
  continue
294
340
 
295
- if key.startswith('Cryptex1,'):
341
+ if key.startswith("Cryptex1,"):
296
342
  continue
297
343
 
298
- info_dict = manifest_entry.get('Info')
344
+ info_dict = manifest_entry.get("Info")
299
345
  if info_dict is None:
300
346
  continue
301
347
 
302
- if parameters.get('ApSupportsImg4', False) and ('RestoreRequestRules' not in info_dict):
348
+ if parameters.get("ApSupportsImg4", False) and ("RestoreRequestRules" not in info_dict):
303
349
  logger.debug(f'Skipping "{key}" as it doesn\'t have RestoreRequestRules')
304
350
  continue
305
351
 
306
- if parameters.get('_OnlyFWComponents', False):
307
- if not manifest_node.get('Trusted', False):
308
- logger.debug(f'skipping {key} as it is not trusted')
352
+ if parameters.get("_OnlyFWComponents", False):
353
+ if not manifest_node.get("Trusted", False):
354
+ logger.debug(f"skipping {key} as it is not trusted")
309
355
  continue
310
- info = manifest_entry['Info']
356
+ info = manifest_entry["Info"]
311
357
  if not is_fw_payload(info):
312
- logger.debug(f'skipping {key} as it is not a firmware payload')
358
+ logger.debug(f"skipping {key} as it is not a firmware payload")
313
359
  continue
314
360
 
315
- if info_dict.get('IsFTAB'):
316
- logger.debug('Skipping IsFTAB')
361
+ if info_dict.get("IsFTAB"):
362
+ logger.debug("Skipping IsFTAB")
317
363
  continue
318
364
 
319
365
  # copy this entry
320
366
  tss_entry = dict(manifest_entry)
321
367
 
322
368
  # remove obsolete Info node
323
- tss_entry.pop('Info')
369
+ tss_entry.pop("Info")
324
370
 
325
371
  # handle RestoreRequestRules
326
- if 'Info' in manifest_entry and 'RestoreRequestRules' in manifest_entry['Info']:
327
- rules = manifest_entry['Info']['RestoreRequestRules']
372
+ if "Info" in manifest_entry and "RestoreRequestRules" in manifest_entry["Info"]:
373
+ rules = manifest_entry["Info"]["RestoreRequestRules"]
328
374
  if rules:
329
- logger.debug(f'Applying restore request rules for entry {key}')
375
+ logger.debug(f"Applying restore request rules for entry {key}")
330
376
  tss_entry = self.apply_restore_request_rules(tss_entry, parameters, rules)
331
377
 
332
378
  # Make sure we have a Digest key for Trusted items even if empty
333
- node = manifest_entry.get('Trusted', False)
334
- if node:
335
- if manifest_entry.get('Digest') is None:
336
- tss_entry['Digest'] = b''
379
+ node = manifest_entry.get("Trusted", False)
380
+ if node and manifest_entry.get("Digest") is None:
381
+ tss_entry["Digest"] = b""
337
382
 
338
383
  self._request[key] = tss_entry
339
384
 
@@ -341,91 +386,115 @@ class TSSRequest:
341
386
  self._request.update(overrides)
342
387
 
343
388
  def add_ap_img3_tags(self, parameters: dict):
344
- if 'ApNonce' in parameters:
345
- self._request['ApNonce'] = parameters['ApNonce']
346
- self._request['@APTicket'] = True
389
+ if "ApNonce" in parameters:
390
+ self._request["ApNonce"] = parameters["ApNonce"]
391
+ self._request["@APTicket"] = True
347
392
 
348
393
  def add_ap_img4_tags(self, parameters):
349
394
  keys_to_copy = (
350
- 'ApNonce', 'ApProductionMode', 'ApSecurityMode', 'Ap,OSLongVersion', 'ApSecurityMode', 'ApSepNonce',
351
- 'Ap,SDKPlatform', 'PearlCertificationRootPub', 'NeRDEpoch', 'ApSikaFuse', 'Ap,SikaFuse', 'Ap,OSReleaseType',
352
- 'Ap,ProductType', 'Ap,Target', 'Ap,TargetType', 'AllowNeRDBoot', 'Ap,ProductMarketingVersion',
353
- 'Ap,Timestamp',
395
+ "ApNonce",
396
+ "ApProductionMode",
397
+ "ApSecurityMode",
398
+ "Ap,OSLongVersion",
399
+ "ApSecurityMode",
400
+ "ApSepNonce",
401
+ "Ap,SDKPlatform",
402
+ "PearlCertificationRootPub",
403
+ "NeRDEpoch",
404
+ "ApSikaFuse",
405
+ "Ap,SikaFuse",
406
+ "Ap,OSReleaseType",
407
+ "Ap,ProductType",
408
+ "Ap,Target",
409
+ "Ap,TargetType",
410
+ "AllowNeRDBoot",
411
+ "Ap,ProductMarketingVersion",
412
+ "Ap,Timestamp",
354
413
  )
355
414
  for k in keys_to_copy:
356
415
  if k in parameters:
357
416
  v = parameters[k]
358
- if k == 'ApSepNonce':
359
- k = 'SepNonce'
360
- if k == 'ApSikaFuse':
361
- k = 'Ap,SikaFuse'
417
+ if k == "ApSepNonce":
418
+ k = "SepNonce"
419
+ if k == "ApSikaFuse":
420
+ k = "Ap,SikaFuse"
362
421
  self._request[k] = v
363
422
 
364
- uid_mode = parameters.get('UID_MODE')
423
+ uid_mode = parameters.get("UID_MODE")
365
424
 
366
- if 'NeRDEpoch' in parameters:
367
- self._request['PermitNeRDPivot'] = b''
425
+ if "NeRDEpoch" in parameters:
426
+ self._request["PermitNeRDPivot"] = b""
368
427
 
369
428
  if uid_mode is not None:
370
- self._request['UID_MODE'] = uid_mode
371
- self._request['@ApImg4Ticket'] = True
372
- self._request['@BBTicket'] = True
429
+ self._request["UID_MODE"] = uid_mode
430
+ self._request["@ApImg4Ticket"] = True
431
+ self._request["@BBTicket"] = True
373
432
 
374
- if parameters.get('RequiresUIDMode'):
433
+ if parameters.get("RequiresUIDMode"):
375
434
  # The logic here is missing why this value is expected to be 'false'
376
- self._request['UID_MODE'] = False
435
+ self._request["UID_MODE"] = False
377
436
 
378
437
  # Workaround: We have only seen Ap,SikaFuse together with UID_MODE
379
- self._request['Ap,SikaFuse'] = 0
438
+ self._request["Ap,SikaFuse"] = 0
380
439
 
381
440
  def add_se_tags(self, parameters: dict, overrides=None):
382
- manifest = parameters['Manifest']
441
+ manifest = parameters["Manifest"]
383
442
 
384
443
  # add tags indicating we want to get the SE,Ticket
385
- self._request['@BBTicket'] = True
386
- self._request['@SE,Ticket'] = True
444
+ self._request["@BBTicket"] = True
445
+ self._request["@SE,Ticket"] = True
387
446
 
388
- keys_to_copy = ('SE,ChipID', 'SE,ID', 'SE,Nonce', 'SE,Nonce', 'SE,RootKeyIdentifier',
389
- 'SEChipID', 'SEID', 'SENonce', 'SENonce', 'SERootKeyIdentifier',)
447
+ keys_to_copy = (
448
+ "SE,ChipID",
449
+ "SE,ID",
450
+ "SE,Nonce",
451
+ "SE,Nonce",
452
+ "SE,RootKeyIdentifier",
453
+ "SEChipID",
454
+ "SEID",
455
+ "SENonce",
456
+ "SENonce",
457
+ "SERootKeyIdentifier",
458
+ )
390
459
 
391
460
  for src_key in keys_to_copy:
392
461
  if src_key not in parameters:
393
462
  continue
394
463
 
395
- if src_key.startswith('SE'):
464
+ if src_key.startswith("SE"):
396
465
  dst_key = src_key
397
- if not dst_key.startswith('SE,'):
466
+ if not dst_key.startswith("SE,"):
398
467
  # make sure there is a comma (,) after prefix
399
- dst_key = 'SE,' + dst_key.split('SE', 1)[1]
468
+ dst_key = "SE," + dst_key.split("SE", 1)[1]
400
469
  self._request[dst_key] = parameters[src_key]
401
470
 
402
471
  # 'IsDev' determines whether we have Production or Development
403
- is_dev = parameters.get('SE,IsDev')
472
+ is_dev = parameters.get("SE,IsDev")
404
473
  if is_dev is None:
405
- is_dev = parameters.get('SEIsDev', False)
474
+ is_dev = parameters.get("SEIsDev", False)
406
475
 
407
476
  # add SE,* components from build manifest to request
408
477
  for key, manifest_entry in manifest.items():
409
- if not key.startswith('SE'):
478
+ if not key.startswith("SE"):
410
479
  continue
411
480
 
412
481
  # copy this entry
413
482
  tss_entry = dict(manifest_entry)
414
483
 
415
484
  # remove Info node
416
- tss_entry.pop('Info')
485
+ tss_entry.pop("Info")
417
486
 
418
487
  # remove Development or Production key/hash node
419
488
  if is_dev:
420
- if 'ProductionCMAC' in tss_entry:
421
- tss_entry.pop('ProductionCMAC')
422
- if 'ProductionUpdatePayloadHash' in tss_entry:
423
- tss_entry.pop('ProductionUpdatePayloadHash')
489
+ if "ProductionCMAC" in tss_entry:
490
+ tss_entry.pop("ProductionCMAC")
491
+ if "ProductionUpdatePayloadHash" in tss_entry:
492
+ tss_entry.pop("ProductionUpdatePayloadHash")
424
493
  else:
425
- if 'DevelopmentCMAC' in tss_entry:
426
- tss_entry.pop('DevelopmentCMAC')
427
- if 'DevelopmentUpdatePayloadHash' in tss_entry:
428
- tss_entry.pop('DevelopmentUpdatePayloadHash')
494
+ if "DevelopmentCMAC" in tss_entry:
495
+ tss_entry.pop("DevelopmentCMAC")
496
+ if "DevelopmentUpdatePayloadHash" in tss_entry:
497
+ tss_entry.pop("DevelopmentUpdatePayloadHash")
429
498
 
430
499
  # add entry to request
431
500
  self._request[key] = tss_entry
@@ -434,21 +503,27 @@ class TSSRequest:
434
503
  self._request.update(overrides)
435
504
 
436
505
  def add_savage_tags(self, parameters: dict, overrides=None, component_name=None):
437
- manifest = parameters['Manifest']
506
+ manifest = parameters["Manifest"]
438
507
 
439
508
  # add tags indicating we want to get the Savage,Ticket
440
- self._request['@BBTicket'] = True
441
- self._request['@Savage,Ticket'] = True
509
+ self._request["@BBTicket"] = True
510
+ self._request["@Savage,Ticket"] = True
442
511
 
443
512
  # add Savage,UID
444
- self._request['Savage,UID'] = get_with_or_without_comma(parameters, 'Savage,UID')
513
+ self._request["Savage,UID"] = get_with_or_without_comma(parameters, "Savage,UID")
445
514
 
446
515
  # add SEP
447
- self._request['SEP'] = {'Digest': manifest['SEP']['Digest']}
516
+ self._request["SEP"] = {"Digest": manifest["SEP"]["Digest"]}
448
517
 
449
518
  keys_to_copy = (
450
- 'Savage,PatchEpoch', 'Savage,ChipID', 'Savage,AllowOfflineBoot', 'Savage,ReadFWKey',
451
- 'Savage,ProductionMode', 'Savage,Nonce', 'Savage,Nonce')
519
+ "Savage,PatchEpoch",
520
+ "Savage,ChipID",
521
+ "Savage,AllowOfflineBoot",
522
+ "Savage,ReadFWKey",
523
+ "Savage,ProductionMode",
524
+ "Savage,Nonce",
525
+ "Savage,Nonce",
526
+ )
452
527
 
453
528
  for k in keys_to_copy:
454
529
  value = get_with_or_without_comma(parameters, k)
@@ -456,22 +531,22 @@ class TSSRequest:
456
531
  continue
457
532
  self._request[k] = value
458
533
 
459
- isprod = get_with_or_without_comma(parameters, 'Savage,ProductionMode')
534
+ isprod = get_with_or_without_comma(parameters, "Savage,ProductionMode")
460
535
 
461
536
  # get the right component name
462
- comp_name = 'Savage,B0-Prod-Patch' if isprod else 'Savage,B0-Dev-Patch'
463
- node = get_with_or_without_comma(parameters, 'Savage,Revision')
537
+ comp_name = "Savage,B0-Prod-Patch" if isprod else "Savage,B0-Dev-Patch"
538
+ node = get_with_or_without_comma(parameters, "Savage,Revision")
464
539
 
465
540
  if isinstance(node, bytes):
466
541
  savage_rev = node
467
542
  if ((savage_rev[0] | 0x10) & 0xF0) == 0x30:
468
- comp_name = 'Savage,B2-Prod-Patch' if isprod else 'Savage,B2-Dev-Patch'
543
+ comp_name = "Savage,B2-Prod-Patch" if isprod else "Savage,B2-Dev-Patch"
469
544
  elif (savage_rev[0] & 0xF0) == 0xA0:
470
- comp_name = 'Savage,BA-Prod-Patch' if isprod else 'Savage,BA-Dev-Patch'
545
+ comp_name = "Savage,BA-Prod-Patch" if isprod else "Savage,BA-Dev-Patch"
471
546
 
472
547
  # add Savage,B?-*-Patch
473
548
  d = dict(manifest[comp_name])
474
- d.pop('Info')
549
+ d.pop("Info")
475
550
  self._request[comp_name] = d
476
551
 
477
552
  if overrides is not None:
@@ -480,36 +555,44 @@ class TSSRequest:
480
555
  return comp_name
481
556
 
482
557
  def add_yonkers_tags(self, parameters: dict, overrides=None):
483
- manifest = parameters['Manifest']
558
+ manifest = parameters["Manifest"]
484
559
 
485
560
  # add tags indicating we want to get the Yonkers,Ticket
486
- self._request['@BBTicket'] = True
487
- self._request['@Yonkers,Ticket'] = True
561
+ self._request["@BBTicket"] = True
562
+ self._request["@Yonkers,Ticket"] = True
488
563
 
489
564
  # add SEP
490
- self._request['SEP'] = {'Digest': manifest['SEP']['Digest']}
565
+ self._request["SEP"] = {"Digest": manifest["SEP"]["Digest"]}
491
566
 
492
567
  keys_to_copy = (
493
- 'Yonkers,AllowOfflineBoot', 'Yonkers,BoardID', 'Yonkers,ChipID', 'Yonkers,ECID', 'Yonkers,Nonce',
494
- 'Yonkers,PatchEpoch', 'Yonkers,ProductionMode', 'Yonkers,ReadECKey', 'Yonkers,ReadFWKey',)
568
+ "Yonkers,AllowOfflineBoot",
569
+ "Yonkers,BoardID",
570
+ "Yonkers,ChipID",
571
+ "Yonkers,ECID",
572
+ "Yonkers,Nonce",
573
+ "Yonkers,PatchEpoch",
574
+ "Yonkers,ProductionMode",
575
+ "Yonkers,ReadECKey",
576
+ "Yonkers,ReadFWKey",
577
+ )
495
578
 
496
579
  for k in keys_to_copy:
497
580
  self._request[k] = get_with_or_without_comma(parameters, k)
498
581
 
499
- isprod = get_with_or_without_comma(parameters, 'Yonkers,ProductionMode', 1)
500
- fabrevision = get_with_or_without_comma(parameters, 'Yonkers,FabRevision', 0xffffffffffffffff)
582
+ isprod = get_with_or_without_comma(parameters, "Yonkers,ProductionMode", 1)
583
+ fabrevision = get_with_or_without_comma(parameters, "Yonkers,FabRevision", 0xFFFFFFFFFFFFFFFF)
501
584
  comp_node = None
502
585
  result_comp_name = None
503
586
 
504
587
  for comp_name, node in manifest.items():
505
- if not comp_name.startswith('Yonkers,'):
588
+ if not comp_name.startswith("Yonkers,"):
506
589
  continue
507
590
 
508
591
  target_node = 1
509
- sub_node = node.get('EPRO')
592
+ sub_node = node.get("EPRO")
510
593
  if sub_node:
511
594
  target_node &= sub_node if isprod else not sub_node
512
- sub_node = node.get('FabRevision')
595
+ sub_node = node.get("FabRevision")
513
596
  if sub_node:
514
597
  target_node &= sub_node == fabrevision
515
598
 
@@ -519,11 +602,11 @@ class TSSRequest:
519
602
  break
520
603
 
521
604
  if comp_node is None:
522
- raise PyMobileDevice3Exception(f'No Yonkers node for {isprod}/{fabrevision}')
605
+ raise PyMobileDevice3Exception(f"No Yonkers node for {isprod}/{fabrevision}")
523
606
 
524
607
  # add Yonkers,SysTopPatch
525
608
  comp_dict = dict(comp_node)
526
- comp_dict.pop('Info')
609
+ comp_dict.pop("Info")
527
610
  self._request[result_comp_name] = comp_dict
528
611
 
529
612
  if overrides is not None:
@@ -532,45 +615,60 @@ class TSSRequest:
532
615
  return result_comp_name
533
616
 
534
617
  def add_baseband_tags(self, parameters: dict, overrides=None):
535
- self._request['@BBTicket'] = True
618
+ self._request["@BBTicket"] = True
536
619
 
537
620
  keys_to_copy = (
538
- 'BbChipID', 'BbProvisioningManifestKeyHash', 'BbActivationManifestKeyHash', 'BbCalibrationManifestKeyHash',
539
- 'BbFactoryActivationManifestKeyHash', 'BbFDRSecurityKeyHash', 'BbSkeyId', 'BbNonce',
540
- 'BbGoldCertId', 'BbSNUM', 'PearlCertificationRootPub', 'Ap,OSLongVersion')
621
+ "BbChipID",
622
+ "BbProvisioningManifestKeyHash",
623
+ "BbActivationManifestKeyHash",
624
+ "BbCalibrationManifestKeyHash",
625
+ "BbFactoryActivationManifestKeyHash",
626
+ "BbFDRSecurityKeyHash",
627
+ "BbSkeyId",
628
+ "BbNonce",
629
+ "BbGoldCertId",
630
+ "BbSNUM",
631
+ "PearlCertificationRootPub",
632
+ "Ap,OSLongVersion",
633
+ )
541
634
 
542
635
  for k in keys_to_copy:
543
636
  if k in parameters:
544
637
  self._request[k] = parameters[k]
545
638
 
546
- bb_chip_id = parameters['BbChipID']
547
- bb_cert_id = parameters['BbGoldCertId']
639
+ bb_chip_id = parameters["BbChipID"]
640
+ bb_cert_id = parameters["BbGoldCertId"]
548
641
 
549
- bbfwdict = dict(parameters['Manifest']['BasebandFirmware'])
550
- bbfwdict.pop('Info')
642
+ bbfwdict = dict(parameters["Manifest"]["BasebandFirmware"])
643
+ bbfwdict.pop("Info")
551
644
 
552
645
  if bb_chip_id == 0x68:
553
646
  # depending on the BasebandCertId remove certain nodes
554
647
  if bb_cert_id in (0x26F3FACC, 0x5CF2EC4E, 0x8399785A):
555
- bbfwdict.pop('PSI2-PartialDigest')
556
- bbfwdict.pop('RestorePSI2-PartialDigest')
648
+ bbfwdict.pop("PSI2-PartialDigest")
649
+ bbfwdict.pop("RestorePSI2-PartialDigest")
557
650
  else:
558
- bbfwdict.pop('PSI-PartialDigest')
559
- bbfwdict.pop('RestorePSI-PartialDigest')
651
+ bbfwdict.pop("PSI-PartialDigest")
652
+ bbfwdict.pop("RestorePSI-PartialDigest")
560
653
 
561
- self._request['BasebandFirmware'] = bbfwdict
654
+ self._request["BasebandFirmware"] = bbfwdict
562
655
 
563
656
  if overrides:
564
657
  self._request.update(overrides)
565
658
 
566
659
  def add_rose_tags(self, parameters: dict, overrides: typing.Optional[dict] = None):
567
- manifest = parameters['Manifest']
660
+ manifest = parameters["Manifest"]
568
661
 
569
662
  # add tags indicating we want to get the Rap,Ticket
570
- self._request['@BBTicket'] = True
571
- self._request['@Rap,Ticket'] = True
572
-
573
- keys_to_copy_uint = ('Rap,BoardID', 'Rap,ChipID', 'Rap,ECID', 'Rap,SecurityDomain',)
663
+ self._request["@BBTicket"] = True
664
+ self._request["@Rap,Ticket"] = True
665
+
666
+ keys_to_copy_uint = (
667
+ "Rap,BoardID",
668
+ "Rap,ChipID",
669
+ "Rap,ECID",
670
+ "Rap,SecurityDomain",
671
+ )
574
672
 
575
673
  for key in keys_to_copy_uint:
576
674
  value = get_with_or_without_comma(parameters, key)
@@ -580,41 +678,44 @@ class TSSRequest:
580
678
  else:
581
679
  self._request[key] = value
582
680
 
583
- keys_to_copy_bool = ('Rap,ProductionMode', 'Rap,SecurityMode',)
681
+ keys_to_copy_bool = (
682
+ "Rap,ProductionMode",
683
+ "Rap,SecurityMode",
684
+ )
584
685
 
585
686
  for key in keys_to_copy_bool:
586
687
  value = get_with_or_without_comma(parameters, key)
587
688
  self._request[key] = bytes_to_uint(value) == 1
588
689
 
589
- nonce = get_with_or_without_comma(parameters, 'Rap,Nonce')
690
+ nonce = get_with_or_without_comma(parameters, "Rap,Nonce")
590
691
  if nonce is not None:
591
- self._request['Rap,Nonce'] = nonce
692
+ self._request["Rap,Nonce"] = nonce
592
693
 
593
- digest = get_with_or_without_comma(parameters, 'Rap,FdrRootCaDigest')
694
+ digest = get_with_or_without_comma(parameters, "Rap,FdrRootCaDigest")
594
695
  if digest is not None:
595
- self._request['Rap,FdrRootCaDigest'] = digest
696
+ self._request["Rap,FdrRootCaDigest"] = digest
596
697
 
597
698
  for comp_name, node in manifest.items():
598
- if not comp_name.startswith('Rap,'):
699
+ if not comp_name.startswith("Rap,"):
599
700
  continue
600
701
 
601
702
  manifest_entry = dict(node)
602
703
 
603
704
  # handle RestoreRequestRules
604
- rules = manifest_entry['Info'].get('RestoreRequestRules')
705
+ rules = manifest_entry["Info"].get("RestoreRequestRules")
605
706
  if rules is not None:
606
707
  self.apply_restore_request_rules(manifest_entry, parameters, rules)
607
708
 
608
709
  # Make sure we have a Digest key for Trusted items even if empty
609
- trusted = manifest_entry.get('Trusted', False)
710
+ trusted = manifest_entry.get("Trusted", False)
610
711
 
611
712
  if trusted:
612
- digest = manifest_entry.get('Digest')
713
+ digest = manifest_entry.get("Digest")
613
714
  if digest is None:
614
- logger.debug(f'No Digest data, using empty value for entry {comp_name}')
615
- manifest_entry['Digest'] = b''
715
+ logger.debug(f"No Digest data, using empty value for entry {comp_name}")
716
+ manifest_entry["Digest"] = b""
616
717
 
617
- manifest_entry.pop('Info')
718
+ manifest_entry.pop("Info")
618
719
 
619
720
  # finally add entry to request
620
721
  self._request[comp_name] = manifest_entry
@@ -623,41 +724,41 @@ class TSSRequest:
623
724
  self._request.update(overrides)
624
725
 
625
726
  def add_veridian_tags(self, parameters: dict, overrides: typing.Optional[dict] = None):
626
- manifest = parameters['Manifest']
727
+ manifest = parameters["Manifest"]
627
728
 
628
729
  # add tags indicating we want to get the Rap,Ticket
629
- self._request['@BBTicket'] = True
630
- self._request['@BMU,Ticket'] = True
730
+ self._request["@BBTicket"] = True
731
+ self._request["@BMU,Ticket"] = True
631
732
 
632
- self._request['BMU,ChipID'] = parameters['ChipID']
633
- self._request['BMU,UniqueID'] = parameters['UniqueID']
634
- self._request['BMU,ProductionMode'] = parameters['ProductionMode']
733
+ self._request["BMU,ChipID"] = parameters["ChipID"]
734
+ self._request["BMU,UniqueID"] = parameters["UniqueID"]
735
+ self._request["BMU,ProductionMode"] = parameters["ProductionMode"]
635
736
 
636
- nonce = parameters.get('Nonce')
737
+ nonce = parameters.get("Nonce")
637
738
  if nonce is not None:
638
- self._request['BMU,Nonce'] = nonce
739
+ self._request["BMU,Nonce"] = nonce
639
740
 
640
741
  for comp_name, node in manifest.items():
641
- if not comp_name.startswith('BMU,'):
742
+ if not comp_name.startswith("BMU,"):
642
743
  continue
643
744
 
644
745
  manifest_entry = dict(node)
645
746
 
646
747
  # handle RestoreRequestRules
647
- rules = manifest_entry['Info'].get('RestoreRequestRules')
748
+ rules = manifest_entry["Info"].get("RestoreRequestRules")
648
749
  if rules is not None:
649
750
  self.apply_restore_request_rules(manifest_entry, parameters, rules)
650
751
 
651
752
  # Make sure we have a Digest key for Trusted items even if empty
652
- trusted = manifest_entry.get('Trusted', False)
753
+ trusted = manifest_entry.get("Trusted", False)
653
754
 
654
755
  if trusted:
655
- digest = manifest_entry.get('Digest')
756
+ digest = manifest_entry.get("Digest")
656
757
  if digest is None:
657
- logger.debug(f'No Digest data, using empty value for entry {comp_name}')
658
- manifest_entry['Digest'] = b''
758
+ logger.debug(f"No Digest data, using empty value for entry {comp_name}")
759
+ manifest_entry["Digest"] = b""
659
760
 
660
- manifest_entry.pop('Info')
761
+ manifest_entry.pop("Info")
661
762
 
662
763
  # finally add entry to request
663
764
  self._request[comp_name] = manifest_entry
@@ -666,14 +767,19 @@ class TSSRequest:
666
767
  self._request.update(overrides)
667
768
 
668
769
  def add_tcon_tags(self, parameters: dict, overrides: typing.Optional[dict] = None):
669
- manifest = parameters['Manifest']
770
+ manifest = parameters["Manifest"]
670
771
 
671
772
  # add tags indicating we want to get the Baobab,Ticket
672
- self._request['@BBTicket'] = True
673
- self._request['@Baobab,Ticket'] = True
674
-
675
- keys_to_copy_uint = ('Baobab,BoardID', 'Baobab,ChipID', 'Baobab,Life', 'Baobab,ManifestEpoch',
676
- 'Baobab,SecurityDomain',)
773
+ self._request["@BBTicket"] = True
774
+ self._request["@Baobab,Ticket"] = True
775
+
776
+ keys_to_copy_uint = (
777
+ "Baobab,BoardID",
778
+ "Baobab,ChipID",
779
+ "Baobab,Life",
780
+ "Baobab,ManifestEpoch",
781
+ "Baobab,SecurityDomain",
782
+ )
677
783
 
678
784
  for key in keys_to_copy_uint:
679
785
  value = get_with_or_without_comma(parameters, key)
@@ -683,26 +789,26 @@ class TSSRequest:
683
789
  else:
684
790
  self._request[key] = value
685
791
 
686
- isprod = bool(get_with_or_without_comma(parameters, 'Baobab,ProductionMode', False))
687
- self._request['Baobab,ProductionMode'] = isprod
792
+ isprod = bool(get_with_or_without_comma(parameters, "Baobab,ProductionMode", False))
793
+ self._request["Baobab,ProductionMode"] = isprod
688
794
 
689
- nonce = get_with_or_without_comma(parameters, 'Baobab,UpdateNonce')
795
+ nonce = get_with_or_without_comma(parameters, "Baobab,UpdateNonce")
690
796
 
691
797
  if nonce is not None:
692
- self._request['Baobab,UpdateNonce'] = nonce
798
+ self._request["Baobab,UpdateNonce"] = nonce
693
799
 
694
- ecid = get_with_or_without_comma(parameters, 'Baobab,ECID')
800
+ ecid = get_with_or_without_comma(parameters, "Baobab,ECID")
695
801
 
696
802
  if ecid is not None:
697
- self._request['Baobab,ECID'] = ecid
803
+ self._request["Baobab,ECID"] = ecid
698
804
 
699
805
  for comp_name, node in manifest.items():
700
- if not comp_name.startswith('Baobab,'):
806
+ if not comp_name.startswith("Baobab,"):
701
807
  continue
702
808
 
703
809
  manifest_entry = dict(node)
704
- manifest_entry.pop('Info')
705
- manifest_entry['EPRO'] = isprod
810
+ manifest_entry.pop("Info")
811
+ manifest_entry["EPRO"] = isprod
706
812
 
707
813
  # finally add entry to request
708
814
  self._request[comp_name] = manifest_entry
@@ -713,31 +819,31 @@ class TSSRequest:
713
819
  def img4_create_local_manifest(self, build_identity=None):
714
820
  manifest = None
715
821
  if build_identity is not None:
716
- manifest = build_identity['Manifest']
822
+ manifest = build_identity["Manifest"]
717
823
 
718
824
  p = asn1.Encoder()
719
825
  p.start()
720
826
 
721
- p.write(b'MANP', asn1.Numbers.IA5String)
827
+ p.write(b"MANP", asn1.Numbers.IA5String)
722
828
  p.enter(asn1.Numbers.Set)
723
829
 
724
- p.write(b'BORD', asn1.Numbers.IA5String)
725
- p.write(self._request['ApBoardID'], asn1.Numbers.Integer)
830
+ p.write(b"BORD", asn1.Numbers.IA5String)
831
+ p.write(self._request["ApBoardID"], asn1.Numbers.Integer)
726
832
 
727
- p.write(b'CEPO', asn1.Numbers.IA5String)
833
+ p.write(b"CEPO", asn1.Numbers.IA5String)
728
834
  p.write(0, asn1.Numbers.Integer)
729
835
 
730
- p.write(b'CHIP', asn1.Numbers.IA5String)
731
- p.write(self._request['ApChipID'], asn1.Numbers.Integer)
836
+ p.write(b"CHIP", asn1.Numbers.IA5String)
837
+ p.write(self._request["ApChipID"], asn1.Numbers.Integer)
732
838
 
733
- p.write(b'CPRO', asn1.Numbers.IA5String)
734
- p.write(self._request['ApProductionMode'], asn1.Numbers.Integer)
839
+ p.write(b"CPRO", asn1.Numbers.IA5String)
840
+ p.write(self._request["ApProductionMode"], asn1.Numbers.Integer)
735
841
 
736
- p.write(b'CSEC', asn1.Numbers.IA5String)
842
+ p.write(b"CSEC", asn1.Numbers.IA5String)
737
843
  p.write(0, asn1.Numbers.Integer)
738
844
 
739
- p.write(b'SDOM', asn1.Numbers.IA5String)
740
- p.write(self._request['ApSecurityDomain'], asn1.Numbers.Integer)
845
+ p.write(b"SDOM", asn1.Numbers.IA5String)
846
+ p.write(self._request["ApSecurityDomain"], asn1.Numbers.Integer)
741
847
 
742
848
  p.leave()
743
849
 
@@ -747,23 +853,23 @@ class TSSRequest:
747
853
  # check if component has Img4PayloadType
748
854
  comp = None
749
855
  if manifest is not None:
750
- comp = manifest[k]['Info'].get('Img4PayloadType')
856
+ comp = manifest[k]["Info"].get("Img4PayloadType")
751
857
 
752
858
  if comp is None:
753
859
  comp = COMPONENT_FOURCC.get(k)
754
860
 
755
861
  if comp is None:
756
- raise NotImplementedError(f'Unhandled component {k} - can\'t create manifest')
862
+ raise NotImplementedError(f"Unhandled component {k} - can't create manifest")
757
863
 
758
- logger.debug(f'found component {comp} ({k})')
864
+ logger.debug(f"found component {comp} ({k})")
759
865
 
760
866
  # write manifest body header
761
- p.write(b'MANB', asn1.Numbers.IA5String)
867
+ p.write(b"MANB", asn1.Numbers.IA5String)
762
868
  p.enter(asn1.Numbers.Set)
763
869
  p.leave()
764
870
 
765
871
  # write header values
766
- p.write(b'IM4M', asn1.Numbers.IA5String)
872
+ p.write(b"IM4M", asn1.Numbers.IA5String)
767
873
  p.write(0, asn1.Numbers.Integer)
768
874
 
769
875
  return p.output()
@@ -777,28 +883,30 @@ class TSSRequest:
777
883
 
778
884
  async def send_receive(self) -> TSSResponse:
779
885
  headers = {
780
- 'Cache-Control': 'no-cache',
781
- 'Content-type': 'text/xml; charset="utf-8"',
782
- 'User-Agent': 'InetURL/1.0',
783
- 'Expect': '',
886
+ "Cache-Control": "no-cache",
887
+ "Content-type": 'text/xml; charset="utf-8"',
888
+ "User-Agent": "InetURL/1.0",
889
+ "Expect": "",
784
890
  }
785
891
 
786
- logger.info('Sending TSS request...')
892
+ logger.info("Sending TSS request...")
787
893
  logger.debug(self._request)
788
894
 
789
895
  loop = asyncio.get_event_loop()
790
896
  with ThreadPoolExecutor() as executor:
897
+
791
898
  def post() -> bytes:
792
- return requests.post(TSS_CONTROLLER_ACTION_URL, headers=headers, data=plistlib.dumps(self._request),
793
- verify=False).content
899
+ return requests.post(
900
+ TSS_CONTROLLER_ACTION_URL, headers=headers, data=plistlib.dumps(self._request), verify=False
901
+ ).content
794
902
 
795
903
  content = await loop.run_in_executor(executor, post)
796
904
 
797
- if b'MESSAGE=SUCCESS' in content:
798
- logger.info('response successfully received')
905
+ if b"MESSAGE=SUCCESS" in content:
906
+ logger.info("response successfully received")
799
907
 
800
- message = content.split(b'MESSAGE=', 1)[1].split(b'&', 1)[0].decode()
801
- if message != 'SUCCESS':
802
- raise Exception(f'server replied: {message}')
908
+ message = content.split(b"MESSAGE=", 1)[1].split(b"&", 1)[0].decode()
909
+ if message != "SUCCESS":
910
+ raise TSSError(f"server replied: {message}")
803
911
 
804
- return TSSResponse(plistlib.loads(content.split(b'REQUEST_STRING=', 1)[1]))
912
+ return TSSResponse(plistlib.loads(content.split(b"REQUEST_STRING=", 1)[1]))