]>
Commit | Line | Data |
---|---|---|
8b63877a KM |
1 | ## @file\r |
2 | # Generate a capsule.\r | |
3 | #\r | |
104a1aa1 | 4 | # This tool generates a UEFI Capsule around an FMP Capsule. The capsule payload\r |
d6f079b6 KM |
5 | # be signed using signtool or OpenSSL and if it is signed the signed content\r |
6 | # includes an FMP Payload Header.\r | |
7 | #\r | |
8 | # This tool is intended to be used to generate UEFI Capsules to update the\r | |
104a1aa1 | 9 | # system firmware or device firmware for integrated devices. In order to\r |
d6f079b6 | 10 | # keep the tool as simple as possible, it has the following limitations:\r |
d6f079b6 KM |
11 | # * Do not support vendor code bytes in a capsule.\r |
12 | #\r | |
b68d5664 | 13 | # Copyright (c) 2018 - 2022, Intel Corporation. All rights reserved.<BR>\r |
2e351cbe | 14 | # SPDX-License-Identifier: BSD-2-Clause-Patent\r |
8b63877a KM |
15 | #\r |
16 | \r | |
17 | '''\r | |
18 | GenerateCapsule\r | |
19 | '''\r | |
20 | \r | |
21 | import sys\r | |
22 | import argparse\r | |
23 | import uuid\r | |
24 | import struct\r | |
25 | import subprocess\r | |
26 | import os\r | |
27 | import tempfile\r | |
28 | import shutil\r | |
29 | import platform\r | |
104a1aa1 | 30 | import json\r |
8b63877a KM |
31 | from Common.Uefi.Capsule.UefiCapsuleHeader import UefiCapsuleHeaderClass\r |
32 | from Common.Uefi.Capsule.FmpCapsuleHeader import FmpCapsuleHeaderClass\r | |
33 | from Common.Uefi.Capsule.FmpAuthHeader import FmpAuthHeaderClass\r | |
f6f66e0c | 34 | from Common.Uefi.Capsule.CapsuleDependency import CapsuleDependencyClass\r |
8b63877a KM |
35 | from Common.Edk2.Capsule.FmpPayloadHeader import FmpPayloadHeaderClass\r |
36 | \r | |
37 | #\r | |
38 | # Globals for help information\r | |
39 | #\r | |
40 | __prog__ = 'GenerateCapsule'\r | |
b68d5664 JL |
41 | __version__ = '0.10'\r |
42 | __copyright__ = 'Copyright (c) 2022, Intel Corporation. All rights reserved.'\r | |
8b63877a KM |
43 | __description__ = 'Generate a capsule.\n'\r |
44 | \r | |
b68d5664 | 45 | def SignPayloadSignTool (Payload, ToolPath, PfxFile, SubjectName, Verbose = False):\r |
8b63877a KM |
46 | #\r |
47 | # Create a temporary directory\r | |
48 | #\r | |
49 | TempDirectoryName = tempfile.mkdtemp()\r | |
50 | \r | |
51 | #\r | |
52 | # Generate temp file name for the payload contents\r | |
53 | #\r | |
54 | TempFileName = os.path.join (TempDirectoryName, 'Payload.bin')\r | |
55 | \r | |
56 | #\r | |
57 | # Create temporary payload file for signing\r | |
58 | #\r | |
59 | try:\r | |
104a1aa1 JE |
60 | with open (TempFileName, 'wb') as File:\r |
61 | File.write (Payload)\r | |
8b63877a KM |
62 | except:\r |
63 | shutil.rmtree (TempDirectoryName)\r | |
64 | raise ValueError ('GenerateCapsule: error: can not write temporary payload file.')\r | |
65 | \r | |
66 | #\r | |
67 | # Build signtool command\r | |
68 | #\r | |
69 | if ToolPath is None:\r | |
70 | ToolPath = ''\r | |
71 | Command = ''\r | |
72 | Command = Command + '"{Path}" '.format (Path = os.path.join (ToolPath, 'signtool.exe'))\r | |
73 | Command = Command + 'sign /fd sha256 /p7ce DetachedSignedData /p7co 1.2.840.113549.1.7.2 '\r | |
74 | Command = Command + '/p7 {TempDir} '.format (TempDir = TempDirectoryName)\r | |
b68d5664 JL |
75 | if PfxFile is not None:\r |
76 | Command = Command + '/f {PfxFile} '.format (PfxFile = PfxFile)\r | |
77 | if SubjectName is not None:\r | |
78 | Command = Command + '/n {SubjectName} '.format (SubjectName = SubjectName)\r | |
8b63877a | 79 | Command = Command + TempFileName\r |
104a1aa1 JE |
80 | if Verbose:\r |
81 | print (Command)\r | |
8b63877a KM |
82 | \r |
83 | #\r | |
84 | # Sign the input file using the specified private key\r | |
85 | #\r | |
86 | try:\r | |
87 | Process = subprocess.Popen (Command, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = True)\r | |
88 | Result = Process.communicate('')\r | |
89 | except:\r | |
90 | shutil.rmtree (TempDirectoryName)\r | |
91 | raise ValueError ('GenerateCapsule: error: can not run signtool.')\r | |
92 | \r | |
93 | if Process.returncode != 0:\r | |
94 | shutil.rmtree (TempDirectoryName)\r | |
104a1aa1 | 95 | print (Result[1].decode())\r |
8b63877a KM |
96 | raise ValueError ('GenerateCapsule: error: signtool failed.')\r |
97 | \r | |
98 | #\r | |
99 | # Read the signature from the generated output file\r | |
100 | #\r | |
101 | try:\r | |
104a1aa1 JE |
102 | with open (TempFileName + '.p7', 'rb') as File:\r |
103 | Signature = File.read ()\r | |
8b63877a KM |
104 | except:\r |
105 | shutil.rmtree (TempDirectoryName)\r | |
106 | raise ValueError ('GenerateCapsule: error: can not read signature file.')\r | |
107 | \r | |
108 | shutil.rmtree (TempDirectoryName)\r | |
109 | return Signature\r | |
110 | \r | |
b68d5664 | 111 | def VerifyPayloadSignTool (Payload, CertData, ToolPath, PfxFile, SubjectName, Verbose = False):\r |
8b63877a KM |
112 | print ('signtool verify is not supported.')\r |
113 | raise ValueError ('GenerateCapsule: error: signtool verify is not supported.')\r | |
114 | \r | |
104a1aa1 | 115 | def SignPayloadOpenSsl (Payload, ToolPath, SignerPrivateCertFile, OtherPublicCertFile, TrustedPublicCertFile, Verbose = False):\r |
8b63877a KM |
116 | #\r |
117 | # Build openssl command\r | |
118 | #\r | |
119 | if ToolPath is None:\r | |
120 | ToolPath = ''\r | |
121 | Command = ''\r | |
122 | Command = Command + '"{Path}" '.format (Path = os.path.join (ToolPath, 'openssl'))\r | |
123 | Command = Command + 'smime -sign -binary -outform DER -md sha256 '\r | |
124 | Command = Command + '-signer "{Private}" -certfile "{Public}"'.format (Private = SignerPrivateCertFile, Public = OtherPublicCertFile)\r | |
104a1aa1 JE |
125 | if Verbose:\r |
126 | print (Command)\r | |
8b63877a KM |
127 | \r |
128 | #\r | |
129 | # Sign the input file using the specified private key and capture signature from STDOUT\r | |
130 | #\r | |
131 | try:\r | |
132 | Process = subprocess.Popen (Command, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = True)\r | |
133 | Result = Process.communicate(input = Payload)\r | |
87bfb9bc | 134 | Signature = Result[0]\r |
8b63877a KM |
135 | except:\r |
136 | raise ValueError ('GenerateCapsule: error: can not run openssl.')\r | |
137 | \r | |
138 | if Process.returncode != 0:\r | |
104a1aa1 | 139 | print (Result[1].decode())\r |
8b63877a KM |
140 | raise ValueError ('GenerateCapsule: error: openssl failed.')\r |
141 | \r | |
142 | return Signature\r | |
143 | \r | |
104a1aa1 | 144 | def VerifyPayloadOpenSsl (Payload, CertData, ToolPath, SignerPrivateCertFile, OtherPublicCertFile, TrustedPublicCertFile, Verbose = False):\r |
8b63877a KM |
145 | #\r |
146 | # Create a temporary directory\r | |
147 | #\r | |
148 | TempDirectoryName = tempfile.mkdtemp()\r | |
149 | \r | |
150 | #\r | |
151 | # Generate temp file name for the payload contents\r | |
152 | #\r | |
153 | TempFileName = os.path.join (TempDirectoryName, 'Payload.bin')\r | |
154 | \r | |
155 | #\r | |
156 | # Create temporary payload file for verification\r | |
157 | #\r | |
158 | try:\r | |
104a1aa1 JE |
159 | with open (TempFileName, 'wb') as File:\r |
160 | File.write (Payload)\r | |
8b63877a KM |
161 | except:\r |
162 | shutil.rmtree (TempDirectoryName)\r | |
163 | raise ValueError ('GenerateCapsule: error: can not write temporary payload file.')\r | |
164 | \r | |
165 | #\r | |
166 | # Build openssl command\r | |
167 | #\r | |
168 | if ToolPath is None:\r | |
169 | ToolPath = ''\r | |
170 | Command = ''\r | |
171 | Command = Command + '"{Path}" '.format (Path = os.path.join (ToolPath, 'openssl'))\r | |
172 | Command = Command + 'smime -verify -inform DER '\r | |
173 | Command = Command + '-content {Content} -CAfile "{Public}"'.format (Content = TempFileName, Public = TrustedPublicCertFile)\r | |
104a1aa1 JE |
174 | if Verbose:\r |
175 | print (Command)\r | |
8b63877a KM |
176 | \r |
177 | #\r | |
178 | # Verify signature\r | |
179 | #\r | |
180 | try:\r | |
181 | Process = subprocess.Popen (Command, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = True)\r | |
182 | Result = Process.communicate(input = CertData)\r | |
183 | except:\r | |
184 | shutil.rmtree (TempDirectoryName)\r | |
185 | raise ValueError ('GenerateCapsule: error: can not run openssl.')\r | |
186 | \r | |
187 | if Process.returncode != 0:\r | |
188 | shutil.rmtree (TempDirectoryName)\r | |
104a1aa1 | 189 | print (Result[1].decode())\r |
8b63877a KM |
190 | raise ValueError ('GenerateCapsule: error: openssl failed.')\r |
191 | \r | |
192 | shutil.rmtree (TempDirectoryName)\r | |
193 | return Payload\r | |
194 | \r | |
195 | if __name__ == '__main__':\r | |
196 | def convert_arg_line_to_args(arg_line):\r | |
197 | for arg in arg_line.split():\r | |
198 | if not arg.strip():\r | |
199 | continue\r | |
200 | yield arg\r | |
201 | \r | |
202 | def ValidateUnsignedInteger (Argument):\r | |
203 | try:\r | |
204 | Value = int (Argument, 0)\r | |
205 | except:\r | |
206 | Message = '{Argument} is not a valid integer value.'.format (Argument = Argument)\r | |
207 | raise argparse.ArgumentTypeError (Message)\r | |
208 | if Value < 0:\r | |
209 | Message = '{Argument} is a negative value.'.format (Argument = Argument)\r | |
210 | raise argparse.ArgumentTypeError (Message)\r | |
211 | return Value\r | |
212 | \r | |
213 | def ValidateRegistryFormatGuid (Argument):\r | |
214 | try:\r | |
215 | Value = uuid.UUID (Argument)\r | |
216 | except:\r | |
217 | Message = '{Argument} is not a valid registry format GUID value.'.format (Argument = Argument)\r | |
218 | raise argparse.ArgumentTypeError (Message)\r | |
219 | return Value\r | |
220 | \r | |
104a1aa1 JE |
221 | def ConvertJsonValue (Config, FieldName, Convert, Required = True, Default = None, Open = False):\r |
222 | if FieldName not in Config:\r | |
223 | if Required:\r | |
224 | print ('GenerateCapsule: error: Payload descriptor invalid syntax. Could not find {Key} in payload descriptor.'.format(Key = FieldName))\r | |
225 | sys.exit (1)\r | |
226 | return Default\r | |
227 | try:\r | |
228 | Value = Convert (Config[FieldName])\r | |
229 | except:\r | |
230 | print ('GenerateCapsule: error: {Key} in payload descriptor has invalid syntax.'.format (Key = FieldName))\r | |
231 | sys.exit (1)\r | |
232 | if Open:\r | |
233 | try:\r | |
234 | Value = open (Value, "rb")\r | |
235 | except:\r | |
236 | print ('GenerateCapsule: error: can not open file {File}'.format (File = FieldName))\r | |
237 | sys.exit (1)\r | |
238 | return Value\r | |
239 | \r | |
240 | def DecodeJsonFileParse (Json):\r | |
241 | if 'Payloads' not in Json:\r | |
242 | print ('GenerateCapsule: error "Payloads" section not found in JSON file {File}'.format (File = args.JsonFile.name))\r | |
243 | sys.exit (1)\r | |
244 | for Config in Json['Payloads']:\r | |
245 | #\r | |
246 | # Parse fields from JSON\r | |
247 | #\r | |
248 | PayloadFile = ConvertJsonValue (Config, 'Payload', os.path.expandvars, Required = False)\r | |
249 | Guid = ConvertJsonValue (Config, 'Guid', ValidateRegistryFormatGuid, Required = False)\r | |
250 | FwVersion = ConvertJsonValue (Config, 'FwVersion', ValidateUnsignedInteger, Required = False)\r | |
251 | LowestSupportedVersion = ConvertJsonValue (Config, 'LowestSupportedVersion', ValidateUnsignedInteger, Required = False)\r | |
252 | HardwareInstance = ConvertJsonValue (Config, 'HardwareInstance', ValidateUnsignedInteger, Required = False, Default = 0)\r | |
253 | MonotonicCount = ConvertJsonValue (Config, 'MonotonicCount', ValidateUnsignedInteger, Required = False, Default = 0)\r | |
254 | SignToolPfxFile = ConvertJsonValue (Config, 'SignToolPfxFile', os.path.expandvars, Required = False, Default = None, Open = True)\r | |
b68d5664 | 255 | SignToolSubjectName = ConvertJsonValue (Config, 'SignToolSubjectName', os.path.expandvars, Required = False, Default = None, Open = True)\r |
104a1aa1 JE |
256 | OpenSslSignerPrivateCertFile = ConvertJsonValue (Config, 'OpenSslSignerPrivateCertFile', os.path.expandvars, Required = False, Default = None, Open = True)\r |
257 | OpenSslOtherPublicCertFile = ConvertJsonValue (Config, 'OpenSslOtherPublicCertFile', os.path.expandvars, Required = False, Default = None, Open = True)\r | |
258 | OpenSslTrustedPublicCertFile = ConvertJsonValue (Config, 'OpenSslTrustedPublicCertFile', os.path.expandvars, Required = False, Default = None, Open = True)\r | |
259 | SigningToolPath = ConvertJsonValue (Config, 'SigningToolPath', os.path.expandvars, Required = False, Default = None)\r | |
260 | UpdateImageIndex = ConvertJsonValue (Config, 'UpdateImageIndex', ValidateUnsignedInteger, Required = False, Default = 1)\r | |
261 | \r | |
262 | PayloadDescriptorList.append (PayloadDescriptor (\r | |
263 | PayloadFile,\r | |
264 | Guid,\r | |
265 | FwVersion,\r | |
266 | LowestSupportedVersion,\r | |
267 | MonotonicCount,\r | |
268 | HardwareInstance,\r | |
269 | UpdateImageIndex,\r | |
270 | SignToolPfxFile,\r | |
b68d5664 | 271 | SignToolSubjectName,\r |
104a1aa1 JE |
272 | OpenSslSignerPrivateCertFile,\r |
273 | OpenSslOtherPublicCertFile,\r | |
274 | OpenSslTrustedPublicCertFile,\r | |
275 | SigningToolPath\r | |
276 | ))\r | |
277 | \r | |
278 | def EncodeJsonFileParse (Json):\r | |
279 | if 'EmbeddedDrivers' not in Json:\r | |
280 | print ('GenerateCapsule: warning "EmbeddedDrivers" section not found in JSON file {File}'.format (File = args.JsonFile.name))\r | |
281 | else:\r | |
282 | for Config in Json['EmbeddedDrivers']:\r | |
283 | EmbeddedDriverFile = ConvertJsonValue(Config, 'Driver', os.path.expandvars, Open = True)\r | |
284 | #\r | |
285 | #Read EmbeddedDriver file\r | |
286 | #\r | |
287 | try:\r | |
288 | if args.Verbose:\r | |
289 | print ('Read EmbeddedDriver file {File}'.format (File = EmbeddedDriverFile.name))\r | |
290 | Driver = EmbeddedDriverFile.read()\r | |
291 | except:\r | |
292 | print ('GenerateCapsule: error: can not read EmbeddedDriver file {File}'.format (File = EmbeddedDriverFile.name))\r | |
293 | sys.exit (1)\r | |
294 | EmbeddedDriverDescriptorList.append (Driver)\r | |
295 | \r | |
296 | if 'Payloads' not in Json:\r | |
297 | print ('GenerateCapsule: error: "Payloads" section not found in JSON file {File}'.format (File = args.JsonFile.name))\r | |
298 | sys.exit (1)\r | |
299 | for Config in Json['Payloads']:\r | |
300 | #\r | |
301 | # Parse fields from JSON\r | |
302 | #\r | |
303 | PayloadFile = ConvertJsonValue (Config, 'Payload', os.path.expandvars, Open = True)\r | |
304 | Guid = ConvertJsonValue (Config, 'Guid', ValidateRegistryFormatGuid)\r | |
305 | FwVersion = ConvertJsonValue (Config, 'FwVersion', ValidateUnsignedInteger)\r | |
306 | LowestSupportedVersion = ConvertJsonValue (Config, 'LowestSupportedVersion', ValidateUnsignedInteger)\r | |
307 | HardwareInstance = ConvertJsonValue (Config, 'HardwareInstance', ValidateUnsignedInteger, Required = False, Default = 0)\r | |
308 | UpdateImageIndex = ConvertJsonValue (Config, 'UpdateImageIndex', ValidateUnsignedInteger, Required = False, Default = 1)\r | |
309 | MonotonicCount = ConvertJsonValue (Config, 'MonotonicCount', ValidateUnsignedInteger, Required = False, Default = 0)\r | |
310 | SignToolPfxFile = ConvertJsonValue (Config, 'SignToolPfxFile', os.path.expandvars, Required = False, Default = None, Open = True)\r | |
b68d5664 | 311 | SignToolSubjectName = ConvertJsonValue (Config, 'SignToolSubjectName', os.path.expandvars, Required = False, Default = None, Open = True)\r |
104a1aa1 JE |
312 | OpenSslSignerPrivateCertFile = ConvertJsonValue (Config, 'OpenSslSignerPrivateCertFile', os.path.expandvars, Required = False, Default = None, Open = True)\r |
313 | OpenSslOtherPublicCertFile = ConvertJsonValue (Config, 'OpenSslOtherPublicCertFile', os.path.expandvars, Required = False, Default = None, Open = True)\r | |
314 | OpenSslTrustedPublicCertFile = ConvertJsonValue (Config, 'OpenSslTrustedPublicCertFile', os.path.expandvars, Required = False, Default = None, Open = True)\r | |
315 | SigningToolPath = ConvertJsonValue (Config, 'SigningToolPath', os.path.expandvars, Required = False, Default = None)\r | |
f6f66e0c | 316 | DepexExp = ConvertJsonValue (Config, 'Dependencies', str, Required = False, Default = None)\r |
104a1aa1 JE |
317 | \r |
318 | #\r | |
319 | # Read binary input file\r | |
320 | #\r | |
321 | try:\r | |
322 | if args.Verbose:\r | |
323 | print ('Read binary input file {File}'.format (File = PayloadFile.name))\r | |
324 | Payload = PayloadFile.read()\r | |
325 | PayloadFile.close ()\r | |
326 | except:\r | |
327 | print ('GenerateCapsule: error: can not read binary input file {File}'.format (File = PayloadFile.name))\r | |
328 | sys.exit (1)\r | |
329 | PayloadDescriptorList.append (PayloadDescriptor (\r | |
330 | Payload,\r | |
331 | Guid,\r | |
332 | FwVersion,\r | |
333 | LowestSupportedVersion,\r | |
334 | MonotonicCount,\r | |
335 | HardwareInstance,\r | |
336 | UpdateImageIndex,\r | |
337 | SignToolPfxFile,\r | |
b68d5664 | 338 | SignToolSubjectName,\r |
104a1aa1 JE |
339 | OpenSslSignerPrivateCertFile,\r |
340 | OpenSslOtherPublicCertFile,\r | |
341 | OpenSslTrustedPublicCertFile,\r | |
f6f66e0c LA |
342 | SigningToolPath,\r |
343 | DepexExp\r | |
104a1aa1 JE |
344 | ))\r |
345 | \r | |
346 | def GenerateOutputJson (PayloadJsonDescriptorList):\r | |
347 | PayloadJson = {\r | |
348 | "Payloads" : [\r | |
349 | {\r | |
350 | "Guid": str(PayloadDescriptor.Guid).upper(),\r | |
351 | "FwVersion": str(PayloadDescriptor.FwVersion),\r | |
352 | "LowestSupportedVersion": str(PayloadDescriptor.LowestSupportedVersion),\r | |
353 | "MonotonicCount": str(PayloadDescriptor.MonotonicCount),\r | |
354 | "Payload": PayloadDescriptor.Payload,\r | |
355 | "HardwareInstance": str(PayloadDescriptor.HardwareInstance),\r | |
356 | "UpdateImageIndex": str(PayloadDescriptor.UpdateImageIndex),\r | |
357 | "SignToolPfxFile": str(PayloadDescriptor.SignToolPfxFile),\r | |
b68d5664 | 358 | "SignToolSubjectName": str(PayloadDescriptor.SignToolSubjectName),\r |
104a1aa1 JE |
359 | "OpenSslSignerPrivateCertFile": str(PayloadDescriptor.OpenSslSignerPrivateCertFile),\r |
360 | "OpenSslOtherPublicCertFile": str(PayloadDescriptor.OpenSslOtherPublicCertFile),\r | |
361 | "OpenSslTrustedPublicCertFile": str(PayloadDescriptor.OpenSslTrustedPublicCertFile),\r | |
f6f66e0c LA |
362 | "SigningToolPath": str(PayloadDescriptor.SigningToolPath),\r |
363 | "Dependencies" : str(PayloadDescriptor.DepexExp)\r | |
104a1aa1 JE |
364 | }for PayloadDescriptor in PayloadJsonDescriptorList\r |
365 | ]\r | |
366 | }\r | |
367 | OutputJsonFile = args.OutputFile.name + '.json'\r | |
368 | if 'Payloads' in PayloadJson:\r | |
369 | PayloadSection = PayloadJson ['Payloads']\r | |
370 | Index = 0\r | |
371 | for PayloadField in PayloadSection:\r | |
372 | if PayloadJsonDescriptorList[Index].SignToolPfxFile is None:\r | |
373 | del PayloadField ['SignToolPfxFile']\r | |
b68d5664 JL |
374 | if PayloadJsonDescriptorList[Index].SignToolSubjectName is None:\r |
375 | del PayloadField ['SignToolSubjectName']\r | |
104a1aa1 JE |
376 | if PayloadJsonDescriptorList[Index].OpenSslSignerPrivateCertFile is None:\r |
377 | del PayloadField ['OpenSslSignerPrivateCertFile']\r | |
378 | if PayloadJsonDescriptorList[Index].OpenSslOtherPublicCertFile is None:\r | |
379 | del PayloadField ['OpenSslOtherPublicCertFile']\r | |
380 | if PayloadJsonDescriptorList[Index].OpenSslTrustedPublicCertFile is None:\r | |
381 | del PayloadField ['OpenSslTrustedPublicCertFile']\r | |
382 | if PayloadJsonDescriptorList[Index].SigningToolPath is None:\r | |
383 | del PayloadField ['SigningToolPath']\r | |
384 | Index = Index + 1\r | |
385 | Result = json.dumps (PayloadJson, indent=4, sort_keys=True, separators=(',', ': '))\r | |
386 | with open (OutputJsonFile, 'w') as OutputFile:\r | |
387 | OutputFile.write (Result)\r | |
388 | \r | |
389 | def CheckArgumentConflict (args):\r | |
390 | if args.Encode:\r | |
391 | if args.InputFile:\r | |
392 | print ('GenerateCapsule: error: Argument InputFile conflicts with Argument -j')\r | |
393 | sys.exit (1)\r | |
394 | if args.EmbeddedDriver:\r | |
395 | print ('GenerateCapsule: error: Argument --embedded-driver conflicts with Argument -j')\r | |
396 | sys.exit (1)\r | |
397 | if args.Guid:\r | |
398 | print ('GenerateCapsule: error: Argument --guid conflicts with Argument -j')\r | |
399 | sys.exit (1)\r | |
400 | if args.FwVersion:\r | |
401 | print ('GenerateCapsule: error: Argument --fw-version conflicts with Argument -j')\r | |
402 | sys.exit (1)\r | |
403 | if args.LowestSupportedVersion:\r | |
404 | print ('GenerateCapsule: error: Argument --lsv conflicts with Argument -j')\r | |
405 | sys.exit (1)\r | |
406 | if args.MonotonicCount:\r | |
407 | print ('GenerateCapsule: error: Argument --monotonic-count conflicts with Argument -j')\r | |
408 | sys.exit (1)\r | |
409 | if args.HardwareInstance:\r | |
410 | print ('GenerateCapsule: error: Argument --hardware-instance conflicts with Argument -j')\r | |
411 | sys.exit (1)\r | |
412 | if args.SignToolPfxFile:\r | |
413 | print ('GenerateCapsule: error: Argument --pfx-file conflicts with Argument -j')\r | |
414 | sys.exit (1)\r | |
b68d5664 JL |
415 | if args.SignToolSubjectName:\r |
416 | print ('GenerateCapsule: error: Argument --SubjectName conflicts with Argument -j')\r | |
417 | sys.exit (1)\r | |
104a1aa1 JE |
418 | if args.OpenSslSignerPrivateCertFile:\r |
419 | print ('GenerateCapsule: error: Argument --signer-private-cert conflicts with Argument -j')\r | |
420 | sys.exit (1)\r | |
421 | if args.OpenSslOtherPublicCertFile:\r | |
422 | print ('GenerateCapsule: error: Argument --other-public-cert conflicts with Argument -j')\r | |
423 | sys.exit (1)\r | |
424 | if args.OpenSslTrustedPublicCertFile:\r | |
425 | print ('GenerateCapsule: error: Argument --trusted-public-cert conflicts with Argument -j')\r | |
426 | sys.exit (1)\r | |
427 | if args.SigningToolPath:\r | |
428 | print ('GenerateCapsule: error: Argument --signing-tool-path conflicts with Argument -j')\r | |
429 | sys.exit (1)\r | |
430 | \r | |
431 | class PayloadDescriptor (object):\r | |
432 | def __init__(self,\r | |
433 | Payload,\r | |
434 | Guid,\r | |
435 | FwVersion,\r | |
436 | LowestSupportedVersion,\r | |
437 | MonotonicCount = 0,\r | |
438 | HardwareInstance = 0,\r | |
439 | UpdateImageIndex = 1,\r | |
440 | SignToolPfxFile = None,\r | |
b68d5664 | 441 | SignToolSubjectName = None,\r |
104a1aa1 JE |
442 | OpenSslSignerPrivateCertFile = None,\r |
443 | OpenSslOtherPublicCertFile = None,\r | |
444 | OpenSslTrustedPublicCertFile = None,\r | |
f6f66e0c LA |
445 | SigningToolPath = None,\r |
446 | DepexExp = None\r | |
104a1aa1 JE |
447 | ):\r |
448 | self.Payload = Payload\r | |
449 | self.Guid = Guid\r | |
450 | self.FwVersion = FwVersion\r | |
451 | self.LowestSupportedVersion = LowestSupportedVersion\r | |
452 | self.MonotonicCount = MonotonicCount\r | |
453 | self.HardwareInstance = HardwareInstance\r | |
454 | self.UpdateImageIndex = UpdateImageIndex\r | |
455 | self.SignToolPfxFile = SignToolPfxFile\r | |
b68d5664 | 456 | self.SignToolSubjectName = SignToolSubjectName\r |
104a1aa1 JE |
457 | self.OpenSslSignerPrivateCertFile = OpenSslSignerPrivateCertFile\r |
458 | self.OpenSslOtherPublicCertFile = OpenSslOtherPublicCertFile\r | |
459 | self.OpenSslTrustedPublicCertFile = OpenSslTrustedPublicCertFile\r | |
460 | self.SigningToolPath = SigningToolPath\r | |
f6f66e0c | 461 | self.DepexExp = DepexExp\r |
104a1aa1 | 462 | \r |
b68d5664 JL |
463 | self.UseSignTool = (self.SignToolPfxFile is not None or\r |
464 | self.SignToolSubjectName is not None)\r | |
104a1aa1 JE |
465 | self.UseOpenSsl = (self.OpenSslSignerPrivateCertFile is not None and\r |
466 | self.OpenSslOtherPublicCertFile is not None and\r | |
467 | self.OpenSslTrustedPublicCertFile is not None)\r | |
468 | self.AnyOpenSsl = (self.OpenSslSignerPrivateCertFile is not None or\r | |
469 | self.OpenSslOtherPublicCertFile is not None or\r | |
470 | self.OpenSslTrustedPublicCertFile is not None)\r | |
f6f66e0c | 471 | self.UseDependency = self.DepexExp is not None\r |
104a1aa1 JE |
472 | \r |
473 | def Validate(self, args):\r | |
474 | if self.UseSignTool and self.AnyOpenSsl:\r | |
475 | raise argparse.ArgumentTypeError ('Providing both signtool and OpenSSL options is not supported')\r | |
476 | if not self.UseSignTool and not self.UseOpenSsl and self.AnyOpenSsl:\r | |
477 | if args.JsonFile:\r | |
478 | raise argparse.ArgumentTypeError ('the following JSON fields are required for OpenSSL: OpenSslSignerPrivateCertFile, OpenSslOtherPublicCertFile, OpenSslTrustedPublicCertFile')\r | |
479 | else:\r | |
480 | raise argparse.ArgumentTypeError ('the following options are required for OpenSSL: --signer-private-cert, --other-public-cert, --trusted-public-cert')\r | |
481 | if self.UseSignTool and platform.system() != 'Windows':\r | |
482 | raise argparse.ArgumentTypeError ('Use of signtool is not supported on this operating system.')\r | |
483 | if args.Encode:\r | |
484 | if self.FwVersion is None or self.LowestSupportedVersion is None:\r | |
485 | if args.JsonFile:\r | |
486 | raise argparse.ArgumentTypeError ('the following JSON fields are required: FwVersion, LowestSupportedVersion')\r | |
487 | else:\r | |
488 | raise argparse.ArgumentTypeError ('the following options are required: --fw-version, --lsv')\r | |
489 | if self.FwVersion > 0xFFFFFFFF:\r | |
490 | if args.JsonFile:\r | |
491 | raise argparse.ArgumentTypeError ('JSON field FwVersion must be an integer in range 0x0..0xffffffff')\r | |
492 | else:\r | |
493 | raise argparse.ArgumentTypeError ('--fw-version must be an integer in range 0x0..0xffffffff')\r | |
494 | if self.LowestSupportedVersion > 0xFFFFFFFF:\r | |
495 | if args.JsonFile:\r | |
496 | raise argparse.ArgumentTypeError ('JSON field LowestSupportedVersion must be an integer in range 0x0..0xffffffff')\r | |
497 | else:\r | |
498 | raise argparse.ArgumentTypeError ('--lsv must be an integer in range 0x0..0xffffffff')\r | |
499 | \r | |
500 | if args.Encode:\r | |
501 | if self.Guid is None:\r | |
502 | if args.JsonFile:\r | |
503 | raise argparse.ArgumentTypeError ('the following JSON field is required: Guid')\r | |
504 | else:\r | |
505 | raise argparse.ArgumentTypeError ('the following option is required: --guid')\r | |
506 | if self.HardwareInstance > 0xFFFFFFFFFFFFFFFF:\r | |
507 | if args.JsonFile:\r | |
508 | raise argparse.ArgumentTypeError ('JSON field HardwareInstance must be an integer in range 0x0..0xffffffffffffffff')\r | |
509 | else:\r | |
510 | raise argparse.ArgumentTypeError ('--hardware-instance must be an integer in range 0x0..0xffffffffffffffff')\r | |
511 | if self.MonotonicCount > 0xFFFFFFFFFFFFFFFF:\r | |
512 | if args.JsonFile:\r | |
513 | raise argparse.ArgumentTypeError ('JSON field MonotonicCount must be an integer in range 0x0..0xffffffffffffffff')\r | |
514 | else:\r | |
515 | raise argparse.ArgumentTypeError ('--monotonic-count must be an integer in range 0x0..0xffffffffffffffff')\r | |
516 | if self.UpdateImageIndex >0xFF:\r | |
517 | if args.JsonFile:\r | |
518 | raise argparse.ArgumentTypeError ('JSON field UpdateImageIndex must be an integer in range 0x0..0xff')\r | |
519 | else:\r | |
520 | raise argparse.ArgumentTypeError ('--update-image-index must be an integer in range 0x0..0xff')\r | |
521 | \r | |
522 | if self.UseSignTool:\r | |
b68d5664 JL |
523 | if self.SignToolPfxFile is not None:\r |
524 | self.SignToolPfxFile.close()\r | |
525 | self.SignToolPfxFile = self.SignToolPfxFile.name\r | |
104a1aa1 JE |
526 | if self.UseOpenSsl:\r |
527 | self.OpenSslSignerPrivateCertFile.close()\r | |
528 | self.OpenSslOtherPublicCertFile.close()\r | |
529 | self.OpenSslTrustedPublicCertFile.close()\r | |
530 | self.OpenSslSignerPrivateCertFile = self.OpenSslSignerPrivateCertFile.name\r | |
531 | self.OpenSslOtherPublicCertFile = self.OpenSslOtherPublicCertFile.name\r | |
532 | self.OpenSslTrustedPublicCertFile = self.OpenSslTrustedPublicCertFile.name\r | |
533 | \r | |
534 | #\r | |
535 | # Perform additional argument verification\r | |
536 | #\r | |
537 | if args.Encode:\r | |
538 | if 'PersistAcrossReset' not in args.CapsuleFlag:\r | |
539 | if 'InitiateReset' in args.CapsuleFlag:\r | |
540 | raise argparse.ArgumentTypeError ('--capflag InitiateReset also requires --capflag PersistAcrossReset')\r | |
541 | if args.CapsuleOemFlag > 0xFFFF:\r | |
542 | raise argparse.ArgumentTypeError ('--capoemflag must be an integer between 0x0000 and 0xffff')\r | |
543 | \r | |
544 | return True\r | |
545 | \r | |
546 | \r | |
547 | def Encode (PayloadDescriptorList, EmbeddedDriverDescriptorList, Buffer):\r | |
548 | if args.JsonFile:\r | |
549 | CheckArgumentConflict(args)\r | |
550 | try:\r | |
551 | Json = json.loads (args.JsonFile.read ())\r | |
552 | except:\r | |
553 | print ('GenerateCapsule: error: {JSONFile} loads failure. '.format (JSONFile = args.JsonFile))\r | |
554 | sys.exit (1)\r | |
555 | EncodeJsonFileParse(Json)\r | |
556 | else:\r | |
557 | for Driver in args.EmbeddedDriver:\r | |
558 | EmbeddedDriverDescriptorList.append (Driver.read())\r | |
559 | PayloadDescriptorList.append (PayloadDescriptor (\r | |
560 | Buffer,\r | |
561 | args.Guid,\r | |
562 | args.FwVersion,\r | |
563 | args.LowestSupportedVersion,\r | |
564 | args.MonotonicCount,\r | |
565 | args.HardwareInstance,\r | |
566 | args.UpdateImageIndex,\r | |
567 | args.SignToolPfxFile,\r | |
b68d5664 | 568 | args.SignToolSubjectName,\r |
104a1aa1 JE |
569 | args.OpenSslSignerPrivateCertFile,\r |
570 | args.OpenSslOtherPublicCertFile,\r | |
571 | args.OpenSslTrustedPublicCertFile,\r | |
f6f66e0c LA |
572 | args.SigningToolPath,\r |
573 | None\r | |
104a1aa1 JE |
574 | ))\r |
575 | for SinglePayloadDescriptor in PayloadDescriptorList:\r | |
576 | try:\r | |
577 | SinglePayloadDescriptor.Validate (args)\r | |
578 | except Exception as Msg:\r | |
579 | print ('GenerateCapsule: error:' + str(Msg))\r | |
580 | sys.exit (1)\r | |
581 | for SinglePayloadDescriptor in PayloadDescriptorList:\r | |
5531fd48 | 582 | ImageCapsuleSupport = 0x0000000000000000\r |
104a1aa1 JE |
583 | Result = SinglePayloadDescriptor.Payload\r |
584 | try:\r | |
585 | FmpPayloadHeader.FwVersion = SinglePayloadDescriptor.FwVersion\r | |
586 | FmpPayloadHeader.LowestSupportedVersion = SinglePayloadDescriptor.LowestSupportedVersion\r | |
587 | FmpPayloadHeader.Payload = SinglePayloadDescriptor.Payload\r | |
588 | Result = FmpPayloadHeader.Encode ()\r | |
589 | if args.Verbose:\r | |
590 | FmpPayloadHeader.DumpInfo ()\r | |
591 | except:\r | |
592 | print ('GenerateCapsule: error: can not encode FMP Payload Header')\r | |
593 | sys.exit (1)\r | |
f6f66e0c LA |
594 | if SinglePayloadDescriptor.UseDependency:\r |
595 | CapsuleDependency.Payload = Result\r | |
596 | CapsuleDependency.DepexExp = SinglePayloadDescriptor.DepexExp\r | |
5531fd48 | 597 | ImageCapsuleSupport |= FmpCapsuleHeader.CAPSULE_SUPPORT_DEPENDENCY\r |
f6f66e0c LA |
598 | Result = CapsuleDependency.Encode ()\r |
599 | if args.Verbose:\r | |
600 | CapsuleDependency.DumpInfo ()\r | |
104a1aa1 JE |
601 | if SinglePayloadDescriptor.UseOpenSsl or SinglePayloadDescriptor.UseSignTool:\r |
602 | #\r | |
603 | # Sign image with 64-bit MonotonicCount appended to end of image\r | |
604 | #\r | |
605 | try:\r | |
606 | if SinglePayloadDescriptor.UseSignTool:\r | |
607 | CertData = SignPayloadSignTool (\r | |
608 | Result + struct.pack ('<Q', SinglePayloadDescriptor.MonotonicCount),\r | |
609 | SinglePayloadDescriptor.SigningToolPath,\r | |
610 | SinglePayloadDescriptor.SignToolPfxFile,\r | |
b68d5664 | 611 | SinglePayloadDescriptor.SignToolSubjectName,\r |
104a1aa1 JE |
612 | Verbose = args.Verbose\r |
613 | )\r | |
614 | else:\r | |
615 | CertData = SignPayloadOpenSsl (\r | |
616 | Result + struct.pack ('<Q', SinglePayloadDescriptor.MonotonicCount),\r | |
617 | SinglePayloadDescriptor.SigningToolPath,\r | |
618 | SinglePayloadDescriptor.OpenSslSignerPrivateCertFile,\r | |
619 | SinglePayloadDescriptor.OpenSslOtherPublicCertFile,\r | |
620 | SinglePayloadDescriptor.OpenSslTrustedPublicCertFile,\r | |
621 | Verbose = args.Verbose\r | |
622 | )\r | |
623 | except Exception as Msg:\r | |
624 | print ('GenerateCapsule: error: can not sign payload \n' + str(Msg))\r | |
625 | sys.exit (1)\r | |
626 | \r | |
627 | try:\r | |
628 | FmpAuthHeader.MonotonicCount = SinglePayloadDescriptor.MonotonicCount\r | |
629 | FmpAuthHeader.CertData = CertData\r | |
630 | FmpAuthHeader.Payload = Result\r | |
5531fd48 | 631 | ImageCapsuleSupport |= FmpCapsuleHeader.CAPSULE_SUPPORT_AUTHENTICATION\r |
104a1aa1 JE |
632 | Result = FmpAuthHeader.Encode ()\r |
633 | if args.Verbose:\r | |
634 | FmpAuthHeader.DumpInfo ()\r | |
635 | except:\r | |
636 | print ('GenerateCapsule: error: can not encode FMP Auth Header')\r | |
637 | sys.exit (1)\r | |
5531fd48 | 638 | FmpCapsuleHeader.AddPayload (SinglePayloadDescriptor.Guid, Result, HardwareInstance = SinglePayloadDescriptor.HardwareInstance, UpdateImageIndex = SinglePayloadDescriptor.UpdateImageIndex, CapsuleSupport = ImageCapsuleSupport)\r |
104a1aa1 JE |
639 | try:\r |
640 | for EmbeddedDriver in EmbeddedDriverDescriptorList:\r | |
641 | FmpCapsuleHeader.AddEmbeddedDriver(EmbeddedDriver)\r | |
642 | \r | |
643 | Result = FmpCapsuleHeader.Encode ()\r | |
644 | if args.Verbose:\r | |
645 | FmpCapsuleHeader.DumpInfo ()\r | |
646 | except:\r | |
647 | print ('GenerateCapsule: error: can not encode FMP Capsule Header')\r | |
648 | sys.exit (1)\r | |
649 | \r | |
650 | try:\r | |
651 | UefiCapsuleHeader.OemFlags = args.CapsuleOemFlag\r | |
652 | UefiCapsuleHeader.PersistAcrossReset = 'PersistAcrossReset' in args.CapsuleFlag\r | |
653 | UefiCapsuleHeader.PopulateSystemTable = False\r | |
654 | UefiCapsuleHeader.InitiateReset = 'InitiateReset' in args.CapsuleFlag\r | |
655 | UefiCapsuleHeader.Payload = Result\r | |
656 | Result = UefiCapsuleHeader.Encode ()\r | |
657 | if args.Verbose:\r | |
658 | UefiCapsuleHeader.DumpInfo ()\r | |
659 | except:\r | |
660 | print ('GenerateCapsule: error: can not encode UEFI Capsule Header')\r | |
661 | sys.exit (1)\r | |
662 | try:\r | |
663 | if args.Verbose:\r | |
664 | print ('Write binary output file {File}'.format (File = args.OutputFile.name))\r | |
665 | args.OutputFile.write (Result)\r | |
666 | args.OutputFile.close ()\r | |
667 | except:\r | |
668 | print ('GenerateCapsule: error: can not write binary output file {File}'.format (File = args.OutputFile.name))\r | |
669 | sys.exit (1)\r | |
670 | \r | |
671 | def Decode (PayloadDescriptorList, PayloadJsonDescriptorList, Buffer):\r | |
672 | if args.JsonFile:\r | |
673 | CheckArgumentConflict(args)\r | |
674 | #\r | |
675 | # Parse payload descriptors from JSON\r | |
676 | #\r | |
677 | try:\r | |
678 | Json = json.loads (args.JsonFile.read())\r | |
679 | except:\r | |
680 | print ('GenerateCapsule: error: {JSONFile} loads failure. '.format (JSONFile = args.JsonFile))\r | |
681 | sys.exit (1)\r | |
682 | DecodeJsonFileParse (Json)\r | |
683 | else:\r | |
684 | PayloadDescriptorList.append (PayloadDescriptor (\r | |
685 | Buffer,\r | |
686 | args.Guid,\r | |
687 | args.FwVersion,\r | |
688 | args.LowestSupportedVersion,\r | |
689 | args.MonotonicCount,\r | |
690 | args.HardwareInstance,\r | |
691 | args.UpdateImageIndex,\r | |
692 | args.SignToolPfxFile,\r | |
b68d5664 | 693 | args.SignSubjectName,\r |
104a1aa1 JE |
694 | args.OpenSslSignerPrivateCertFile,\r |
695 | args.OpenSslOtherPublicCertFile,\r | |
696 | args.OpenSslTrustedPublicCertFile,\r | |
f6f66e0c LA |
697 | args.SigningToolPath,\r |
698 | None\r | |
104a1aa1 JE |
699 | ))\r |
700 | #\r | |
701 | # Perform additional verification on payload descriptors\r | |
702 | #\r | |
703 | for SinglePayloadDescriptor in PayloadDescriptorList:\r | |
704 | try:\r | |
705 | SinglePayloadDescriptor.Validate (args)\r | |
706 | except Exception as Msg:\r | |
707 | print ('GenerateCapsule: error:' + str(Msg))\r | |
708 | sys.exit (1)\r | |
709 | try:\r | |
710 | Result = UefiCapsuleHeader.Decode (Buffer)\r | |
711 | if len (Result) > 0:\r | |
712 | Result = FmpCapsuleHeader.Decode (Result)\r | |
713 | if args.JsonFile:\r | |
714 | if FmpCapsuleHeader.PayloadItemCount != len (PayloadDescriptorList):\r | |
715 | CapsulePayloadNum = FmpCapsuleHeader.PayloadItemCount\r | |
716 | JsonPayloadNum = len (PayloadDescriptorList)\r | |
717 | print ('GenerateCapsule: Decode error: {JsonPayloadNumber} payloads in JSON file {File} and {CapsulePayloadNumber} payloads in Capsule {CapsuleName}'.format (JsonPayloadNumber = JsonPayloadNum, File = args.JsonFile.name, CapsulePayloadNumber = CapsulePayloadNum, CapsuleName = args.InputFile.name))\r | |
718 | sys.exit (1)\r | |
719 | for Index in range (0, FmpCapsuleHeader.PayloadItemCount):\r | |
720 | if Index < len (PayloadDescriptorList):\r | |
721 | GUID = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).UpdateImageTypeId\r | |
722 | HardwareInstance = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).UpdateHardwareInstance\r | |
723 | UpdateImageIndex = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).UpdateImageIndex\r | |
724 | if PayloadDescriptorList[Index].Guid != GUID or PayloadDescriptorList[Index].HardwareInstance != HardwareInstance:\r | |
725 | print ('GenerateCapsule: Decode error: Guid or HardwareInstance pair in input JSON file {File} does not match the payload {PayloadIndex} in Capsule {InputCapsule}'.format (File = args.JsonFile.name, PayloadIndex = Index + 1, InputCapsule = args.InputFile.name))\r | |
726 | sys.exit (1)\r | |
727 | PayloadDescriptorList[Index].Payload = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).Payload\r | |
728 | DecodeJsonOutput = args.OutputFile.name + '.Payload.{Index:d}.bin'.format (Index = Index + 1)\r | |
729 | PayloadJsonDescriptorList.append (PayloadDescriptor (\r | |
730 | DecodeJsonOutput,\r | |
731 | GUID,\r | |
732 | None,\r | |
733 | None,\r | |
734 | None,\r | |
735 | HardwareInstance,\r | |
736 | UpdateImageIndex,\r | |
737 | PayloadDescriptorList[Index].SignToolPfxFile,\r | |
b68d5664 | 738 | PayloadDescriptorList[Index].SignToolSubjectName,\r |
104a1aa1 JE |
739 | PayloadDescriptorList[Index].OpenSslSignerPrivateCertFile,\r |
740 | PayloadDescriptorList[Index].OpenSslOtherPublicCertFile,\r | |
741 | PayloadDescriptorList[Index].OpenSslTrustedPublicCertFile,\r | |
f6f66e0c LA |
742 | PayloadDescriptorList[Index].SigningToolPath,\r |
743 | None\r | |
104a1aa1 JE |
744 | ))\r |
745 | else:\r | |
746 | PayloadDescriptorList[0].Payload = FmpCapsuleHeader.GetFmpCapsuleImageHeader (0).Payload\r | |
747 | for Index in range (0, FmpCapsuleHeader.PayloadItemCount):\r | |
748 | if Index > 0:\r | |
749 | PayloadDecodeFile = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).Payload\r | |
750 | PayloadDescriptorList.append (PayloadDescriptor (PayloadDecodeFile,\r | |
751 | None,\r | |
752 | None,\r | |
753 | None,\r | |
754 | None,\r | |
755 | None,\r | |
756 | None,\r | |
757 | None,\r | |
758 | None,\r | |
759 | None,\r | |
760 | None,\r | |
f6f66e0c | 761 | None,\r |
104a1aa1 JE |
762 | None\r |
763 | ))\r | |
764 | GUID = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).UpdateImageTypeId\r | |
765 | HardwareInstance = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).UpdateHardwareInstance\r | |
766 | UpdateImageIndex = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).UpdateImageIndex\r | |
767 | DecodeJsonOutput = args.OutputFile.name + '.Payload.{Index:d}.bin'.format (Index = Index + 1)\r | |
768 | PayloadJsonDescriptorList.append (PayloadDescriptor (\r | |
769 | DecodeJsonOutput,\r | |
770 | GUID,\r | |
771 | None,\r | |
772 | None,\r | |
773 | None,\r | |
774 | HardwareInstance,\r | |
775 | UpdateImageIndex,\r | |
776 | PayloadDescriptorList[Index].SignToolPfxFile,\r | |
b68d5664 | 777 | PayloadDescriptorList[Index].SignToolSubjectName,\r |
104a1aa1 JE |
778 | PayloadDescriptorList[Index].OpenSslSignerPrivateCertFile,\r |
779 | PayloadDescriptorList[Index].OpenSslOtherPublicCertFile,\r | |
780 | PayloadDescriptorList[Index].OpenSslTrustedPublicCertFile,\r | |
f6f66e0c LA |
781 | PayloadDescriptorList[Index].SigningToolPath,\r |
782 | None\r | |
104a1aa1 JE |
783 | ))\r |
784 | JsonIndex = 0\r | |
785 | for SinglePayloadDescriptor in PayloadDescriptorList:\r | |
786 | if args.Verbose:\r | |
787 | print ('========')\r | |
788 | UefiCapsuleHeader.DumpInfo ()\r | |
789 | print ('--------')\r | |
790 | FmpCapsuleHeader.DumpInfo ()\r | |
791 | if FmpAuthHeader.IsSigned(SinglePayloadDescriptor.Payload):\r | |
792 | if not SinglePayloadDescriptor.UseOpenSsl and not SinglePayloadDescriptor.UseSignTool:\r | |
793 | print ('GenerateCapsule: decode warning: can not verify singed payload without cert or pfx file. Index = {Index}'.format (Index = JsonIndex + 1))\r | |
794 | SinglePayloadDescriptor.Payload = FmpAuthHeader.Decode (SinglePayloadDescriptor.Payload)\r | |
795 | PayloadJsonDescriptorList[JsonIndex].MonotonicCount = FmpAuthHeader.MonotonicCount\r | |
796 | if args.Verbose:\r | |
797 | print ('--------')\r | |
798 | FmpAuthHeader.DumpInfo ()\r | |
799 | \r | |
800 | #\r | |
801 | # Verify Image with 64-bit MonotonicCount appended to end of image\r | |
802 | #\r | |
803 | try:\r | |
804 | if SinglePayloadDescriptor.UseSignTool:\r | |
805 | CertData = VerifyPayloadSignTool (\r | |
806 | FmpAuthHeader.Payload + struct.pack ('<Q', FmpAuthHeader.MonotonicCount),\r | |
807 | FmpAuthHeader.CertData,\r | |
808 | SinglePayloadDescriptor.SigningToolPath,\r | |
809 | SinglePayloadDescriptor.SignToolPfxFile,\r | |
b68d5664 | 810 | SinglePayloadDescriptor.SignToolSubjectName,\r |
104a1aa1 JE |
811 | Verbose = args.Verbose\r |
812 | )\r | |
813 | else:\r | |
814 | CertData = VerifyPayloadOpenSsl (\r | |
815 | FmpAuthHeader.Payload + struct.pack ('<Q', FmpAuthHeader.MonotonicCount),\r | |
816 | FmpAuthHeader.CertData,\r | |
817 | SinglePayloadDescriptor.SigningToolPath,\r | |
818 | SinglePayloadDescriptor.OpenSslSignerPrivateCertFile,\r | |
819 | SinglePayloadDescriptor.OpenSslOtherPublicCertFile,\r | |
820 | SinglePayloadDescriptor.OpenSslTrustedPublicCertFile,\r | |
821 | Verbose = args.Verbose\r | |
822 | )\r | |
823 | except Exception as Msg:\r | |
824 | print ('GenerateCapsule: warning: payload verification failed Index = {Index} \n'.format (Index = JsonIndex + 1) + str(Msg))\r | |
825 | else:\r | |
826 | if args.Verbose:\r | |
827 | print ('--------')\r | |
828 | print ('No EFI_FIRMWARE_IMAGE_AUTHENTICATION')\r | |
f6f66e0c LA |
829 | \r |
830 | PayloadSignature = struct.unpack ('<I', SinglePayloadDescriptor.Payload[0:4])\r | |
831 | if PayloadSignature != FmpPayloadHeader.Signature:\r | |
832 | SinglePayloadDescriptor.UseDependency = True\r | |
833 | try:\r | |
834 | SinglePayloadDescriptor.Payload = CapsuleDependency.Decode (SinglePayloadDescriptor.Payload)\r | |
835 | PayloadJsonDescriptorList[JsonIndex].DepexExp = CapsuleDependency.DepexExp\r | |
836 | if args.Verbose:\r | |
837 | print ('--------')\r | |
838 | CapsuleDependency.DumpInfo ()\r | |
839 | except Exception as Msg:\r | |
840 | print ('GenerateCapsule: error: invalid dependency expression')\r | |
841 | else:\r | |
842 | if args.Verbose:\r | |
843 | print ('--------')\r | |
844 | print ('No EFI_FIRMWARE_IMAGE_DEP')\r | |
845 | \r | |
104a1aa1 JE |
846 | try:\r |
847 | SinglePayloadDescriptor.Payload = FmpPayloadHeader.Decode (SinglePayloadDescriptor.Payload)\r | |
848 | PayloadJsonDescriptorList[JsonIndex].FwVersion = FmpPayloadHeader.FwVersion\r | |
849 | PayloadJsonDescriptorList[JsonIndex].LowestSupportedVersion = FmpPayloadHeader.LowestSupportedVersion\r | |
850 | JsonIndex = JsonIndex + 1\r | |
851 | if args.Verbose:\r | |
852 | print ('--------')\r | |
853 | FmpPayloadHeader.DumpInfo ()\r | |
854 | print ('========')\r | |
855 | except:\r | |
856 | if args.Verbose:\r | |
857 | print ('--------')\r | |
858 | print ('No FMP_PAYLOAD_HEADER')\r | |
859 | print ('========')\r | |
860 | sys.exit (1)\r | |
861 | #\r | |
862 | # Write embedded driver file(s)\r | |
863 | #\r | |
864 | for Index in range (0, FmpCapsuleHeader.EmbeddedDriverCount):\r | |
865 | EmbeddedDriverBuffer = FmpCapsuleHeader.GetEmbeddedDriver (Index)\r | |
866 | EmbeddedDriverPath = args.OutputFile.name + '.EmbeddedDriver.{Index:d}.efi'.format (Index = Index + 1)\r | |
867 | try:\r | |
868 | if args.Verbose:\r | |
869 | print ('Write embedded driver file {File}'.format (File = EmbeddedDriverPath))\r | |
870 | with open (EmbeddedDriverPath, 'wb') as EmbeddedDriverFile:\r | |
871 | EmbeddedDriverFile.write (EmbeddedDriverBuffer)\r | |
872 | except:\r | |
873 | print ('GenerateCapsule: error: can not write embedded driver file {File}'.format (File = EmbeddedDriverPath))\r | |
874 | sys.exit (1)\r | |
875 | \r | |
876 | except:\r | |
877 | print ('GenerateCapsule: error: can not decode capsule')\r | |
878 | sys.exit (1)\r | |
879 | GenerateOutputJson(PayloadJsonDescriptorList)\r | |
880 | PayloadIndex = 0\r | |
881 | for SinglePayloadDescriptor in PayloadDescriptorList:\r | |
882 | if args.OutputFile is None:\r | |
883 | print ('GenerateCapsule: Decode error: OutputFile is needed for decode output')\r | |
884 | sys.exit (1)\r | |
885 | try:\r | |
886 | if args.Verbose:\r | |
887 | print ('Write binary output file {File}'.format (File = args.OutputFile.name))\r | |
888 | PayloadDecodePath = args.OutputFile.name + '.Payload.{Index:d}.bin'.format (Index = PayloadIndex + 1)\r | |
889 | with open (PayloadDecodePath, 'wb') as PayloadDecodeFile:\r | |
890 | PayloadDecodeFile.write (SinglePayloadDescriptor.Payload)\r | |
891 | PayloadIndex = PayloadIndex + 1\r | |
892 | except:\r | |
893 | print ('GenerateCapsule: error: can not write binary output file {File}'.format (File = SinglePayloadDescriptor.OutputFile.name))\r | |
894 | sys.exit (1)\r | |
895 | \r | |
896 | def DumpInfo (Buffer, args):\r | |
897 | if args.OutputFile is not None:\r | |
898 | raise argparse.ArgumentTypeError ('the following option is not supported for dumpinfo operations: --output')\r | |
899 | try:\r | |
900 | Result = UefiCapsuleHeader.Decode (Buffer)\r | |
901 | print ('========')\r | |
902 | UefiCapsuleHeader.DumpInfo ()\r | |
903 | if len (Result) > 0:\r | |
904 | FmpCapsuleHeader.Decode (Result)\r | |
905 | print ('--------')\r | |
906 | FmpCapsuleHeader.DumpInfo ()\r | |
907 | for Index in range (0, FmpCapsuleHeader.PayloadItemCount):\r | |
908 | Result = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).Payload\r | |
909 | try:\r | |
910 | Result = FmpAuthHeader.Decode (Result)\r | |
911 | print ('--------')\r | |
912 | FmpAuthHeader.DumpInfo ()\r | |
913 | except:\r | |
914 | print ('--------')\r | |
915 | print ('No EFI_FIRMWARE_IMAGE_AUTHENTICATION')\r | |
f6f66e0c LA |
916 | \r |
917 | PayloadSignature = struct.unpack ('<I', Result[0:4])\r | |
918 | if PayloadSignature != FmpPayloadHeader.Signature:\r | |
919 | try:\r | |
920 | Result = CapsuleDependency.Decode (Result)\r | |
921 | print ('--------')\r | |
922 | CapsuleDependency.DumpInfo ()\r | |
923 | except:\r | |
924 | print ('GenerateCapsule: error: invalid dependency expression')\r | |
925 | else:\r | |
926 | print ('--------')\r | |
927 | print ('No EFI_FIRMWARE_IMAGE_DEP')\r | |
104a1aa1 JE |
928 | try:\r |
929 | Result = FmpPayloadHeader.Decode (Result)\r | |
930 | print ('--------')\r | |
931 | FmpPayloadHeader.DumpInfo ()\r | |
932 | except:\r | |
933 | print ('--------')\r | |
934 | print ('No FMP_PAYLOAD_HEADER')\r | |
935 | print ('========')\r | |
936 | except:\r | |
937 | print ('GenerateCapsule: error: can not decode capsule')\r | |
938 | sys.exit (1)\r | |
8b63877a KM |
939 | #\r |
940 | # Create command line argument parser object\r | |
941 | #\r | |
942 | parser = argparse.ArgumentParser (\r | |
943 | prog = __prog__,\r | |
944 | description = __description__ + __copyright__,\r | |
945 | conflict_handler = 'resolve',\r | |
946 | fromfile_prefix_chars = '@'\r | |
947 | )\r | |
948 | parser.convert_arg_line_to_args = convert_arg_line_to_args\r | |
949 | \r | |
950 | #\r | |
951 | # Add input and output file arguments\r | |
952 | #\r | |
104a1aa1 | 953 | parser.add_argument("InputFile", type = argparse.FileType('rb'), nargs='?',\r |
8b63877a KM |
954 | help = "Input binary payload filename.")\r |
955 | parser.add_argument("-o", "--output", dest = 'OutputFile', type = argparse.FileType('wb'),\r | |
956 | help = "Output filename.")\r | |
957 | #\r | |
958 | # Add group for -e and -d flags that are mutually exclusive and required\r | |
959 | #\r | |
960 | group = parser.add_mutually_exclusive_group (required = True)\r | |
961 | group.add_argument ("-e", "--encode", dest = 'Encode', action = "store_true",\r | |
962 | help = "Encode file")\r | |
963 | group.add_argument ("-d", "--decode", dest = 'Decode', action = "store_true",\r | |
964 | help = "Decode file")\r | |
965 | group.add_argument ("--dump-info", dest = 'DumpInfo', action = "store_true",\r | |
966 | help = "Display FMP Payload Header information")\r | |
967 | #\r | |
968 | # Add optional arguments for this command\r | |
969 | #\r | |
104a1aa1 JE |
970 | parser.add_argument ("-j", "--json-file", dest = 'JsonFile', type=argparse.FileType('r'),\r |
971 | help = "JSON configuration file for multiple payloads and embedded drivers.")\r | |
8b63877a | 972 | parser.add_argument ("--capflag", dest = 'CapsuleFlag', action='append', default = [],\r |
2779c222 KM |
973 | choices=['PersistAcrossReset', 'InitiateReset'],\r |
974 | help = "Capsule flag can be PersistAcrossReset or InitiateReset or not set")\r | |
8b63877a KM |
975 | parser.add_argument ("--capoemflag", dest = 'CapsuleOemFlag', type = ValidateUnsignedInteger, default = 0x0000,\r |
976 | help = "Capsule OEM Flag is an integer between 0x0000 and 0xffff.")\r | |
977 | \r | |
978 | parser.add_argument ("--guid", dest = 'Guid', type = ValidateRegistryFormatGuid,\r | |
104a1aa1 | 979 | help = "The FMP/ESRT GUID in registry format. Required for single payload encode operations.")\r |
8b63877a KM |
980 | parser.add_argument ("--hardware-instance", dest = 'HardwareInstance', type = ValidateUnsignedInteger, default = 0x0000000000000000,\r |
981 | help = "The 64-bit hardware instance. The default is 0x0000000000000000")\r | |
982 | \r | |
983 | \r | |
984 | parser.add_argument ("--monotonic-count", dest = 'MonotonicCount', type = ValidateUnsignedInteger, default = 0x0000000000000000,\r | |
985 | help = "64-bit monotonic count value in header. Default is 0x0000000000000000.")\r | |
986 | \r | |
987 | parser.add_argument ("--fw-version", dest = 'FwVersion', type = ValidateUnsignedInteger,\r | |
104a1aa1 | 988 | help = "The 32-bit version of the binary payload (e.g. 0x11223344 or 5678). Required for encode operations.")\r |
8b63877a | 989 | parser.add_argument ("--lsv", dest = 'LowestSupportedVersion', type = ValidateUnsignedInteger,\r |
104a1aa1 | 990 | help = "The 32-bit lowest supported version of the binary payload (e.g. 0x11223344 or 5678). Required for encode operations.")\r |
8b63877a KM |
991 | \r |
992 | parser.add_argument ("--pfx-file", dest='SignToolPfxFile', type=argparse.FileType('rb'),\r | |
993 | help="signtool PFX certificate filename.")\r | |
b68d5664 JL |
994 | parser.add_argument ("--subject-name", dest='SignToolSubjectName',\r |
995 | help="signtool certificate subject name.")\r | |
8b63877a KM |
996 | \r |
997 | parser.add_argument ("--signer-private-cert", dest='OpenSslSignerPrivateCertFile', type=argparse.FileType('rb'),\r | |
998 | help="OpenSSL signer private certificate filename.")\r | |
999 | parser.add_argument ("--other-public-cert", dest='OpenSslOtherPublicCertFile', type=argparse.FileType('rb'),\r | |
1000 | help="OpenSSL other public certificate filename.")\r | |
1001 | parser.add_argument ("--trusted-public-cert", dest='OpenSslTrustedPublicCertFile', type=argparse.FileType('rb'),\r | |
1002 | help="OpenSSL trusted public certificate filename.")\r | |
1003 | \r | |
1004 | parser.add_argument ("--signing-tool-path", dest = 'SigningToolPath',\r | |
1005 | help = "Path to signtool or OpenSSL tool. Optional if path to tools are already in PATH.")\r | |
1006 | \r | |
104a1aa1 JE |
1007 | parser.add_argument ("--embedded-driver", dest = 'EmbeddedDriver', type = argparse.FileType('rb'), action='append', default = [],\r |
1008 | help = "Path to embedded UEFI driver to add to capsule.")\r | |
1009 | \r | |
8b63877a KM |
1010 | #\r |
1011 | # Add optional arguments common to all operations\r | |
1012 | #\r | |
1013 | parser.add_argument ('--version', action='version', version='%(prog)s ' + __version__)\r | |
1014 | parser.add_argument ("-v", "--verbose", dest = 'Verbose', action = "store_true",\r | |
1015 | help = "Turn on verbose output with informational messages printed, including capsule headers and warning messages.")\r | |
1016 | parser.add_argument ("-q", "--quiet", dest = 'Quiet', action = "store_true",\r | |
1017 | help = "Disable all messages except fatal errors.")\r | |
1018 | parser.add_argument ("--debug", dest = 'Debug', type = int, metavar = '[0-9]', choices = range (0, 10), default = 0,\r | |
1019 | help = "Set debug level")\r | |
104a1aa1 | 1020 | parser.add_argument ("--update-image-index", dest = 'UpdateImageIndex', type = ValidateUnsignedInteger, default = 0x01, help = "unique number identifying the firmware image within the device ")\r |
8b63877a KM |
1021 | \r |
1022 | #\r | |
1023 | # Parse command line arguments\r | |
1024 | #\r | |
1025 | args = parser.parse_args()\r | |
1026 | \r | |
8b63877a KM |
1027 | #\r |
1028 | # Read binary input file\r | |
1029 | #\r | |
104a1aa1 JE |
1030 | Buffer = ''\r |
1031 | if args.InputFile:\r | |
1032 | if os.path.getsize (args.InputFile.name) == 0:\r | |
1033 | print ('GenerateCapsule: error: InputFile {File} is empty'.format (File = args.InputFile.name))\r | |
1034 | sys.exit (1)\r | |
1035 | try:\r | |
1036 | if args.Verbose:\r | |
1037 | print ('Read binary input file {File}'.format (File = args.InputFile.name))\r | |
1038 | Buffer = args.InputFile.read ()\r | |
1039 | args.InputFile.close ()\r | |
1040 | except:\r | |
1041 | print ('GenerateCapsule: error: can not read binary input file {File}'.format (File = args.InputFile.name))\r | |
1042 | sys.exit (1)\r | |
8b63877a KM |
1043 | \r |
1044 | #\r | |
1045 | # Create objects\r | |
1046 | #\r | |
1047 | UefiCapsuleHeader = UefiCapsuleHeaderClass ()\r | |
1048 | FmpCapsuleHeader = FmpCapsuleHeaderClass ()\r | |
1049 | FmpAuthHeader = FmpAuthHeaderClass ()\r | |
1050 | FmpPayloadHeader = FmpPayloadHeaderClass ()\r | |
f6f66e0c | 1051 | CapsuleDependency = CapsuleDependencyClass ()\r |
8b63877a | 1052 | \r |
104a1aa1 JE |
1053 | EmbeddedDriverDescriptorList = []\r |
1054 | PayloadDescriptorList = []\r | |
1055 | PayloadJsonDescriptorList = []\r | |
8b63877a | 1056 | \r |
104a1aa1 JE |
1057 | #\r |
1058 | #Encode Operation\r | |
1059 | #\r | |
1060 | if args.Encode:\r | |
1061 | Encode (PayloadDescriptorList, EmbeddedDriverDescriptorList, Buffer)\r | |
8b63877a | 1062 | \r |
104a1aa1 JE |
1063 | #\r |
1064 | #Decode Operation\r | |
1065 | #\r | |
1066 | if args.Decode:\r | |
1067 | Decode (PayloadDescriptorList, PayloadJsonDescriptorList, Buffer)\r | |
8b63877a KM |
1068 | \r |
1069 | #\r | |
104a1aa1 | 1070 | #Dump Info Operation\r |
8b63877a | 1071 | #\r |
104a1aa1 JE |
1072 | if args.DumpInfo:\r |
1073 | DumpInfo (Buffer, args)\r | |
8b63877a KM |
1074 | \r |
1075 | if args.Verbose:\r | |
1076 | print('Success')\r |