4 # This tool generates a UEFI Capsule around an FMP Capsule. The capsule payload
5 # be signed using signtool or OpenSSL and if it is signed the signed content
6 # includes an FMP Payload Header.
8 # This tool is intended to be used to generate UEFI Capsules to update the
9 # system firmware or device firmware for integrated devices. In order to
10 # keep the tool as simple as possible, it has the following limitations:
11 # * Do not support vendor code bytes in a capsule.
13 # Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
14 # SPDX-License-Identifier: BSD-2-Clause-Patent
31 from Common
.Uefi
.Capsule
.UefiCapsuleHeader
import UefiCapsuleHeaderClass
32 from Common
.Uefi
.Capsule
.FmpCapsuleHeader
import FmpCapsuleHeaderClass
33 from Common
.Uefi
.Capsule
.FmpAuthHeader
import FmpAuthHeaderClass
34 from Common
.Edk2
.Capsule
.FmpPayloadHeader
import FmpPayloadHeaderClass
37 # Globals for help information
39 __prog__
= 'GenerateCapsule'
41 __copyright__
= 'Copyright (c) 2018, Intel Corporation. All rights reserved.'
42 __description__
= 'Generate a capsule.\n'
44 def SignPayloadSignTool (Payload
, ToolPath
, PfxFile
, Verbose
= False):
46 # Create a temporary directory
48 TempDirectoryName
= tempfile
.mkdtemp()
51 # Generate temp file name for the payload contents
53 TempFileName
= os
.path
.join (TempDirectoryName
, 'Payload.bin')
56 # Create temporary payload file for signing
59 with
open (TempFileName
, 'wb') as File
:
62 shutil
.rmtree (TempDirectoryName
)
63 raise ValueError ('GenerateCapsule: error: can not write temporary payload file.')
66 # Build signtool command
71 Command
= Command
+ '"{Path}" '.format (Path
= os
.path
.join (ToolPath
, 'signtool.exe'))
72 Command
= Command
+ 'sign /fd sha256 /p7ce DetachedSignedData /p7co 1.2.840.113549.1.7.2 '
73 Command
= Command
+ '/p7 {TempDir} '.format (TempDir
= TempDirectoryName
)
74 Command
= Command
+ '/f {PfxFile} '.format (PfxFile
= PfxFile
)
75 Command
= Command
+ TempFileName
80 # Sign the input file using the specified private key
83 Process
= subprocess
.Popen (Command
, stdin
= subprocess
.PIPE
, stdout
= subprocess
.PIPE
, stderr
= subprocess
.PIPE
, shell
= True)
84 Result
= Process
.communicate('')
86 shutil
.rmtree (TempDirectoryName
)
87 raise ValueError ('GenerateCapsule: error: can not run signtool.')
89 if Process
.returncode
!= 0:
90 shutil
.rmtree (TempDirectoryName
)
91 print (Result
[1].decode())
92 raise ValueError ('GenerateCapsule: error: signtool failed.')
95 # Read the signature from the generated output file
98 with
open (TempFileName
+ '.p7', 'rb') as File
:
99 Signature
= File
.read ()
101 shutil
.rmtree (TempDirectoryName
)
102 raise ValueError ('GenerateCapsule: error: can not read signature file.')
104 shutil
.rmtree (TempDirectoryName
)
107 def VerifyPayloadSignTool (Payload
, CertData
, ToolPath
, PfxFile
, Verbose
= False):
108 print ('signtool verify is not supported.')
109 raise ValueError ('GenerateCapsule: error: signtool verify is not supported.')
111 def SignPayloadOpenSsl (Payload
, ToolPath
, SignerPrivateCertFile
, OtherPublicCertFile
, TrustedPublicCertFile
, Verbose
= False):
113 # Build openssl command
118 Command
= Command
+ '"{Path}" '.format (Path
= os
.path
.join (ToolPath
, 'openssl'))
119 Command
= Command
+ 'smime -sign -binary -outform DER -md sha256 '
120 Command
= Command
+ '-signer "{Private}" -certfile "{Public}"'.format (Private
= SignerPrivateCertFile
, Public
= OtherPublicCertFile
)
125 # Sign the input file using the specified private key and capture signature from STDOUT
128 Process
= subprocess
.Popen (Command
, stdin
= subprocess
.PIPE
, stdout
= subprocess
.PIPE
, stderr
= subprocess
.PIPE
, shell
= True)
129 Result
= Process
.communicate(input = Payload
)
130 Signature
= Result
[0]
132 raise ValueError ('GenerateCapsule: error: can not run openssl.')
134 if Process
.returncode
!= 0:
135 print (Result
[1].decode())
136 raise ValueError ('GenerateCapsule: error: openssl failed.')
140 def VerifyPayloadOpenSsl (Payload
, CertData
, ToolPath
, SignerPrivateCertFile
, OtherPublicCertFile
, TrustedPublicCertFile
, Verbose
= False):
142 # Create a temporary directory
144 TempDirectoryName
= tempfile
.mkdtemp()
147 # Generate temp file name for the payload contents
149 TempFileName
= os
.path
.join (TempDirectoryName
, 'Payload.bin')
152 # Create temporary payload file for verification
155 with
open (TempFileName
, 'wb') as File
:
158 shutil
.rmtree (TempDirectoryName
)
159 raise ValueError ('GenerateCapsule: error: can not write temporary payload file.')
162 # Build openssl command
167 Command
= Command
+ '"{Path}" '.format (Path
= os
.path
.join (ToolPath
, 'openssl'))
168 Command
= Command
+ 'smime -verify -inform DER '
169 Command
= Command
+ '-content {Content} -CAfile "{Public}"'.format (Content
= TempFileName
, Public
= TrustedPublicCertFile
)
177 Process
= subprocess
.Popen (Command
, stdin
= subprocess
.PIPE
, stdout
= subprocess
.PIPE
, stderr
= subprocess
.PIPE
, shell
= True)
178 Result
= Process
.communicate(input = CertData
)
180 shutil
.rmtree (TempDirectoryName
)
181 raise ValueError ('GenerateCapsule: error: can not run openssl.')
183 if Process
.returncode
!= 0:
184 shutil
.rmtree (TempDirectoryName
)
185 print (Result
[1].decode())
186 raise ValueError ('GenerateCapsule: error: openssl failed.')
188 shutil
.rmtree (TempDirectoryName
)
191 if __name__
== '__main__':
192 def convert_arg_line_to_args(arg_line
):
193 for arg
in arg_line
.split():
198 def ValidateUnsignedInteger (Argument
):
200 Value
= int (Argument
, 0)
202 Message
= '{Argument} is not a valid integer value.'.format (Argument
= Argument
)
203 raise argparse
.ArgumentTypeError (Message
)
205 Message
= '{Argument} is a negative value.'.format (Argument
= Argument
)
206 raise argparse
.ArgumentTypeError (Message
)
209 def ValidateRegistryFormatGuid (Argument
):
211 Value
= uuid
.UUID (Argument
)
213 Message
= '{Argument} is not a valid registry format GUID value.'.format (Argument
= Argument
)
214 raise argparse
.ArgumentTypeError (Message
)
217 def ConvertJsonValue (Config
, FieldName
, Convert
, Required
= True, Default
= None, Open
= False):
218 if FieldName
not in Config
:
220 print ('GenerateCapsule: error: Payload descriptor invalid syntax. Could not find {Key} in payload descriptor.'.format(Key
= FieldName
))
224 Value
= Convert (Config
[FieldName
])
226 print ('GenerateCapsule: error: {Key} in payload descriptor has invalid syntax.'.format (Key
= FieldName
))
230 Value
= open (Value
, "rb")
232 print ('GenerateCapsule: error: can not open file {File}'.format (File
= FieldName
))
236 def DecodeJsonFileParse (Json
):
237 if 'Payloads' not in Json
:
238 print ('GenerateCapsule: error "Payloads" section not found in JSON file {File}'.format (File
= args
.JsonFile
.name
))
240 for Config
in Json
['Payloads']:
242 # Parse fields from JSON
244 PayloadFile
= ConvertJsonValue (Config
, 'Payload', os
.path
.expandvars
, Required
= False)
245 Guid
= ConvertJsonValue (Config
, 'Guid', ValidateRegistryFormatGuid
, Required
= False)
246 FwVersion
= ConvertJsonValue (Config
, 'FwVersion', ValidateUnsignedInteger
, Required
= False)
247 LowestSupportedVersion
= ConvertJsonValue (Config
, 'LowestSupportedVersion', ValidateUnsignedInteger
, Required
= False)
248 HardwareInstance
= ConvertJsonValue (Config
, 'HardwareInstance', ValidateUnsignedInteger
, Required
= False, Default
= 0)
249 MonotonicCount
= ConvertJsonValue (Config
, 'MonotonicCount', ValidateUnsignedInteger
, Required
= False, Default
= 0)
250 SignToolPfxFile
= ConvertJsonValue (Config
, 'SignToolPfxFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
251 OpenSslSignerPrivateCertFile
= ConvertJsonValue (Config
, 'OpenSslSignerPrivateCertFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
252 OpenSslOtherPublicCertFile
= ConvertJsonValue (Config
, 'OpenSslOtherPublicCertFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
253 OpenSslTrustedPublicCertFile
= ConvertJsonValue (Config
, 'OpenSslTrustedPublicCertFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
254 SigningToolPath
= ConvertJsonValue (Config
, 'SigningToolPath', os
.path
.expandvars
, Required
= False, Default
= None)
255 UpdateImageIndex
= ConvertJsonValue (Config
, 'UpdateImageIndex', ValidateUnsignedInteger
, Required
= False, Default
= 1)
257 PayloadDescriptorList
.append (PayloadDescriptor (
261 LowestSupportedVersion
,
266 OpenSslSignerPrivateCertFile
,
267 OpenSslOtherPublicCertFile
,
268 OpenSslTrustedPublicCertFile
,
272 def EncodeJsonFileParse (Json
):
273 if 'EmbeddedDrivers' not in Json
:
274 print ('GenerateCapsule: warning "EmbeddedDrivers" section not found in JSON file {File}'.format (File
= args
.JsonFile
.name
))
276 for Config
in Json
['EmbeddedDrivers']:
277 EmbeddedDriverFile
= ConvertJsonValue(Config
, 'Driver', os
.path
.expandvars
, Open
= True)
279 #Read EmbeddedDriver file
283 print ('Read EmbeddedDriver file {File}'.format (File
= EmbeddedDriverFile
.name
))
284 Driver
= EmbeddedDriverFile
.read()
286 print ('GenerateCapsule: error: can not read EmbeddedDriver file {File}'.format (File
= EmbeddedDriverFile
.name
))
288 EmbeddedDriverDescriptorList
.append (Driver
)
290 if 'Payloads' not in Json
:
291 print ('GenerateCapsule: error: "Payloads" section not found in JSON file {File}'.format (File
= args
.JsonFile
.name
))
293 for Config
in Json
['Payloads']:
295 # Parse fields from JSON
297 PayloadFile
= ConvertJsonValue (Config
, 'Payload', os
.path
.expandvars
, Open
= True)
298 Guid
= ConvertJsonValue (Config
, 'Guid', ValidateRegistryFormatGuid
)
299 FwVersion
= ConvertJsonValue (Config
, 'FwVersion', ValidateUnsignedInteger
)
300 LowestSupportedVersion
= ConvertJsonValue (Config
, 'LowestSupportedVersion', ValidateUnsignedInteger
)
301 HardwareInstance
= ConvertJsonValue (Config
, 'HardwareInstance', ValidateUnsignedInteger
, Required
= False, Default
= 0)
302 UpdateImageIndex
= ConvertJsonValue (Config
, 'UpdateImageIndex', ValidateUnsignedInteger
, Required
= False, Default
= 1)
303 MonotonicCount
= ConvertJsonValue (Config
, 'MonotonicCount', ValidateUnsignedInteger
, Required
= False, Default
= 0)
304 SignToolPfxFile
= ConvertJsonValue (Config
, 'SignToolPfxFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
305 OpenSslSignerPrivateCertFile
= ConvertJsonValue (Config
, 'OpenSslSignerPrivateCertFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
306 OpenSslOtherPublicCertFile
= ConvertJsonValue (Config
, 'OpenSslOtherPublicCertFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
307 OpenSslTrustedPublicCertFile
= ConvertJsonValue (Config
, 'OpenSslTrustedPublicCertFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
308 SigningToolPath
= ConvertJsonValue (Config
, 'SigningToolPath', os
.path
.expandvars
, Required
= False, Default
= None)
311 # Read binary input file
315 print ('Read binary input file {File}'.format (File
= PayloadFile
.name
))
316 Payload
= PayloadFile
.read()
319 print ('GenerateCapsule: error: can not read binary input file {File}'.format (File
= PayloadFile
.name
))
321 PayloadDescriptorList
.append (PayloadDescriptor (
325 LowestSupportedVersion
,
330 OpenSslSignerPrivateCertFile
,
331 OpenSslOtherPublicCertFile
,
332 OpenSslTrustedPublicCertFile
,
336 def GenerateOutputJson (PayloadJsonDescriptorList
):
340 "Guid": str(PayloadDescriptor
.Guid
).upper(),
341 "FwVersion": str(PayloadDescriptor
.FwVersion
),
342 "LowestSupportedVersion": str(PayloadDescriptor
.LowestSupportedVersion
),
343 "MonotonicCount": str(PayloadDescriptor
.MonotonicCount
),
344 "Payload": PayloadDescriptor
.Payload
,
345 "HardwareInstance": str(PayloadDescriptor
.HardwareInstance
),
346 "UpdateImageIndex": str(PayloadDescriptor
.UpdateImageIndex
),
347 "SignToolPfxFile": str(PayloadDescriptor
.SignToolPfxFile
),
348 "OpenSslSignerPrivateCertFile": str(PayloadDescriptor
.OpenSslSignerPrivateCertFile
),
349 "OpenSslOtherPublicCertFile": str(PayloadDescriptor
.OpenSslOtherPublicCertFile
),
350 "OpenSslTrustedPublicCertFile": str(PayloadDescriptor
.OpenSslTrustedPublicCertFile
),
351 "SigningToolPath": str(PayloadDescriptor
.SigningToolPath
)
352 }for PayloadDescriptor
in PayloadJsonDescriptorList
355 OutputJsonFile
= args
.OutputFile
.name
+ '.json'
356 if 'Payloads' in PayloadJson
:
357 PayloadSection
= PayloadJson
['Payloads']
359 for PayloadField
in PayloadSection
:
360 if PayloadJsonDescriptorList
[Index
].SignToolPfxFile
is None:
361 del PayloadField
['SignToolPfxFile']
362 if PayloadJsonDescriptorList
[Index
].OpenSslSignerPrivateCertFile
is None:
363 del PayloadField
['OpenSslSignerPrivateCertFile']
364 if PayloadJsonDescriptorList
[Index
].OpenSslOtherPublicCertFile
is None:
365 del PayloadField
['OpenSslOtherPublicCertFile']
366 if PayloadJsonDescriptorList
[Index
].OpenSslTrustedPublicCertFile
is None:
367 del PayloadField
['OpenSslTrustedPublicCertFile']
368 if PayloadJsonDescriptorList
[Index
].SigningToolPath
is None:
369 del PayloadField
['SigningToolPath']
371 Result
= json
.dumps (PayloadJson
, indent
=4, sort_keys
=True, separators
=(',', ': '))
372 with
open (OutputJsonFile
, 'w') as OutputFile
:
373 OutputFile
.write (Result
)
375 def CheckArgumentConflict (args
):
378 print ('GenerateCapsule: error: Argument InputFile conflicts with Argument -j')
380 if args
.EmbeddedDriver
:
381 print ('GenerateCapsule: error: Argument --embedded-driver conflicts with Argument -j')
384 print ('GenerateCapsule: error: Argument --guid conflicts with Argument -j')
387 print ('GenerateCapsule: error: Argument --fw-version conflicts with Argument -j')
389 if args
.LowestSupportedVersion
:
390 print ('GenerateCapsule: error: Argument --lsv conflicts with Argument -j')
392 if args
.MonotonicCount
:
393 print ('GenerateCapsule: error: Argument --monotonic-count conflicts with Argument -j')
395 if args
.HardwareInstance
:
396 print ('GenerateCapsule: error: Argument --hardware-instance conflicts with Argument -j')
398 if args
.SignToolPfxFile
:
399 print ('GenerateCapsule: error: Argument --pfx-file conflicts with Argument -j')
401 if args
.OpenSslSignerPrivateCertFile
:
402 print ('GenerateCapsule: error: Argument --signer-private-cert conflicts with Argument -j')
404 if args
.OpenSslOtherPublicCertFile
:
405 print ('GenerateCapsule: error: Argument --other-public-cert conflicts with Argument -j')
407 if args
.OpenSslTrustedPublicCertFile
:
408 print ('GenerateCapsule: error: Argument --trusted-public-cert conflicts with Argument -j')
410 if args
.SigningToolPath
:
411 print ('GenerateCapsule: error: Argument --signing-tool-path conflicts with Argument -j')
414 class PayloadDescriptor (object):
419 LowestSupportedVersion
,
421 HardwareInstance
= 0,
422 UpdateImageIndex
= 1,
423 SignToolPfxFile
= None,
424 OpenSslSignerPrivateCertFile
= None,
425 OpenSslOtherPublicCertFile
= None,
426 OpenSslTrustedPublicCertFile
= None,
427 SigningToolPath
= None
429 self
.Payload
= Payload
431 self
.FwVersion
= FwVersion
432 self
.LowestSupportedVersion
= LowestSupportedVersion
433 self
.MonotonicCount
= MonotonicCount
434 self
.HardwareInstance
= HardwareInstance
435 self
.UpdateImageIndex
= UpdateImageIndex
436 self
.SignToolPfxFile
= SignToolPfxFile
437 self
.OpenSslSignerPrivateCertFile
= OpenSslSignerPrivateCertFile
438 self
.OpenSslOtherPublicCertFile
= OpenSslOtherPublicCertFile
439 self
.OpenSslTrustedPublicCertFile
= OpenSslTrustedPublicCertFile
440 self
.SigningToolPath
= SigningToolPath
442 self
.UseSignTool
= self
.SignToolPfxFile
is not None
443 self
.UseOpenSsl
= (self
.OpenSslSignerPrivateCertFile
is not None and
444 self
.OpenSslOtherPublicCertFile
is not None and
445 self
.OpenSslTrustedPublicCertFile
is not None)
446 self
.AnyOpenSsl
= (self
.OpenSslSignerPrivateCertFile
is not None or
447 self
.OpenSslOtherPublicCertFile
is not None or
448 self
.OpenSslTrustedPublicCertFile
is not None)
450 def Validate(self
, args
):
451 if self
.UseSignTool
and self
.AnyOpenSsl
:
452 raise argparse
.ArgumentTypeError ('Providing both signtool and OpenSSL options is not supported')
453 if not self
.UseSignTool
and not self
.UseOpenSsl
and self
.AnyOpenSsl
:
455 raise argparse
.ArgumentTypeError ('the following JSON fields are required for OpenSSL: OpenSslSignerPrivateCertFile, OpenSslOtherPublicCertFile, OpenSslTrustedPublicCertFile')
457 raise argparse
.ArgumentTypeError ('the following options are required for OpenSSL: --signer-private-cert, --other-public-cert, --trusted-public-cert')
458 if self
.UseSignTool
and platform
.system() != 'Windows':
459 raise argparse
.ArgumentTypeError ('Use of signtool is not supported on this operating system.')
461 if self
.FwVersion
is None or self
.LowestSupportedVersion
is None:
463 raise argparse
.ArgumentTypeError ('the following JSON fields are required: FwVersion, LowestSupportedVersion')
465 raise argparse
.ArgumentTypeError ('the following options are required: --fw-version, --lsv')
466 if self
.FwVersion
> 0xFFFFFFFF:
468 raise argparse
.ArgumentTypeError ('JSON field FwVersion must be an integer in range 0x0..0xffffffff')
470 raise argparse
.ArgumentTypeError ('--fw-version must be an integer in range 0x0..0xffffffff')
471 if self
.LowestSupportedVersion
> 0xFFFFFFFF:
473 raise argparse
.ArgumentTypeError ('JSON field LowestSupportedVersion must be an integer in range 0x0..0xffffffff')
475 raise argparse
.ArgumentTypeError ('--lsv must be an integer in range 0x0..0xffffffff')
478 if self
.Guid
is None:
480 raise argparse
.ArgumentTypeError ('the following JSON field is required: Guid')
482 raise argparse
.ArgumentTypeError ('the following option is required: --guid')
483 if self
.HardwareInstance
> 0xFFFFFFFFFFFFFFFF:
485 raise argparse
.ArgumentTypeError ('JSON field HardwareInstance must be an integer in range 0x0..0xffffffffffffffff')
487 raise argparse
.ArgumentTypeError ('--hardware-instance must be an integer in range 0x0..0xffffffffffffffff')
488 if self
.MonotonicCount
> 0xFFFFFFFFFFFFFFFF:
490 raise argparse
.ArgumentTypeError ('JSON field MonotonicCount must be an integer in range 0x0..0xffffffffffffffff')
492 raise argparse
.ArgumentTypeError ('--monotonic-count must be an integer in range 0x0..0xffffffffffffffff')
493 if self
.UpdateImageIndex
>0xFF:
495 raise argparse
.ArgumentTypeError ('JSON field UpdateImageIndex must be an integer in range 0x0..0xff')
497 raise argparse
.ArgumentTypeError ('--update-image-index must be an integer in range 0x0..0xff')
500 self
.SignToolPfxFile
.close()
501 self
.SignToolPfxFile
= self
.SignToolPfxFile
.name
503 self
.OpenSslSignerPrivateCertFile
.close()
504 self
.OpenSslOtherPublicCertFile
.close()
505 self
.OpenSslTrustedPublicCertFile
.close()
506 self
.OpenSslSignerPrivateCertFile
= self
.OpenSslSignerPrivateCertFile
.name
507 self
.OpenSslOtherPublicCertFile
= self
.OpenSslOtherPublicCertFile
.name
508 self
.OpenSslTrustedPublicCertFile
= self
.OpenSslTrustedPublicCertFile
.name
511 # Perform additional argument verification
514 if 'PersistAcrossReset' not in args
.CapsuleFlag
:
515 if 'InitiateReset' in args
.CapsuleFlag
:
516 raise argparse
.ArgumentTypeError ('--capflag InitiateReset also requires --capflag PersistAcrossReset')
517 if args
.CapsuleOemFlag
> 0xFFFF:
518 raise argparse
.ArgumentTypeError ('--capoemflag must be an integer between 0x0000 and 0xffff')
523 def Encode (PayloadDescriptorList
, EmbeddedDriverDescriptorList
, Buffer
):
525 CheckArgumentConflict(args
)
527 Json
= json
.loads (args
.JsonFile
.read ())
529 print ('GenerateCapsule: error: {JSONFile} loads failure. '.format (JSONFile
= args
.JsonFile
))
531 EncodeJsonFileParse(Json
)
533 for Driver
in args
.EmbeddedDriver
:
534 EmbeddedDriverDescriptorList
.append (Driver
.read())
535 PayloadDescriptorList
.append (PayloadDescriptor (
539 args
.LowestSupportedVersion
,
541 args
.HardwareInstance
,
542 args
.UpdateImageIndex
,
543 args
.SignToolPfxFile
,
544 args
.OpenSslSignerPrivateCertFile
,
545 args
.OpenSslOtherPublicCertFile
,
546 args
.OpenSslTrustedPublicCertFile
,
549 for SinglePayloadDescriptor
in PayloadDescriptorList
:
551 SinglePayloadDescriptor
.Validate (args
)
552 except Exception as Msg
:
553 print ('GenerateCapsule: error:' + str(Msg
))
555 for SinglePayloadDescriptor
in PayloadDescriptorList
:
556 Result
= SinglePayloadDescriptor
.Payload
558 FmpPayloadHeader
.FwVersion
= SinglePayloadDescriptor
.FwVersion
559 FmpPayloadHeader
.LowestSupportedVersion
= SinglePayloadDescriptor
.LowestSupportedVersion
560 FmpPayloadHeader
.Payload
= SinglePayloadDescriptor
.Payload
561 Result
= FmpPayloadHeader
.Encode ()
563 FmpPayloadHeader
.DumpInfo ()
565 print ('GenerateCapsule: error: can not encode FMP Payload Header')
567 if SinglePayloadDescriptor
.UseOpenSsl
or SinglePayloadDescriptor
.UseSignTool
:
569 # Sign image with 64-bit MonotonicCount appended to end of image
572 if SinglePayloadDescriptor
.UseSignTool
:
573 CertData
= SignPayloadSignTool (
574 Result
+ struct
.pack ('<Q', SinglePayloadDescriptor
.MonotonicCount
),
575 SinglePayloadDescriptor
.SigningToolPath
,
576 SinglePayloadDescriptor
.SignToolPfxFile
,
577 Verbose
= args
.Verbose
580 CertData
= SignPayloadOpenSsl (
581 Result
+ struct
.pack ('<Q', SinglePayloadDescriptor
.MonotonicCount
),
582 SinglePayloadDescriptor
.SigningToolPath
,
583 SinglePayloadDescriptor
.OpenSslSignerPrivateCertFile
,
584 SinglePayloadDescriptor
.OpenSslOtherPublicCertFile
,
585 SinglePayloadDescriptor
.OpenSslTrustedPublicCertFile
,
586 Verbose
= args
.Verbose
588 except Exception as Msg
:
589 print ('GenerateCapsule: error: can not sign payload \n' + str(Msg
))
593 FmpAuthHeader
.MonotonicCount
= SinglePayloadDescriptor
.MonotonicCount
594 FmpAuthHeader
.CertData
= CertData
595 FmpAuthHeader
.Payload
= Result
596 Result
= FmpAuthHeader
.Encode ()
598 FmpAuthHeader
.DumpInfo ()
600 print ('GenerateCapsule: error: can not encode FMP Auth Header')
602 FmpCapsuleHeader
.AddPayload (SinglePayloadDescriptor
.Guid
, Result
, HardwareInstance
= SinglePayloadDescriptor
.HardwareInstance
, UpdateImageIndex
= SinglePayloadDescriptor
.UpdateImageIndex
)
604 for EmbeddedDriver
in EmbeddedDriverDescriptorList
:
605 FmpCapsuleHeader
.AddEmbeddedDriver(EmbeddedDriver
)
607 Result
= FmpCapsuleHeader
.Encode ()
609 FmpCapsuleHeader
.DumpInfo ()
611 print ('GenerateCapsule: error: can not encode FMP Capsule Header')
615 UefiCapsuleHeader
.OemFlags
= args
.CapsuleOemFlag
616 UefiCapsuleHeader
.PersistAcrossReset
= 'PersistAcrossReset' in args
.CapsuleFlag
617 UefiCapsuleHeader
.PopulateSystemTable
= False
618 UefiCapsuleHeader
.InitiateReset
= 'InitiateReset' in args
.CapsuleFlag
619 UefiCapsuleHeader
.Payload
= Result
620 Result
= UefiCapsuleHeader
.Encode ()
622 UefiCapsuleHeader
.DumpInfo ()
624 print ('GenerateCapsule: error: can not encode UEFI Capsule Header')
628 print ('Write binary output file {File}'.format (File
= args
.OutputFile
.name
))
629 args
.OutputFile
.write (Result
)
630 args
.OutputFile
.close ()
632 print ('GenerateCapsule: error: can not write binary output file {File}'.format (File
= args
.OutputFile
.name
))
635 def Decode (PayloadDescriptorList
, PayloadJsonDescriptorList
, Buffer
):
637 CheckArgumentConflict(args
)
639 # Parse payload descriptors from JSON
642 Json
= json
.loads (args
.JsonFile
.read())
644 print ('GenerateCapsule: error: {JSONFile} loads failure. '.format (JSONFile
= args
.JsonFile
))
646 DecodeJsonFileParse (Json
)
648 PayloadDescriptorList
.append (PayloadDescriptor (
652 args
.LowestSupportedVersion
,
654 args
.HardwareInstance
,
655 args
.UpdateImageIndex
,
656 args
.SignToolPfxFile
,
657 args
.OpenSslSignerPrivateCertFile
,
658 args
.OpenSslOtherPublicCertFile
,
659 args
.OpenSslTrustedPublicCertFile
,
663 # Perform additional verification on payload descriptors
665 for SinglePayloadDescriptor
in PayloadDescriptorList
:
667 SinglePayloadDescriptor
.Validate (args
)
668 except Exception as Msg
:
669 print ('GenerateCapsule: error:' + str(Msg
))
672 Result
= UefiCapsuleHeader
.Decode (Buffer
)
674 Result
= FmpCapsuleHeader
.Decode (Result
)
676 if FmpCapsuleHeader
.PayloadItemCount
!= len (PayloadDescriptorList
):
677 CapsulePayloadNum
= FmpCapsuleHeader
.PayloadItemCount
678 JsonPayloadNum
= len (PayloadDescriptorList
)
679 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
))
681 for Index
in range (0, FmpCapsuleHeader
.PayloadItemCount
):
682 if Index
< len (PayloadDescriptorList
):
683 GUID
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).UpdateImageTypeId
684 HardwareInstance
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).UpdateHardwareInstance
685 UpdateImageIndex
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).UpdateImageIndex
686 if PayloadDescriptorList
[Index
].Guid
!= GUID
or PayloadDescriptorList
[Index
].HardwareInstance
!= HardwareInstance
:
687 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
))
689 PayloadDescriptorList
[Index
].Payload
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).Payload
690 DecodeJsonOutput
= args
.OutputFile
.name
+ '.Payload.{Index:d}.bin'.format (Index
= Index
+ 1)
691 PayloadJsonDescriptorList
.append (PayloadDescriptor (
699 PayloadDescriptorList
[Index
].SignToolPfxFile
,
700 PayloadDescriptorList
[Index
].OpenSslSignerPrivateCertFile
,
701 PayloadDescriptorList
[Index
].OpenSslOtherPublicCertFile
,
702 PayloadDescriptorList
[Index
].OpenSslTrustedPublicCertFile
,
703 PayloadDescriptorList
[Index
].SigningToolPath
706 PayloadDescriptorList
[0].Payload
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (0).Payload
707 for Index
in range (0, FmpCapsuleHeader
.PayloadItemCount
):
709 PayloadDecodeFile
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).Payload
710 PayloadDescriptorList
.append (PayloadDescriptor (PayloadDecodeFile
,
723 GUID
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).UpdateImageTypeId
724 HardwareInstance
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).UpdateHardwareInstance
725 UpdateImageIndex
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).UpdateImageIndex
726 DecodeJsonOutput
= args
.OutputFile
.name
+ '.Payload.{Index:d}.bin'.format (Index
= Index
+ 1)
727 PayloadJsonDescriptorList
.append (PayloadDescriptor (
735 PayloadDescriptorList
[Index
].SignToolPfxFile
,
736 PayloadDescriptorList
[Index
].OpenSslSignerPrivateCertFile
,
737 PayloadDescriptorList
[Index
].OpenSslOtherPublicCertFile
,
738 PayloadDescriptorList
[Index
].OpenSslTrustedPublicCertFile
,
739 PayloadDescriptorList
[Index
].SigningToolPath
742 for SinglePayloadDescriptor
in PayloadDescriptorList
:
745 UefiCapsuleHeader
.DumpInfo ()
747 FmpCapsuleHeader
.DumpInfo ()
748 if FmpAuthHeader
.IsSigned(SinglePayloadDescriptor
.Payload
):
749 if not SinglePayloadDescriptor
.UseOpenSsl
and not SinglePayloadDescriptor
.UseSignTool
:
750 print ('GenerateCapsule: decode warning: can not verify singed payload without cert or pfx file. Index = {Index}'.format (Index
= JsonIndex
+ 1))
751 SinglePayloadDescriptor
.Payload
= FmpAuthHeader
.Decode (SinglePayloadDescriptor
.Payload
)
752 PayloadJsonDescriptorList
[JsonIndex
].MonotonicCount
= FmpAuthHeader
.MonotonicCount
755 FmpAuthHeader
.DumpInfo ()
758 # Verify Image with 64-bit MonotonicCount appended to end of image
761 if SinglePayloadDescriptor
.UseSignTool
:
762 CertData
= VerifyPayloadSignTool (
763 FmpAuthHeader
.Payload
+ struct
.pack ('<Q', FmpAuthHeader
.MonotonicCount
),
764 FmpAuthHeader
.CertData
,
765 SinglePayloadDescriptor
.SigningToolPath
,
766 SinglePayloadDescriptor
.SignToolPfxFile
,
767 Verbose
= args
.Verbose
770 CertData
= VerifyPayloadOpenSsl (
771 FmpAuthHeader
.Payload
+ struct
.pack ('<Q', FmpAuthHeader
.MonotonicCount
),
772 FmpAuthHeader
.CertData
,
773 SinglePayloadDescriptor
.SigningToolPath
,
774 SinglePayloadDescriptor
.OpenSslSignerPrivateCertFile
,
775 SinglePayloadDescriptor
.OpenSslOtherPublicCertFile
,
776 SinglePayloadDescriptor
.OpenSslTrustedPublicCertFile
,
777 Verbose
= args
.Verbose
779 except Exception as Msg
:
780 print ('GenerateCapsule: warning: payload verification failed Index = {Index} \n'.format (Index
= JsonIndex
+ 1) + str(Msg
))
784 print ('No EFI_FIRMWARE_IMAGE_AUTHENTICATION')
786 SinglePayloadDescriptor
.Payload
= FmpPayloadHeader
.Decode (SinglePayloadDescriptor
.Payload
)
787 PayloadJsonDescriptorList
[JsonIndex
].FwVersion
= FmpPayloadHeader
.FwVersion
788 PayloadJsonDescriptorList
[JsonIndex
].LowestSupportedVersion
= FmpPayloadHeader
.LowestSupportedVersion
789 JsonIndex
= JsonIndex
+ 1
792 FmpPayloadHeader
.DumpInfo ()
797 print ('No FMP_PAYLOAD_HEADER')
801 # Write embedded driver file(s)
803 for Index
in range (0, FmpCapsuleHeader
.EmbeddedDriverCount
):
804 EmbeddedDriverBuffer
= FmpCapsuleHeader
.GetEmbeddedDriver (Index
)
805 EmbeddedDriverPath
= args
.OutputFile
.name
+ '.EmbeddedDriver.{Index:d}.efi'.format (Index
= Index
+ 1)
808 print ('Write embedded driver file {File}'.format (File
= EmbeddedDriverPath
))
809 with
open (EmbeddedDriverPath
, 'wb') as EmbeddedDriverFile
:
810 EmbeddedDriverFile
.write (EmbeddedDriverBuffer
)
812 print ('GenerateCapsule: error: can not write embedded driver file {File}'.format (File
= EmbeddedDriverPath
))
816 print ('GenerateCapsule: error: can not decode capsule')
818 GenerateOutputJson(PayloadJsonDescriptorList
)
820 for SinglePayloadDescriptor
in PayloadDescriptorList
:
821 if args
.OutputFile
is None:
822 print ('GenerateCapsule: Decode error: OutputFile is needed for decode output')
826 print ('Write binary output file {File}'.format (File
= args
.OutputFile
.name
))
827 PayloadDecodePath
= args
.OutputFile
.name
+ '.Payload.{Index:d}.bin'.format (Index
= PayloadIndex
+ 1)
828 with
open (PayloadDecodePath
, 'wb') as PayloadDecodeFile
:
829 PayloadDecodeFile
.write (SinglePayloadDescriptor
.Payload
)
830 PayloadIndex
= PayloadIndex
+ 1
832 print ('GenerateCapsule: error: can not write binary output file {File}'.format (File
= SinglePayloadDescriptor
.OutputFile
.name
))
835 def DumpInfo (Buffer
, args
):
836 if args
.OutputFile
is not None:
837 raise argparse
.ArgumentTypeError ('the following option is not supported for dumpinfo operations: --output')
839 Result
= UefiCapsuleHeader
.Decode (Buffer
)
841 UefiCapsuleHeader
.DumpInfo ()
843 FmpCapsuleHeader
.Decode (Result
)
845 FmpCapsuleHeader
.DumpInfo ()
846 for Index
in range (0, FmpCapsuleHeader
.PayloadItemCount
):
847 Result
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).Payload
849 Result
= FmpAuthHeader
.Decode (Result
)
851 FmpAuthHeader
.DumpInfo ()
854 print ('No EFI_FIRMWARE_IMAGE_AUTHENTICATION')
856 Result
= FmpPayloadHeader
.Decode (Result
)
858 FmpPayloadHeader
.DumpInfo ()
861 print ('No FMP_PAYLOAD_HEADER')
864 print ('GenerateCapsule: error: can not decode capsule')
867 # Create command line argument parser object
869 parser
= argparse
.ArgumentParser (
871 description
= __description__
+ __copyright__
,
872 conflict_handler
= 'resolve',
873 fromfile_prefix_chars
= '@'
875 parser
.convert_arg_line_to_args
= convert_arg_line_to_args
878 # Add input and output file arguments
880 parser
.add_argument("InputFile", type = argparse
.FileType('rb'), nargs
='?',
881 help = "Input binary payload filename.")
882 parser
.add_argument("-o", "--output", dest
= 'OutputFile', type = argparse
.FileType('wb'),
883 help = "Output filename.")
885 # Add group for -e and -d flags that are mutually exclusive and required
887 group
= parser
.add_mutually_exclusive_group (required
= True)
888 group
.add_argument ("-e", "--encode", dest
= 'Encode', action
= "store_true",
889 help = "Encode file")
890 group
.add_argument ("-d", "--decode", dest
= 'Decode', action
= "store_true",
891 help = "Decode file")
892 group
.add_argument ("--dump-info", dest
= 'DumpInfo', action
= "store_true",
893 help = "Display FMP Payload Header information")
895 # Add optional arguments for this command
897 parser
.add_argument ("-j", "--json-file", dest
= 'JsonFile', type=argparse
.FileType('r'),
898 help = "JSON configuration file for multiple payloads and embedded drivers.")
899 parser
.add_argument ("--capflag", dest
= 'CapsuleFlag', action
='append', default
= [],
900 choices
=['PersistAcrossReset', 'InitiateReset'],
901 help = "Capsule flag can be PersistAcrossReset or InitiateReset or not set")
902 parser
.add_argument ("--capoemflag", dest
= 'CapsuleOemFlag', type = ValidateUnsignedInteger
, default
= 0x0000,
903 help = "Capsule OEM Flag is an integer between 0x0000 and 0xffff.")
905 parser
.add_argument ("--guid", dest
= 'Guid', type = ValidateRegistryFormatGuid
,
906 help = "The FMP/ESRT GUID in registry format. Required for single payload encode operations.")
907 parser
.add_argument ("--hardware-instance", dest
= 'HardwareInstance', type = ValidateUnsignedInteger
, default
= 0x0000000000000000,
908 help = "The 64-bit hardware instance. The default is 0x0000000000000000")
911 parser
.add_argument ("--monotonic-count", dest
= 'MonotonicCount', type = ValidateUnsignedInteger
, default
= 0x0000000000000000,
912 help = "64-bit monotonic count value in header. Default is 0x0000000000000000.")
914 parser
.add_argument ("--fw-version", dest
= 'FwVersion', type = ValidateUnsignedInteger
,
915 help = "The 32-bit version of the binary payload (e.g. 0x11223344 or 5678). Required for encode operations.")
916 parser
.add_argument ("--lsv", dest
= 'LowestSupportedVersion', type = ValidateUnsignedInteger
,
917 help = "The 32-bit lowest supported version of the binary payload (e.g. 0x11223344 or 5678). Required for encode operations.")
919 parser
.add_argument ("--pfx-file", dest
='SignToolPfxFile', type=argparse
.FileType('rb'),
920 help="signtool PFX certificate filename.")
922 parser
.add_argument ("--signer-private-cert", dest
='OpenSslSignerPrivateCertFile', type=argparse
.FileType('rb'),
923 help="OpenSSL signer private certificate filename.")
924 parser
.add_argument ("--other-public-cert", dest
='OpenSslOtherPublicCertFile', type=argparse
.FileType('rb'),
925 help="OpenSSL other public certificate filename.")
926 parser
.add_argument ("--trusted-public-cert", dest
='OpenSslTrustedPublicCertFile', type=argparse
.FileType('rb'),
927 help="OpenSSL trusted public certificate filename.")
929 parser
.add_argument ("--signing-tool-path", dest
= 'SigningToolPath',
930 help = "Path to signtool or OpenSSL tool. Optional if path to tools are already in PATH.")
932 parser
.add_argument ("--embedded-driver", dest
= 'EmbeddedDriver', type = argparse
.FileType('rb'), action
='append', default
= [],
933 help = "Path to embedded UEFI driver to add to capsule.")
936 # Add optional arguments common to all operations
938 parser
.add_argument ('--version', action
='version', version
='%(prog)s ' + __version__
)
939 parser
.add_argument ("-v", "--verbose", dest
= 'Verbose', action
= "store_true",
940 help = "Turn on verbose output with informational messages printed, including capsule headers and warning messages.")
941 parser
.add_argument ("-q", "--quiet", dest
= 'Quiet', action
= "store_true",
942 help = "Disable all messages except fatal errors.")
943 parser
.add_argument ("--debug", dest
= 'Debug', type = int, metavar
= '[0-9]', choices
= range (0, 10), default
= 0,
944 help = "Set debug level")
945 parser
.add_argument ("--update-image-index", dest
= 'UpdateImageIndex', type = ValidateUnsignedInteger
, default
= 0x01, help = "unique number identifying the firmware image within the device ")
948 # Parse command line arguments
950 args
= parser
.parse_args()
953 # Read binary input file
957 if os
.path
.getsize (args
.InputFile
.name
) == 0:
958 print ('GenerateCapsule: error: InputFile {File} is empty'.format (File
= args
.InputFile
.name
))
962 print ('Read binary input file {File}'.format (File
= args
.InputFile
.name
))
963 Buffer
= args
.InputFile
.read ()
964 args
.InputFile
.close ()
966 print ('GenerateCapsule: error: can not read binary input file {File}'.format (File
= args
.InputFile
.name
))
972 UefiCapsuleHeader
= UefiCapsuleHeaderClass ()
973 FmpCapsuleHeader
= FmpCapsuleHeaderClass ()
974 FmpAuthHeader
= FmpAuthHeaderClass ()
975 FmpPayloadHeader
= FmpPayloadHeaderClass ()
977 EmbeddedDriverDescriptorList
= []
978 PayloadDescriptorList
= []
979 PayloadJsonDescriptorList
= []
985 Encode (PayloadDescriptorList
, EmbeddedDriverDescriptorList
, Buffer
)
991 Decode (PayloadDescriptorList
, PayloadJsonDescriptorList
, Buffer
)
997 DumpInfo (Buffer
, args
)