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
@@ -7,112 +7,113 @@ from uuid import uuid4
7
7
 
8
8
  import asn1
9
9
  import requests
10
- from ipsw_parser.img4 import COMPONENT_FOURCC
11
10
 
12
- from pymobiledevice3.exceptions import PyMobileDevice3Exception
11
+ from pymobiledevice3.exceptions import PyMobileDevice3Exception, TSSError
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-1033.0.2'
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
 
56
62
 
57
63
  class TSSRequest:
58
64
  def __init__(self):
59
- self._request = {
60
- '@HostPlatformInfo': 'mac',
61
- '@VersionInfo': TSS_CLIENT_VERSION_STRING,
62
- '@UUID': str(uuid4()).upper(),
65
+ self._request: dict[str, typing.Any] = {
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,110 +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
291
+ self._request["@BBTicket"] = True
292
+ self._request["@eUICC,Ticket"] = True
247
293
 
248
- self._request['eUICC,ApProductionMode'] = parameters.get('eUICC,ApProductionMode',
249
- parameters.get('ApProductionMode'))
294
+ self._request["eUICC,ApProductionMode"] = parameters.get(
295
+ "eUICC,ApProductionMode", parameters.get("ApProductionMode")
296
+ )
250
297
 
251
- keys = ('eUICC,ChipID', 'eUICC,EID', 'eUICC,RootKeyIdentifier')
298
+ keys = ("eUICC,ChipID", "eUICC,EID", "eUICC,RootKeyIdentifier")
252
299
  for k in keys:
253
300
  if k in parameters:
254
301
  self._request[k] = parameters[k]
255
302
 
256
- if self._request.get('eUICC,Gold') is None:
257
- 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"))
258
305
  if n:
259
- self._request['eUICC,Gold'] = {'Digest': n['Digest']}
306
+ self._request["eUICC,Gold"] = {"Digest": n["Digest"]}
260
307
 
261
- if self._request.get('eUICC,Main') is None:
262
- 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"))
263
310
  if n:
264
- self._request['eUICC,Main'] = {'Digest': n['Digest']}
311
+ self._request["eUICC,Main"] = {"Digest": n["Digest"]}
265
312
 
266
313
  # set Nonce for eUICC,Gold component
267
- node = parameters.get('EUICCGoldNonce')
314
+ node = parameters.get("EUICCGoldNonce")
268
315
  if node is not None:
269
- n = self._request.get('eUICC,Gold')
316
+ n = self._request.get("eUICC,Gold")
270
317
  if n is not None:
271
- n['Nonce'] = node
318
+ n["Nonce"] = node
272
319
 
273
320
  # set Nonce for eUICC,Main component
274
- node = parameters.get('EUICCMainNonce')
321
+ node = parameters.get("EUICCMainNonce")
275
322
  if node is not None:
276
- n = self._request.get('eUICC,Main')
323
+ n = self._request.get("eUICC,Main")
277
324
  if n is not None:
278
- n['Nonce'] = node
325
+ n["Nonce"] = node
279
326
 
280
327
  if overrides is not None:
281
328
  self._request.update(overrides)
282
329
 
283
330
  def add_ap_tags(self, parameters: dict, overrides=None):
284
- """ loop over components from build manifest """
331
+ """loop over components from build manifest"""
285
332
 
286
- manifest_node = parameters['Manifest']
333
+ manifest_node = parameters["Manifest"]
287
334
 
288
335
  # add components to request
289
- skipped_keys = ('BasebandFirmware', 'SE,UpdatePayload', 'BaseSystem', 'Diags',)
336
+ skipped_keys = ("BasebandFirmware", "SE,UpdatePayload", "BaseSystem", "Diags", "Ap,ExclaveOS")
290
337
  for key, manifest_entry in manifest_node.items():
291
338
  if key in skipped_keys:
292
339
  continue
293
340
 
294
- if key.startswith('Cryptex1,'):
341
+ if key.startswith("Cryptex1,"):
295
342
  continue
296
343
 
297
- info_dict = manifest_entry.get('Info')
344
+ info_dict = manifest_entry.get("Info")
298
345
  if info_dict is None:
299
346
  continue
300
347
 
301
- if parameters.get('ApSupportsImg4', False) and ('RestoreRequestRules' not in info_dict):
348
+ if parameters.get("ApSupportsImg4", False) and ("RestoreRequestRules" not in info_dict):
302
349
  logger.debug(f'Skipping "{key}" as it doesn\'t have RestoreRequestRules')
303
350
  continue
304
351
 
305
- if parameters.get('_OnlyFWComponents', False):
306
- if not manifest_node.get('Trusted', False):
307
- 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")
308
355
  continue
309
- info = manifest_entry['Info']
356
+ info = manifest_entry["Info"]
310
357
  if not is_fw_payload(info):
311
- 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")
312
359
  continue
313
360
 
314
- if info_dict.get('IsFTAB'):
315
- logger.debug('Skipping IsFTAB')
361
+ if info_dict.get("IsFTAB"):
362
+ logger.debug("Skipping IsFTAB")
316
363
  continue
317
364
 
318
365
  # copy this entry
319
366
  tss_entry = dict(manifest_entry)
320
367
 
321
368
  # remove obsolete Info node
322
- tss_entry.pop('Info')
369
+ tss_entry.pop("Info")
323
370
 
324
371
  # handle RestoreRequestRules
325
- if 'Info' in manifest_entry and 'RestoreRequestRules' in manifest_entry['Info']:
326
- rules = manifest_entry['Info']['RestoreRequestRules']
372
+ if "Info" in manifest_entry and "RestoreRequestRules" in manifest_entry["Info"]:
373
+ rules = manifest_entry["Info"]["RestoreRequestRules"]
327
374
  if rules:
328
- logger.debug(f'Applying restore request rules for entry {key}')
375
+ logger.debug(f"Applying restore request rules for entry {key}")
329
376
  tss_entry = self.apply_restore_request_rules(tss_entry, parameters, rules)
330
377
 
331
378
  # Make sure we have a Digest key for Trusted items even if empty
332
- node = manifest_entry.get('Trusted', False)
333
- if node:
334
- if manifest_entry.get('Digest') is None:
335
- 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""
336
382
 
337
383
  self._request[key] = tss_entry
338
384
 
@@ -340,82 +386,115 @@ class TSSRequest:
340
386
  self._request.update(overrides)
341
387
 
342
388
  def add_ap_img3_tags(self, parameters: dict):
343
- if 'ApNonce' in parameters:
344
- self._request['ApNonce'] = parameters['ApNonce']
345
- self._request['@APTicket'] = True
389
+ if "ApNonce" in parameters:
390
+ self._request["ApNonce"] = parameters["ApNonce"]
391
+ self._request["@APTicket"] = True
346
392
 
347
393
  def add_ap_img4_tags(self, parameters):
348
394
  keys_to_copy = (
349
- 'ApNonce', 'ApProductionMode', 'ApSecurityMode', 'Ap,OSLongVersion', 'ApSecurityMode', 'ApSepNonce',
350
- 'Ap,SDKPlatform', 'PearlCertificationRootPub', 'NeRDEpoch', 'ApSikaFuse', 'Ap,SikaFuse', 'Ap,OSReleaseType',
351
- 'Ap,ProductType', 'Ap,Target', 'Ap,TargetType'
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",
352
413
  )
353
414
  for k in keys_to_copy:
354
415
  if k in parameters:
355
416
  v = parameters[k]
356
- if k == 'ApSepNonce':
357
- k = 'SepNonce'
358
- if k == 'ApSikaFuse':
359
- k = 'Ap,SikaFuse'
417
+ if k == "ApSepNonce":
418
+ k = "SepNonce"
419
+ if k == "ApSikaFuse":
420
+ k = "Ap,SikaFuse"
360
421
  self._request[k] = v
361
422
 
362
- uid_mode = parameters.get('UID_MODE', False)
423
+ uid_mode = parameters.get("UID_MODE")
424
+
425
+ if "NeRDEpoch" in parameters:
426
+ self._request["PermitNeRDPivot"] = b""
427
+
428
+ if uid_mode is not None:
429
+ self._request["UID_MODE"] = uid_mode
430
+ self._request["@ApImg4Ticket"] = True
431
+ self._request["@BBTicket"] = True
363
432
 
364
- self._request['UID_MODE'] = uid_mode
365
- self._request['@ApImg4Ticket'] = True
366
- self._request['@BBTicket'] = True
433
+ if parameters.get("RequiresUIDMode"):
434
+ # The logic here is missing why this value is expected to be 'false'
435
+ self._request["UID_MODE"] = False
367
436
 
368
- if parameters.get('RequiresUIDMode'):
369
- self._request['Ap,SikaFuse'] = 0
437
+ # Workaround: We have only seen Ap,SikaFuse together with UID_MODE
438
+ self._request["Ap,SikaFuse"] = 0
370
439
 
371
440
  def add_se_tags(self, parameters: dict, overrides=None):
372
- manifest = parameters['Manifest']
441
+ manifest = parameters["Manifest"]
373
442
 
374
443
  # add tags indicating we want to get the SE,Ticket
375
- self._request['@BBTicket'] = True
376
- self._request['@SE,Ticket'] = True
444
+ self._request["@BBTicket"] = True
445
+ self._request["@SE,Ticket"] = True
377
446
 
378
- keys_to_copy = ('SE,ChipID', 'SE,ID', 'SE,Nonce', 'SE,Nonce', 'SE,RootKeyIdentifier',
379
- '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
+ )
380
459
 
381
460
  for src_key in keys_to_copy:
382
461
  if src_key not in parameters:
383
462
  continue
384
463
 
385
- if src_key.startswith('SE'):
464
+ if src_key.startswith("SE"):
386
465
  dst_key = src_key
387
- if not dst_key.startswith('SE,'):
466
+ if not dst_key.startswith("SE,"):
388
467
  # make sure there is a comma (,) after prefix
389
- dst_key = 'SE,' + dst_key.split('SE', 1)[1]
468
+ dst_key = "SE," + dst_key.split("SE", 1)[1]
390
469
  self._request[dst_key] = parameters[src_key]
391
470
 
392
471
  # 'IsDev' determines whether we have Production or Development
393
- is_dev = parameters.get('SE,IsDev')
472
+ is_dev = parameters.get("SE,IsDev")
394
473
  if is_dev is None:
395
- is_dev = parameters.get('SEIsDev', False)
474
+ is_dev = parameters.get("SEIsDev", False)
396
475
 
397
476
  # add SE,* components from build manifest to request
398
477
  for key, manifest_entry in manifest.items():
399
- if not key.startswith('SE'):
478
+ if not key.startswith("SE"):
400
479
  continue
401
480
 
402
481
  # copy this entry
403
482
  tss_entry = dict(manifest_entry)
404
483
 
405
484
  # remove Info node
406
- tss_entry.pop('Info')
485
+ tss_entry.pop("Info")
407
486
 
408
487
  # remove Development or Production key/hash node
409
488
  if is_dev:
410
- if 'ProductionCMAC' in tss_entry:
411
- tss_entry.pop('ProductionCMAC')
412
- if 'ProductionUpdatePayloadHash' in tss_entry:
413
- 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")
414
493
  else:
415
- if 'DevelopmentCMAC' in tss_entry:
416
- tss_entry.pop('DevelopmentCMAC')
417
- if 'DevelopmentUpdatePayloadHash' in tss_entry:
418
- 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")
419
498
 
420
499
  # add entry to request
421
500
  self._request[key] = tss_entry
@@ -424,21 +503,27 @@ class TSSRequest:
424
503
  self._request.update(overrides)
425
504
 
426
505
  def add_savage_tags(self, parameters: dict, overrides=None, component_name=None):
427
- manifest = parameters['Manifest']
506
+ manifest = parameters["Manifest"]
428
507
 
429
508
  # add tags indicating we want to get the Savage,Ticket
430
- self._request['@BBTicket'] = True
431
- self._request['@Savage,Ticket'] = True
509
+ self._request["@BBTicket"] = True
510
+ self._request["@Savage,Ticket"] = True
432
511
 
433
512
  # add Savage,UID
434
- 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")
435
514
 
436
515
  # add SEP
437
- self._request['SEP'] = {'Digest': manifest['SEP']['Digest']}
516
+ self._request["SEP"] = {"Digest": manifest["SEP"]["Digest"]}
438
517
 
439
518
  keys_to_copy = (
440
- 'Savage,PatchEpoch', 'Savage,ChipID', 'Savage,AllowOfflineBoot', 'Savage,ReadFWKey',
441
- '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
+ )
442
527
 
443
528
  for k in keys_to_copy:
444
529
  value = get_with_or_without_comma(parameters, k)
@@ -446,22 +531,22 @@ class TSSRequest:
446
531
  continue
447
532
  self._request[k] = value
448
533
 
449
- isprod = get_with_or_without_comma(parameters, 'Savage,ProductionMode')
534
+ isprod = get_with_or_without_comma(parameters, "Savage,ProductionMode")
450
535
 
451
536
  # get the right component name
452
- comp_name = 'Savage,B0-Prod-Patch' if isprod else 'Savage,B0-Dev-Patch'
453
- 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")
454
539
 
455
540
  if isinstance(node, bytes):
456
541
  savage_rev = node
457
542
  if ((savage_rev[0] | 0x10) & 0xF0) == 0x30:
458
- 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"
459
544
  elif (savage_rev[0] & 0xF0) == 0xA0:
460
- 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"
461
546
 
462
547
  # add Savage,B?-*-Patch
463
548
  d = dict(manifest[comp_name])
464
- d.pop('Info')
549
+ d.pop("Info")
465
550
  self._request[comp_name] = d
466
551
 
467
552
  if overrides is not None:
@@ -470,36 +555,44 @@ class TSSRequest:
470
555
  return comp_name
471
556
 
472
557
  def add_yonkers_tags(self, parameters: dict, overrides=None):
473
- manifest = parameters['Manifest']
558
+ manifest = parameters["Manifest"]
474
559
 
475
560
  # add tags indicating we want to get the Yonkers,Ticket
476
- self._request['@BBTicket'] = True
477
- self._request['@Yonkers,Ticket'] = True
561
+ self._request["@BBTicket"] = True
562
+ self._request["@Yonkers,Ticket"] = True
478
563
 
479
564
  # add SEP
480
- self._request['SEP'] = {'Digest': manifest['SEP']['Digest']}
565
+ self._request["SEP"] = {"Digest": manifest["SEP"]["Digest"]}
481
566
 
482
567
  keys_to_copy = (
483
- 'Yonkers,AllowOfflineBoot', 'Yonkers,BoardID', 'Yonkers,ChipID', 'Yonkers,ECID', 'Yonkers,Nonce',
484
- '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
+ )
485
578
 
486
579
  for k in keys_to_copy:
487
580
  self._request[k] = get_with_or_without_comma(parameters, k)
488
581
 
489
- isprod = get_with_or_without_comma(parameters, 'Yonkers,ProductionMode', 1)
490
- 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)
491
584
  comp_node = None
492
585
  result_comp_name = None
493
586
 
494
587
  for comp_name, node in manifest.items():
495
- if not comp_name.startswith('Yonkers,'):
588
+ if not comp_name.startswith("Yonkers,"):
496
589
  continue
497
590
 
498
591
  target_node = 1
499
- sub_node = node.get('EPRO')
592
+ sub_node = node.get("EPRO")
500
593
  if sub_node:
501
594
  target_node &= sub_node if isprod else not sub_node
502
- sub_node = node.get('FabRevision')
595
+ sub_node = node.get("FabRevision")
503
596
  if sub_node:
504
597
  target_node &= sub_node == fabrevision
505
598
 
@@ -509,11 +602,11 @@ class TSSRequest:
509
602
  break
510
603
 
511
604
  if comp_node is None:
512
- raise PyMobileDevice3Exception(f'No Yonkers node for {isprod}/{fabrevision}')
605
+ raise PyMobileDevice3Exception(f"No Yonkers node for {isprod}/{fabrevision}")
513
606
 
514
607
  # add Yonkers,SysTopPatch
515
608
  comp_dict = dict(comp_node)
516
- comp_dict.pop('Info')
609
+ comp_dict.pop("Info")
517
610
  self._request[result_comp_name] = comp_dict
518
611
 
519
612
  if overrides is not None:
@@ -522,45 +615,60 @@ class TSSRequest:
522
615
  return result_comp_name
523
616
 
524
617
  def add_baseband_tags(self, parameters: dict, overrides=None):
525
- self._request['@BBTicket'] = True
618
+ self._request["@BBTicket"] = True
526
619
 
527
620
  keys_to_copy = (
528
- 'BbChipID', 'BbProvisioningManifestKeyHash', 'BbActivationManifestKeyHash', 'BbCalibrationManifestKeyHash',
529
- 'BbFactoryActivationManifestKeyHash', 'BbFDRSecurityKeyHash', 'BbSkeyId', 'BbNonce',
530
- '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
+ )
531
634
 
532
635
  for k in keys_to_copy:
533
636
  if k in parameters:
534
637
  self._request[k] = parameters[k]
535
638
 
536
- bb_chip_id = parameters['BbChipID']
537
- bb_cert_id = parameters['BbGoldCertId']
639
+ bb_chip_id = parameters["BbChipID"]
640
+ bb_cert_id = parameters["BbGoldCertId"]
538
641
 
539
- bbfwdict = dict(parameters['Manifest']['BasebandFirmware'])
540
- bbfwdict.pop('Info')
642
+ bbfwdict = dict(parameters["Manifest"]["BasebandFirmware"])
643
+ bbfwdict.pop("Info")
541
644
 
542
645
  if bb_chip_id == 0x68:
543
646
  # depending on the BasebandCertId remove certain nodes
544
647
  if bb_cert_id in (0x26F3FACC, 0x5CF2EC4E, 0x8399785A):
545
- bbfwdict.pop('PSI2-PartialDigest')
546
- bbfwdict.pop('RestorePSI2-PartialDigest')
648
+ bbfwdict.pop("PSI2-PartialDigest")
649
+ bbfwdict.pop("RestorePSI2-PartialDigest")
547
650
  else:
548
- bbfwdict.pop('PSI-PartialDigest')
549
- bbfwdict.pop('RestorePSI-PartialDigest')
651
+ bbfwdict.pop("PSI-PartialDigest")
652
+ bbfwdict.pop("RestorePSI-PartialDigest")
550
653
 
551
- self._request['BasebandFirmware'] = bbfwdict
654
+ self._request["BasebandFirmware"] = bbfwdict
552
655
 
553
656
  if overrides:
554
657
  self._request.update(overrides)
555
658
 
556
659
  def add_rose_tags(self, parameters: dict, overrides: typing.Optional[dict] = None):
557
- manifest = parameters['Manifest']
660
+ manifest = parameters["Manifest"]
558
661
 
559
662
  # add tags indicating we want to get the Rap,Ticket
560
- self._request['@BBTicket'] = True
561
- self._request['@Rap,Ticket'] = True
562
-
563
- 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
+ )
564
672
 
565
673
  for key in keys_to_copy_uint:
566
674
  value = get_with_or_without_comma(parameters, key)
@@ -570,41 +678,44 @@ class TSSRequest:
570
678
  else:
571
679
  self._request[key] = value
572
680
 
573
- keys_to_copy_bool = ('Rap,ProductionMode', 'Rap,SecurityMode',)
681
+ keys_to_copy_bool = (
682
+ "Rap,ProductionMode",
683
+ "Rap,SecurityMode",
684
+ )
574
685
 
575
686
  for key in keys_to_copy_bool:
576
687
  value = get_with_or_without_comma(parameters, key)
577
688
  self._request[key] = bytes_to_uint(value) == 1
578
689
 
579
- nonce = get_with_or_without_comma(parameters, 'Rap,Nonce')
690
+ nonce = get_with_or_without_comma(parameters, "Rap,Nonce")
580
691
  if nonce is not None:
581
- self._request['Rap,Nonce'] = nonce
692
+ self._request["Rap,Nonce"] = nonce
582
693
 
583
- digest = get_with_or_without_comma(parameters, 'Rap,FdrRootCaDigest')
694
+ digest = get_with_or_without_comma(parameters, "Rap,FdrRootCaDigest")
584
695
  if digest is not None:
585
- self._request['Rap,FdrRootCaDigest'] = digest
696
+ self._request["Rap,FdrRootCaDigest"] = digest
586
697
 
587
698
  for comp_name, node in manifest.items():
588
- if not comp_name.startswith('Rap,'):
699
+ if not comp_name.startswith("Rap,"):
589
700
  continue
590
701
 
591
702
  manifest_entry = dict(node)
592
703
 
593
704
  # handle RestoreRequestRules
594
- rules = manifest_entry['Info'].get('RestoreRequestRules')
705
+ rules = manifest_entry["Info"].get("RestoreRequestRules")
595
706
  if rules is not None:
596
707
  self.apply_restore_request_rules(manifest_entry, parameters, rules)
597
708
 
598
709
  # Make sure we have a Digest key for Trusted items even if empty
599
- trusted = manifest_entry.get('Trusted', False)
710
+ trusted = manifest_entry.get("Trusted", False)
600
711
 
601
712
  if trusted:
602
- digest = manifest_entry.get('Digest')
713
+ digest = manifest_entry.get("Digest")
603
714
  if digest is None:
604
- logger.debug(f'No Digest data, using empty value for entry {comp_name}')
605
- manifest_entry['Digest'] = b''
715
+ logger.debug(f"No Digest data, using empty value for entry {comp_name}")
716
+ manifest_entry["Digest"] = b""
606
717
 
607
- manifest_entry.pop('Info')
718
+ manifest_entry.pop("Info")
608
719
 
609
720
  # finally add entry to request
610
721
  self._request[comp_name] = manifest_entry
@@ -613,41 +724,41 @@ class TSSRequest:
613
724
  self._request.update(overrides)
614
725
 
615
726
  def add_veridian_tags(self, parameters: dict, overrides: typing.Optional[dict] = None):
616
- manifest = parameters['Manifest']
727
+ manifest = parameters["Manifest"]
617
728
 
618
729
  # add tags indicating we want to get the Rap,Ticket
619
- self._request['@BBTicket'] = True
620
- self._request['@BMU,Ticket'] = True
730
+ self._request["@BBTicket"] = True
731
+ self._request["@BMU,Ticket"] = True
621
732
 
622
- self._request['BMU,ChipID'] = parameters['ChipID']
623
- self._request['BMU,UniqueID'] = parameters['UniqueID']
624
- 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"]
625
736
 
626
- nonce = parameters.get('Nonce')
737
+ nonce = parameters.get("Nonce")
627
738
  if nonce is not None:
628
- self._request['BMU,Nonce'] = nonce
739
+ self._request["BMU,Nonce"] = nonce
629
740
 
630
741
  for comp_name, node in manifest.items():
631
- if not comp_name.startswith('BMU,'):
742
+ if not comp_name.startswith("BMU,"):
632
743
  continue
633
744
 
634
745
  manifest_entry = dict(node)
635
746
 
636
747
  # handle RestoreRequestRules
637
- rules = manifest_entry['Info'].get('RestoreRequestRules')
748
+ rules = manifest_entry["Info"].get("RestoreRequestRules")
638
749
  if rules is not None:
639
750
  self.apply_restore_request_rules(manifest_entry, parameters, rules)
640
751
 
641
752
  # Make sure we have a Digest key for Trusted items even if empty
642
- trusted = manifest_entry.get('Trusted', False)
753
+ trusted = manifest_entry.get("Trusted", False)
643
754
 
644
755
  if trusted:
645
- digest = manifest_entry.get('Digest')
756
+ digest = manifest_entry.get("Digest")
646
757
  if digest is None:
647
- logger.debug(f'No Digest data, using empty value for entry {comp_name}')
648
- manifest_entry['Digest'] = b''
758
+ logger.debug(f"No Digest data, using empty value for entry {comp_name}")
759
+ manifest_entry["Digest"] = b""
649
760
 
650
- manifest_entry.pop('Info')
761
+ manifest_entry.pop("Info")
651
762
 
652
763
  # finally add entry to request
653
764
  self._request[comp_name] = manifest_entry
@@ -656,14 +767,19 @@ class TSSRequest:
656
767
  self._request.update(overrides)
657
768
 
658
769
  def add_tcon_tags(self, parameters: dict, overrides: typing.Optional[dict] = None):
659
- manifest = parameters['Manifest']
770
+ manifest = parameters["Manifest"]
660
771
 
661
772
  # add tags indicating we want to get the Baobab,Ticket
662
- self._request['@BBTicket'] = True
663
- self._request['@Baobab,Ticket'] = True
664
-
665
- keys_to_copy_uint = ('Baobab,BoardID', 'Baobab,ChipID', 'Baobab,Life', 'Baobab,ManifestEpoch',
666
- '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
+ )
667
783
 
668
784
  for key in keys_to_copy_uint:
669
785
  value = get_with_or_without_comma(parameters, key)
@@ -673,26 +789,26 @@ class TSSRequest:
673
789
  else:
674
790
  self._request[key] = value
675
791
 
676
- isprod = bool(get_with_or_without_comma(parameters, 'Baobab,ProductionMode', False))
677
- self._request['Baobab,ProductionMode'] = isprod
792
+ isprod = bool(get_with_or_without_comma(parameters, "Baobab,ProductionMode", False))
793
+ self._request["Baobab,ProductionMode"] = isprod
678
794
 
679
- nonce = get_with_or_without_comma(parameters, 'Baobab,UpdateNonce')
795
+ nonce = get_with_or_without_comma(parameters, "Baobab,UpdateNonce")
680
796
 
681
797
  if nonce is not None:
682
- self._request['Baobab,UpdateNonce'] = nonce
798
+ self._request["Baobab,UpdateNonce"] = nonce
683
799
 
684
- ecid = get_with_or_without_comma(parameters, 'Baobab,ECID')
800
+ ecid = get_with_or_without_comma(parameters, "Baobab,ECID")
685
801
 
686
802
  if ecid is not None:
687
- self._request['Baobab,ECID'] = ecid
803
+ self._request["Baobab,ECID"] = ecid
688
804
 
689
805
  for comp_name, node in manifest.items():
690
- if not comp_name.startswith('Baobab,'):
806
+ if not comp_name.startswith("Baobab,"):
691
807
  continue
692
808
 
693
809
  manifest_entry = dict(node)
694
- manifest_entry.pop('Info')
695
- manifest_entry['EPRO'] = isprod
810
+ manifest_entry.pop("Info")
811
+ manifest_entry["EPRO"] = isprod
696
812
 
697
813
  # finally add entry to request
698
814
  self._request[comp_name] = manifest_entry
@@ -703,31 +819,31 @@ class TSSRequest:
703
819
  def img4_create_local_manifest(self, build_identity=None):
704
820
  manifest = None
705
821
  if build_identity is not None:
706
- manifest = build_identity['Manifest']
822
+ manifest = build_identity["Manifest"]
707
823
 
708
824
  p = asn1.Encoder()
709
825
  p.start()
710
826
 
711
- p.write(b'MANP', asn1.Numbers.IA5String)
827
+ p.write(b"MANP", asn1.Numbers.IA5String)
712
828
  p.enter(asn1.Numbers.Set)
713
829
 
714
- p.write(b'BORD', asn1.Numbers.IA5String)
715
- 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)
716
832
 
717
- p.write(b'CEPO', asn1.Numbers.IA5String)
833
+ p.write(b"CEPO", asn1.Numbers.IA5String)
718
834
  p.write(0, asn1.Numbers.Integer)
719
835
 
720
- p.write(b'CHIP', asn1.Numbers.IA5String)
721
- 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)
722
838
 
723
- p.write(b'CPRO', asn1.Numbers.IA5String)
724
- 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)
725
841
 
726
- p.write(b'CSEC', asn1.Numbers.IA5String)
842
+ p.write(b"CSEC", asn1.Numbers.IA5String)
727
843
  p.write(0, asn1.Numbers.Integer)
728
844
 
729
- p.write(b'SDOM', asn1.Numbers.IA5String)
730
- 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)
731
847
 
732
848
  p.leave()
733
849
 
@@ -737,23 +853,23 @@ class TSSRequest:
737
853
  # check if component has Img4PayloadType
738
854
  comp = None
739
855
  if manifest is not None:
740
- comp = manifest[k]['Info'].get('Img4PayloadType')
856
+ comp = manifest[k]["Info"].get("Img4PayloadType")
741
857
 
742
858
  if comp is None:
743
859
  comp = COMPONENT_FOURCC.get(k)
744
860
 
745
861
  if comp is None:
746
- raise NotImplementedError(f'Unhandled component {k} - can\'t create manifest')
862
+ raise NotImplementedError(f"Unhandled component {k} - can't create manifest")
747
863
 
748
- logger.debug(f'found component {comp} ({k})')
864
+ logger.debug(f"found component {comp} ({k})")
749
865
 
750
866
  # write manifest body header
751
- p.write(b'MANB', asn1.Numbers.IA5String)
867
+ p.write(b"MANB", asn1.Numbers.IA5String)
752
868
  p.enter(asn1.Numbers.Set)
753
869
  p.leave()
754
870
 
755
871
  # write header values
756
- p.write(b'IM4M', asn1.Numbers.IA5String)
872
+ p.write(b"IM4M", asn1.Numbers.IA5String)
757
873
  p.write(0, asn1.Numbers.Integer)
758
874
 
759
875
  return p.output()
@@ -767,28 +883,30 @@ class TSSRequest:
767
883
 
768
884
  async def send_receive(self) -> TSSResponse:
769
885
  headers = {
770
- 'Cache-Control': 'no-cache',
771
- 'Content-type': 'text/xml; charset="utf-8"',
772
- 'User-Agent': 'InetURL/1.0',
773
- 'Expect': '',
886
+ "Cache-Control": "no-cache",
887
+ "Content-type": 'text/xml; charset="utf-8"',
888
+ "User-Agent": "InetURL/1.0",
889
+ "Expect": "",
774
890
  }
775
891
 
776
- logger.info('Sending TSS request...')
892
+ logger.info("Sending TSS request...")
777
893
  logger.debug(self._request)
778
894
 
779
895
  loop = asyncio.get_event_loop()
780
896
  with ThreadPoolExecutor() as executor:
897
+
781
898
  def post() -> bytes:
782
- return requests.post(TSS_CONTROLLER_ACTION_URL, headers=headers, data=plistlib.dumps(self._request),
783
- verify=False).content
899
+ return requests.post(
900
+ TSS_CONTROLLER_ACTION_URL, headers=headers, data=plistlib.dumps(self._request), verify=False
901
+ ).content
784
902
 
785
903
  content = await loop.run_in_executor(executor, post)
786
904
 
787
- if b'MESSAGE=SUCCESS' in content:
788
- logger.info('response successfully received')
905
+ if b"MESSAGE=SUCCESS" in content:
906
+ logger.info("response successfully received")
789
907
 
790
- message = content.split(b'MESSAGE=', 1)[1].split(b'&', 1)[0].decode()
791
- if message != 'SUCCESS':
792
- 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}")
793
911
 
794
- return TSSResponse(plistlib.loads(content.split(b'REQUEST_STRING=', 1)[1]))
912
+ return TSSResponse(plistlib.loads(content.split(b"REQUEST_STRING=", 1)[1]))