]> git.proxmox.com Git - mirror_edk2.git/blame - BaseTools/Source/Python/Capsule/GenerateCapsule.py
BaseTools/Capsule: Support signtool input subject name to sign capsule file
[mirror_edk2.git] / BaseTools / Source / Python / Capsule / GenerateCapsule.py
CommitLineData
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
18GenerateCapsule\r
19'''\r
20\r
21import sys\r
22import argparse\r
23import uuid\r
24import struct\r
25import subprocess\r
26import os\r
27import tempfile\r
28import shutil\r
29import platform\r
104a1aa1 30import json\r
8b63877a
KM
31from Common.Uefi.Capsule.UefiCapsuleHeader import UefiCapsuleHeaderClass\r
32from Common.Uefi.Capsule.FmpCapsuleHeader import FmpCapsuleHeaderClass\r
33from Common.Uefi.Capsule.FmpAuthHeader import FmpAuthHeaderClass\r
f6f66e0c 34from Common.Uefi.Capsule.CapsuleDependency import CapsuleDependencyClass\r
8b63877a
KM
35from 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 45def 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 111def 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 115def 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 144def 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
195if __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