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
.Uefi
.Capsule
.CapsuleDependency
import CapsuleDependencyClass
35 from Common
.Edk2
.Capsule
.FmpPayloadHeader
import FmpPayloadHeaderClass
38 # Globals for help information
40 __prog__
= 'GenerateCapsule'
42 __copyright__
= 'Copyright (c) 2018, Intel Corporation. All rights reserved.'
43 __description__
= 'Generate a capsule.\n'
45 def SignPayloadSignTool (Payload
, ToolPath
, PfxFile
, Verbose
= False):
47 # Create a temporary directory
49 TempDirectoryName
= tempfile
.mkdtemp()
52 # Generate temp file name for the payload contents
54 TempFileName
= os
.path
.join (TempDirectoryName
, 'Payload.bin')
57 # Create temporary payload file for signing
60 with
open (TempFileName
, 'wb') as File
:
63 shutil
.rmtree (TempDirectoryName
)
64 raise ValueError ('GenerateCapsule: error: can not write temporary payload file.')
67 # Build signtool command
72 Command
= Command
+ '"{Path}" '.format (Path
= os
.path
.join (ToolPath
, 'signtool.exe'))
73 Command
= Command
+ 'sign /fd sha256 /p7ce DetachedSignedData /p7co 1.2.840.113549.1.7.2 '
74 Command
= Command
+ '/p7 {TempDir} '.format (TempDir
= TempDirectoryName
)
75 Command
= Command
+ '/f {PfxFile} '.format (PfxFile
= PfxFile
)
76 Command
= Command
+ TempFileName
81 # Sign the input file using the specified private key
84 Process
= subprocess
.Popen (Command
, stdin
= subprocess
.PIPE
, stdout
= subprocess
.PIPE
, stderr
= subprocess
.PIPE
, shell
= True)
85 Result
= Process
.communicate('')
87 shutil
.rmtree (TempDirectoryName
)
88 raise ValueError ('GenerateCapsule: error: can not run signtool.')
90 if Process
.returncode
!= 0:
91 shutil
.rmtree (TempDirectoryName
)
92 print (Result
[1].decode())
93 raise ValueError ('GenerateCapsule: error: signtool failed.')
96 # Read the signature from the generated output file
99 with
open (TempFileName
+ '.p7', 'rb') as File
:
100 Signature
= File
.read ()
102 shutil
.rmtree (TempDirectoryName
)
103 raise ValueError ('GenerateCapsule: error: can not read signature file.')
105 shutil
.rmtree (TempDirectoryName
)
108 def VerifyPayloadSignTool (Payload
, CertData
, ToolPath
, PfxFile
, Verbose
= False):
109 print ('signtool verify is not supported.')
110 raise ValueError ('GenerateCapsule: error: signtool verify is not supported.')
112 def SignPayloadOpenSsl (Payload
, ToolPath
, SignerPrivateCertFile
, OtherPublicCertFile
, TrustedPublicCertFile
, Verbose
= False):
114 # Build openssl command
119 Command
= Command
+ '"{Path}" '.format (Path
= os
.path
.join (ToolPath
, 'openssl'))
120 Command
= Command
+ 'smime -sign -binary -outform DER -md sha256 '
121 Command
= Command
+ '-signer "{Private}" -certfile "{Public}"'.format (Private
= SignerPrivateCertFile
, Public
= OtherPublicCertFile
)
126 # Sign the input file using the specified private key and capture signature from STDOUT
129 Process
= subprocess
.Popen (Command
, stdin
= subprocess
.PIPE
, stdout
= subprocess
.PIPE
, stderr
= subprocess
.PIPE
, shell
= True)
130 Result
= Process
.communicate(input = Payload
)
131 Signature
= Result
[0]
133 raise ValueError ('GenerateCapsule: error: can not run openssl.')
135 if Process
.returncode
!= 0:
136 print (Result
[1].decode())
137 raise ValueError ('GenerateCapsule: error: openssl failed.')
141 def VerifyPayloadOpenSsl (Payload
, CertData
, ToolPath
, SignerPrivateCertFile
, OtherPublicCertFile
, TrustedPublicCertFile
, Verbose
= False):
143 # Create a temporary directory
145 TempDirectoryName
= tempfile
.mkdtemp()
148 # Generate temp file name for the payload contents
150 TempFileName
= os
.path
.join (TempDirectoryName
, 'Payload.bin')
153 # Create temporary payload file for verification
156 with
open (TempFileName
, 'wb') as File
:
159 shutil
.rmtree (TempDirectoryName
)
160 raise ValueError ('GenerateCapsule: error: can not write temporary payload file.')
163 # Build openssl command
168 Command
= Command
+ '"{Path}" '.format (Path
= os
.path
.join (ToolPath
, 'openssl'))
169 Command
= Command
+ 'smime -verify -inform DER '
170 Command
= Command
+ '-content {Content} -CAfile "{Public}"'.format (Content
= TempFileName
, Public
= TrustedPublicCertFile
)
178 Process
= subprocess
.Popen (Command
, stdin
= subprocess
.PIPE
, stdout
= subprocess
.PIPE
, stderr
= subprocess
.PIPE
, shell
= True)
179 Result
= Process
.communicate(input = CertData
)
181 shutil
.rmtree (TempDirectoryName
)
182 raise ValueError ('GenerateCapsule: error: can not run openssl.')
184 if Process
.returncode
!= 0:
185 shutil
.rmtree (TempDirectoryName
)
186 print (Result
[1].decode())
187 raise ValueError ('GenerateCapsule: error: openssl failed.')
189 shutil
.rmtree (TempDirectoryName
)
192 if __name__
== '__main__':
193 def convert_arg_line_to_args(arg_line
):
194 for arg
in arg_line
.split():
199 def ValidateUnsignedInteger (Argument
):
201 Value
= int (Argument
, 0)
203 Message
= '{Argument} is not a valid integer value.'.format (Argument
= Argument
)
204 raise argparse
.ArgumentTypeError (Message
)
206 Message
= '{Argument} is a negative value.'.format (Argument
= Argument
)
207 raise argparse
.ArgumentTypeError (Message
)
210 def ValidateRegistryFormatGuid (Argument
):
212 Value
= uuid
.UUID (Argument
)
214 Message
= '{Argument} is not a valid registry format GUID value.'.format (Argument
= Argument
)
215 raise argparse
.ArgumentTypeError (Message
)
218 def ConvertJsonValue (Config
, FieldName
, Convert
, Required
= True, Default
= None, Open
= False):
219 if FieldName
not in Config
:
221 print ('GenerateCapsule: error: Payload descriptor invalid syntax. Could not find {Key} in payload descriptor.'.format(Key
= FieldName
))
225 Value
= Convert (Config
[FieldName
])
227 print ('GenerateCapsule: error: {Key} in payload descriptor has invalid syntax.'.format (Key
= FieldName
))
231 Value
= open (Value
, "rb")
233 print ('GenerateCapsule: error: can not open file {File}'.format (File
= FieldName
))
237 def DecodeJsonFileParse (Json
):
238 if 'Payloads' not in Json
:
239 print ('GenerateCapsule: error "Payloads" section not found in JSON file {File}'.format (File
= args
.JsonFile
.name
))
241 for Config
in Json
['Payloads']:
243 # Parse fields from JSON
245 PayloadFile
= ConvertJsonValue (Config
, 'Payload', os
.path
.expandvars
, Required
= False)
246 Guid
= ConvertJsonValue (Config
, 'Guid', ValidateRegistryFormatGuid
, Required
= False)
247 FwVersion
= ConvertJsonValue (Config
, 'FwVersion', ValidateUnsignedInteger
, Required
= False)
248 LowestSupportedVersion
= ConvertJsonValue (Config
, 'LowestSupportedVersion', ValidateUnsignedInteger
, Required
= False)
249 HardwareInstance
= ConvertJsonValue (Config
, 'HardwareInstance', ValidateUnsignedInteger
, Required
= False, Default
= 0)
250 MonotonicCount
= ConvertJsonValue (Config
, 'MonotonicCount', ValidateUnsignedInteger
, Required
= False, Default
= 0)
251 SignToolPfxFile
= ConvertJsonValue (Config
, 'SignToolPfxFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
252 OpenSslSignerPrivateCertFile
= ConvertJsonValue (Config
, 'OpenSslSignerPrivateCertFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
253 OpenSslOtherPublicCertFile
= ConvertJsonValue (Config
, 'OpenSslOtherPublicCertFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
254 OpenSslTrustedPublicCertFile
= ConvertJsonValue (Config
, 'OpenSslTrustedPublicCertFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
255 SigningToolPath
= ConvertJsonValue (Config
, 'SigningToolPath', os
.path
.expandvars
, Required
= False, Default
= None)
256 UpdateImageIndex
= ConvertJsonValue (Config
, 'UpdateImageIndex', ValidateUnsignedInteger
, Required
= False, Default
= 1)
258 PayloadDescriptorList
.append (PayloadDescriptor (
262 LowestSupportedVersion
,
267 OpenSslSignerPrivateCertFile
,
268 OpenSslOtherPublicCertFile
,
269 OpenSslTrustedPublicCertFile
,
273 def EncodeJsonFileParse (Json
):
274 if 'EmbeddedDrivers' not in Json
:
275 print ('GenerateCapsule: warning "EmbeddedDrivers" section not found in JSON file {File}'.format (File
= args
.JsonFile
.name
))
277 for Config
in Json
['EmbeddedDrivers']:
278 EmbeddedDriverFile
= ConvertJsonValue(Config
, 'Driver', os
.path
.expandvars
, Open
= True)
280 #Read EmbeddedDriver file
284 print ('Read EmbeddedDriver file {File}'.format (File
= EmbeddedDriverFile
.name
))
285 Driver
= EmbeddedDriverFile
.read()
287 print ('GenerateCapsule: error: can not read EmbeddedDriver file {File}'.format (File
= EmbeddedDriverFile
.name
))
289 EmbeddedDriverDescriptorList
.append (Driver
)
291 if 'Payloads' not in Json
:
292 print ('GenerateCapsule: error: "Payloads" section not found in JSON file {File}'.format (File
= args
.JsonFile
.name
))
294 for Config
in Json
['Payloads']:
296 # Parse fields from JSON
298 PayloadFile
= ConvertJsonValue (Config
, 'Payload', os
.path
.expandvars
, Open
= True)
299 Guid
= ConvertJsonValue (Config
, 'Guid', ValidateRegistryFormatGuid
)
300 FwVersion
= ConvertJsonValue (Config
, 'FwVersion', ValidateUnsignedInteger
)
301 LowestSupportedVersion
= ConvertJsonValue (Config
, 'LowestSupportedVersion', ValidateUnsignedInteger
)
302 HardwareInstance
= ConvertJsonValue (Config
, 'HardwareInstance', ValidateUnsignedInteger
, Required
= False, Default
= 0)
303 UpdateImageIndex
= ConvertJsonValue (Config
, 'UpdateImageIndex', ValidateUnsignedInteger
, Required
= False, Default
= 1)
304 MonotonicCount
= ConvertJsonValue (Config
, 'MonotonicCount', ValidateUnsignedInteger
, Required
= False, Default
= 0)
305 SignToolPfxFile
= ConvertJsonValue (Config
, 'SignToolPfxFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
306 OpenSslSignerPrivateCertFile
= ConvertJsonValue (Config
, 'OpenSslSignerPrivateCertFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
307 OpenSslOtherPublicCertFile
= ConvertJsonValue (Config
, 'OpenSslOtherPublicCertFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
308 OpenSslTrustedPublicCertFile
= ConvertJsonValue (Config
, 'OpenSslTrustedPublicCertFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
309 SigningToolPath
= ConvertJsonValue (Config
, 'SigningToolPath', os
.path
.expandvars
, Required
= False, Default
= None)
310 DepexExp
= ConvertJsonValue (Config
, 'Dependencies', str, Required
= False, Default
= None)
313 # Read binary input file
317 print ('Read binary input file {File}'.format (File
= PayloadFile
.name
))
318 Payload
= PayloadFile
.read()
321 print ('GenerateCapsule: error: can not read binary input file {File}'.format (File
= PayloadFile
.name
))
323 PayloadDescriptorList
.append (PayloadDescriptor (
327 LowestSupportedVersion
,
332 OpenSslSignerPrivateCertFile
,
333 OpenSslOtherPublicCertFile
,
334 OpenSslTrustedPublicCertFile
,
339 def GenerateOutputJson (PayloadJsonDescriptorList
):
343 "Guid": str(PayloadDescriptor
.Guid
).upper(),
344 "FwVersion": str(PayloadDescriptor
.FwVersion
),
345 "LowestSupportedVersion": str(PayloadDescriptor
.LowestSupportedVersion
),
346 "MonotonicCount": str(PayloadDescriptor
.MonotonicCount
),
347 "Payload": PayloadDescriptor
.Payload
,
348 "HardwareInstance": str(PayloadDescriptor
.HardwareInstance
),
349 "UpdateImageIndex": str(PayloadDescriptor
.UpdateImageIndex
),
350 "SignToolPfxFile": str(PayloadDescriptor
.SignToolPfxFile
),
351 "OpenSslSignerPrivateCertFile": str(PayloadDescriptor
.OpenSslSignerPrivateCertFile
),
352 "OpenSslOtherPublicCertFile": str(PayloadDescriptor
.OpenSslOtherPublicCertFile
),
353 "OpenSslTrustedPublicCertFile": str(PayloadDescriptor
.OpenSslTrustedPublicCertFile
),
354 "SigningToolPath": str(PayloadDescriptor
.SigningToolPath
),
355 "Dependencies" : str(PayloadDescriptor
.DepexExp
)
356 }for PayloadDescriptor
in PayloadJsonDescriptorList
359 OutputJsonFile
= args
.OutputFile
.name
+ '.json'
360 if 'Payloads' in PayloadJson
:
361 PayloadSection
= PayloadJson
['Payloads']
363 for PayloadField
in PayloadSection
:
364 if PayloadJsonDescriptorList
[Index
].SignToolPfxFile
is None:
365 del PayloadField
['SignToolPfxFile']
366 if PayloadJsonDescriptorList
[Index
].OpenSslSignerPrivateCertFile
is None:
367 del PayloadField
['OpenSslSignerPrivateCertFile']
368 if PayloadJsonDescriptorList
[Index
].OpenSslOtherPublicCertFile
is None:
369 del PayloadField
['OpenSslOtherPublicCertFile']
370 if PayloadJsonDescriptorList
[Index
].OpenSslTrustedPublicCertFile
is None:
371 del PayloadField
['OpenSslTrustedPublicCertFile']
372 if PayloadJsonDescriptorList
[Index
].SigningToolPath
is None:
373 del PayloadField
['SigningToolPath']
375 Result
= json
.dumps (PayloadJson
, indent
=4, sort_keys
=True, separators
=(',', ': '))
376 with
open (OutputJsonFile
, 'w') as OutputFile
:
377 OutputFile
.write (Result
)
379 def CheckArgumentConflict (args
):
382 print ('GenerateCapsule: error: Argument InputFile conflicts with Argument -j')
384 if args
.EmbeddedDriver
:
385 print ('GenerateCapsule: error: Argument --embedded-driver conflicts with Argument -j')
388 print ('GenerateCapsule: error: Argument --guid conflicts with Argument -j')
391 print ('GenerateCapsule: error: Argument --fw-version conflicts with Argument -j')
393 if args
.LowestSupportedVersion
:
394 print ('GenerateCapsule: error: Argument --lsv conflicts with Argument -j')
396 if args
.MonotonicCount
:
397 print ('GenerateCapsule: error: Argument --monotonic-count conflicts with Argument -j')
399 if args
.HardwareInstance
:
400 print ('GenerateCapsule: error: Argument --hardware-instance conflicts with Argument -j')
402 if args
.SignToolPfxFile
:
403 print ('GenerateCapsule: error: Argument --pfx-file conflicts with Argument -j')
405 if args
.OpenSslSignerPrivateCertFile
:
406 print ('GenerateCapsule: error: Argument --signer-private-cert conflicts with Argument -j')
408 if args
.OpenSslOtherPublicCertFile
:
409 print ('GenerateCapsule: error: Argument --other-public-cert conflicts with Argument -j')
411 if args
.OpenSslTrustedPublicCertFile
:
412 print ('GenerateCapsule: error: Argument --trusted-public-cert conflicts with Argument -j')
414 if args
.SigningToolPath
:
415 print ('GenerateCapsule: error: Argument --signing-tool-path conflicts with Argument -j')
418 class PayloadDescriptor (object):
423 LowestSupportedVersion
,
425 HardwareInstance
= 0,
426 UpdateImageIndex
= 1,
427 SignToolPfxFile
= None,
428 OpenSslSignerPrivateCertFile
= None,
429 OpenSslOtherPublicCertFile
= None,
430 OpenSslTrustedPublicCertFile
= None,
431 SigningToolPath
= None,
434 self
.Payload
= Payload
436 self
.FwVersion
= FwVersion
437 self
.LowestSupportedVersion
= LowestSupportedVersion
438 self
.MonotonicCount
= MonotonicCount
439 self
.HardwareInstance
= HardwareInstance
440 self
.UpdateImageIndex
= UpdateImageIndex
441 self
.SignToolPfxFile
= SignToolPfxFile
442 self
.OpenSslSignerPrivateCertFile
= OpenSslSignerPrivateCertFile
443 self
.OpenSslOtherPublicCertFile
= OpenSslOtherPublicCertFile
444 self
.OpenSslTrustedPublicCertFile
= OpenSslTrustedPublicCertFile
445 self
.SigningToolPath
= SigningToolPath
446 self
.DepexExp
= DepexExp
448 self
.UseSignTool
= self
.SignToolPfxFile
is not None
449 self
.UseOpenSsl
= (self
.OpenSslSignerPrivateCertFile
is not None and
450 self
.OpenSslOtherPublicCertFile
is not None and
451 self
.OpenSslTrustedPublicCertFile
is not None)
452 self
.AnyOpenSsl
= (self
.OpenSslSignerPrivateCertFile
is not None or
453 self
.OpenSslOtherPublicCertFile
is not None or
454 self
.OpenSslTrustedPublicCertFile
is not None)
455 self
.UseDependency
= self
.DepexExp
is not None
457 def Validate(self
, args
):
458 if self
.UseSignTool
and self
.AnyOpenSsl
:
459 raise argparse
.ArgumentTypeError ('Providing both signtool and OpenSSL options is not supported')
460 if not self
.UseSignTool
and not self
.UseOpenSsl
and self
.AnyOpenSsl
:
462 raise argparse
.ArgumentTypeError ('the following JSON fields are required for OpenSSL: OpenSslSignerPrivateCertFile, OpenSslOtherPublicCertFile, OpenSslTrustedPublicCertFile')
464 raise argparse
.ArgumentTypeError ('the following options are required for OpenSSL: --signer-private-cert, --other-public-cert, --trusted-public-cert')
465 if self
.UseSignTool
and platform
.system() != 'Windows':
466 raise argparse
.ArgumentTypeError ('Use of signtool is not supported on this operating system.')
468 if self
.FwVersion
is None or self
.LowestSupportedVersion
is None:
470 raise argparse
.ArgumentTypeError ('the following JSON fields are required: FwVersion, LowestSupportedVersion')
472 raise argparse
.ArgumentTypeError ('the following options are required: --fw-version, --lsv')
473 if self
.FwVersion
> 0xFFFFFFFF:
475 raise argparse
.ArgumentTypeError ('JSON field FwVersion must be an integer in range 0x0..0xffffffff')
477 raise argparse
.ArgumentTypeError ('--fw-version must be an integer in range 0x0..0xffffffff')
478 if self
.LowestSupportedVersion
> 0xFFFFFFFF:
480 raise argparse
.ArgumentTypeError ('JSON field LowestSupportedVersion must be an integer in range 0x0..0xffffffff')
482 raise argparse
.ArgumentTypeError ('--lsv must be an integer in range 0x0..0xffffffff')
485 if self
.Guid
is None:
487 raise argparse
.ArgumentTypeError ('the following JSON field is required: Guid')
489 raise argparse
.ArgumentTypeError ('the following option is required: --guid')
490 if self
.HardwareInstance
> 0xFFFFFFFFFFFFFFFF:
492 raise argparse
.ArgumentTypeError ('JSON field HardwareInstance must be an integer in range 0x0..0xffffffffffffffff')
494 raise argparse
.ArgumentTypeError ('--hardware-instance must be an integer in range 0x0..0xffffffffffffffff')
495 if self
.MonotonicCount
> 0xFFFFFFFFFFFFFFFF:
497 raise argparse
.ArgumentTypeError ('JSON field MonotonicCount must be an integer in range 0x0..0xffffffffffffffff')
499 raise argparse
.ArgumentTypeError ('--monotonic-count must be an integer in range 0x0..0xffffffffffffffff')
500 if self
.UpdateImageIndex
>0xFF:
502 raise argparse
.ArgumentTypeError ('JSON field UpdateImageIndex must be an integer in range 0x0..0xff')
504 raise argparse
.ArgumentTypeError ('--update-image-index must be an integer in range 0x0..0xff')
507 self
.SignToolPfxFile
.close()
508 self
.SignToolPfxFile
= self
.SignToolPfxFile
.name
510 self
.OpenSslSignerPrivateCertFile
.close()
511 self
.OpenSslOtherPublicCertFile
.close()
512 self
.OpenSslTrustedPublicCertFile
.close()
513 self
.OpenSslSignerPrivateCertFile
= self
.OpenSslSignerPrivateCertFile
.name
514 self
.OpenSslOtherPublicCertFile
= self
.OpenSslOtherPublicCertFile
.name
515 self
.OpenSslTrustedPublicCertFile
= self
.OpenSslTrustedPublicCertFile
.name
518 # Perform additional argument verification
521 if 'PersistAcrossReset' not in args
.CapsuleFlag
:
522 if 'InitiateReset' in args
.CapsuleFlag
:
523 raise argparse
.ArgumentTypeError ('--capflag InitiateReset also requires --capflag PersistAcrossReset')
524 if args
.CapsuleOemFlag
> 0xFFFF:
525 raise argparse
.ArgumentTypeError ('--capoemflag must be an integer between 0x0000 and 0xffff')
530 def Encode (PayloadDescriptorList
, EmbeddedDriverDescriptorList
, Buffer
):
532 CheckArgumentConflict(args
)
534 Json
= json
.loads (args
.JsonFile
.read ())
536 print ('GenerateCapsule: error: {JSONFile} loads failure. '.format (JSONFile
= args
.JsonFile
))
538 EncodeJsonFileParse(Json
)
540 for Driver
in args
.EmbeddedDriver
:
541 EmbeddedDriverDescriptorList
.append (Driver
.read())
542 PayloadDescriptorList
.append (PayloadDescriptor (
546 args
.LowestSupportedVersion
,
548 args
.HardwareInstance
,
549 args
.UpdateImageIndex
,
550 args
.SignToolPfxFile
,
551 args
.OpenSslSignerPrivateCertFile
,
552 args
.OpenSslOtherPublicCertFile
,
553 args
.OpenSslTrustedPublicCertFile
,
554 args
.SigningToolPath
,
557 for SinglePayloadDescriptor
in PayloadDescriptorList
:
559 SinglePayloadDescriptor
.Validate (args
)
560 except Exception as Msg
:
561 print ('GenerateCapsule: error:' + str(Msg
))
563 for SinglePayloadDescriptor
in PayloadDescriptorList
:
564 Result
= SinglePayloadDescriptor
.Payload
566 FmpPayloadHeader
.FwVersion
= SinglePayloadDescriptor
.FwVersion
567 FmpPayloadHeader
.LowestSupportedVersion
= SinglePayloadDescriptor
.LowestSupportedVersion
568 FmpPayloadHeader
.Payload
= SinglePayloadDescriptor
.Payload
569 Result
= FmpPayloadHeader
.Encode ()
571 FmpPayloadHeader
.DumpInfo ()
573 print ('GenerateCapsule: error: can not encode FMP Payload Header')
575 if SinglePayloadDescriptor
.UseDependency
:
576 CapsuleDependency
.Payload
= Result
577 CapsuleDependency
.DepexExp
= SinglePayloadDescriptor
.DepexExp
578 Result
= CapsuleDependency
.Encode ()
580 CapsuleDependency
.DumpInfo ()
581 if SinglePayloadDescriptor
.UseOpenSsl
or SinglePayloadDescriptor
.UseSignTool
:
583 # Sign image with 64-bit MonotonicCount appended to end of image
586 if SinglePayloadDescriptor
.UseSignTool
:
587 CertData
= SignPayloadSignTool (
588 Result
+ struct
.pack ('<Q', SinglePayloadDescriptor
.MonotonicCount
),
589 SinglePayloadDescriptor
.SigningToolPath
,
590 SinglePayloadDescriptor
.SignToolPfxFile
,
591 Verbose
= args
.Verbose
594 CertData
= SignPayloadOpenSsl (
595 Result
+ struct
.pack ('<Q', SinglePayloadDescriptor
.MonotonicCount
),
596 SinglePayloadDescriptor
.SigningToolPath
,
597 SinglePayloadDescriptor
.OpenSslSignerPrivateCertFile
,
598 SinglePayloadDescriptor
.OpenSslOtherPublicCertFile
,
599 SinglePayloadDescriptor
.OpenSslTrustedPublicCertFile
,
600 Verbose
= args
.Verbose
602 except Exception as Msg
:
603 print ('GenerateCapsule: error: can not sign payload \n' + str(Msg
))
607 FmpAuthHeader
.MonotonicCount
= SinglePayloadDescriptor
.MonotonicCount
608 FmpAuthHeader
.CertData
= CertData
609 FmpAuthHeader
.Payload
= Result
610 Result
= FmpAuthHeader
.Encode ()
612 FmpAuthHeader
.DumpInfo ()
614 print ('GenerateCapsule: error: can not encode FMP Auth Header')
616 FmpCapsuleHeader
.AddPayload (SinglePayloadDescriptor
.Guid
, Result
, HardwareInstance
= SinglePayloadDescriptor
.HardwareInstance
, UpdateImageIndex
= SinglePayloadDescriptor
.UpdateImageIndex
)
618 for EmbeddedDriver
in EmbeddedDriverDescriptorList
:
619 FmpCapsuleHeader
.AddEmbeddedDriver(EmbeddedDriver
)
621 Result
= FmpCapsuleHeader
.Encode ()
623 FmpCapsuleHeader
.DumpInfo ()
625 print ('GenerateCapsule: error: can not encode FMP Capsule Header')
629 UefiCapsuleHeader
.OemFlags
= args
.CapsuleOemFlag
630 UefiCapsuleHeader
.PersistAcrossReset
= 'PersistAcrossReset' in args
.CapsuleFlag
631 UefiCapsuleHeader
.PopulateSystemTable
= False
632 UefiCapsuleHeader
.InitiateReset
= 'InitiateReset' in args
.CapsuleFlag
633 UefiCapsuleHeader
.Payload
= Result
634 Result
= UefiCapsuleHeader
.Encode ()
636 UefiCapsuleHeader
.DumpInfo ()
638 print ('GenerateCapsule: error: can not encode UEFI Capsule Header')
642 print ('Write binary output file {File}'.format (File
= args
.OutputFile
.name
))
643 args
.OutputFile
.write (Result
)
644 args
.OutputFile
.close ()
646 print ('GenerateCapsule: error: can not write binary output file {File}'.format (File
= args
.OutputFile
.name
))
649 def Decode (PayloadDescriptorList
, PayloadJsonDescriptorList
, Buffer
):
651 CheckArgumentConflict(args
)
653 # Parse payload descriptors from JSON
656 Json
= json
.loads (args
.JsonFile
.read())
658 print ('GenerateCapsule: error: {JSONFile} loads failure. '.format (JSONFile
= args
.JsonFile
))
660 DecodeJsonFileParse (Json
)
662 PayloadDescriptorList
.append (PayloadDescriptor (
666 args
.LowestSupportedVersion
,
668 args
.HardwareInstance
,
669 args
.UpdateImageIndex
,
670 args
.SignToolPfxFile
,
671 args
.OpenSslSignerPrivateCertFile
,
672 args
.OpenSslOtherPublicCertFile
,
673 args
.OpenSslTrustedPublicCertFile
,
674 args
.SigningToolPath
,
678 # Perform additional verification on payload descriptors
680 for SinglePayloadDescriptor
in PayloadDescriptorList
:
682 SinglePayloadDescriptor
.Validate (args
)
683 except Exception as Msg
:
684 print ('GenerateCapsule: error:' + str(Msg
))
687 Result
= UefiCapsuleHeader
.Decode (Buffer
)
689 Result
= FmpCapsuleHeader
.Decode (Result
)
691 if FmpCapsuleHeader
.PayloadItemCount
!= len (PayloadDescriptorList
):
692 CapsulePayloadNum
= FmpCapsuleHeader
.PayloadItemCount
693 JsonPayloadNum
= len (PayloadDescriptorList
)
694 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
))
696 for Index
in range (0, FmpCapsuleHeader
.PayloadItemCount
):
697 if Index
< len (PayloadDescriptorList
):
698 GUID
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).UpdateImageTypeId
699 HardwareInstance
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).UpdateHardwareInstance
700 UpdateImageIndex
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).UpdateImageIndex
701 if PayloadDescriptorList
[Index
].Guid
!= GUID
or PayloadDescriptorList
[Index
].HardwareInstance
!= HardwareInstance
:
702 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
))
704 PayloadDescriptorList
[Index
].Payload
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).Payload
705 DecodeJsonOutput
= args
.OutputFile
.name
+ '.Payload.{Index:d}.bin'.format (Index
= Index
+ 1)
706 PayloadJsonDescriptorList
.append (PayloadDescriptor (
714 PayloadDescriptorList
[Index
].SignToolPfxFile
,
715 PayloadDescriptorList
[Index
].OpenSslSignerPrivateCertFile
,
716 PayloadDescriptorList
[Index
].OpenSslOtherPublicCertFile
,
717 PayloadDescriptorList
[Index
].OpenSslTrustedPublicCertFile
,
718 PayloadDescriptorList
[Index
].SigningToolPath
,
722 PayloadDescriptorList
[0].Payload
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (0).Payload
723 for Index
in range (0, FmpCapsuleHeader
.PayloadItemCount
):
725 PayloadDecodeFile
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).Payload
726 PayloadDescriptorList
.append (PayloadDescriptor (PayloadDecodeFile
,
740 GUID
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).UpdateImageTypeId
741 HardwareInstance
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).UpdateHardwareInstance
742 UpdateImageIndex
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).UpdateImageIndex
743 DecodeJsonOutput
= args
.OutputFile
.name
+ '.Payload.{Index:d}.bin'.format (Index
= Index
+ 1)
744 PayloadJsonDescriptorList
.append (PayloadDescriptor (
752 PayloadDescriptorList
[Index
].SignToolPfxFile
,
753 PayloadDescriptorList
[Index
].OpenSslSignerPrivateCertFile
,
754 PayloadDescriptorList
[Index
].OpenSslOtherPublicCertFile
,
755 PayloadDescriptorList
[Index
].OpenSslTrustedPublicCertFile
,
756 PayloadDescriptorList
[Index
].SigningToolPath
,
760 for SinglePayloadDescriptor
in PayloadDescriptorList
:
763 UefiCapsuleHeader
.DumpInfo ()
765 FmpCapsuleHeader
.DumpInfo ()
766 if FmpAuthHeader
.IsSigned(SinglePayloadDescriptor
.Payload
):
767 if not SinglePayloadDescriptor
.UseOpenSsl
and not SinglePayloadDescriptor
.UseSignTool
:
768 print ('GenerateCapsule: decode warning: can not verify singed payload without cert or pfx file. Index = {Index}'.format (Index
= JsonIndex
+ 1))
769 SinglePayloadDescriptor
.Payload
= FmpAuthHeader
.Decode (SinglePayloadDescriptor
.Payload
)
770 PayloadJsonDescriptorList
[JsonIndex
].MonotonicCount
= FmpAuthHeader
.MonotonicCount
773 FmpAuthHeader
.DumpInfo ()
776 # Verify Image with 64-bit MonotonicCount appended to end of image
779 if SinglePayloadDescriptor
.UseSignTool
:
780 CertData
= VerifyPayloadSignTool (
781 FmpAuthHeader
.Payload
+ struct
.pack ('<Q', FmpAuthHeader
.MonotonicCount
),
782 FmpAuthHeader
.CertData
,
783 SinglePayloadDescriptor
.SigningToolPath
,
784 SinglePayloadDescriptor
.SignToolPfxFile
,
785 Verbose
= args
.Verbose
788 CertData
= VerifyPayloadOpenSsl (
789 FmpAuthHeader
.Payload
+ struct
.pack ('<Q', FmpAuthHeader
.MonotonicCount
),
790 FmpAuthHeader
.CertData
,
791 SinglePayloadDescriptor
.SigningToolPath
,
792 SinglePayloadDescriptor
.OpenSslSignerPrivateCertFile
,
793 SinglePayloadDescriptor
.OpenSslOtherPublicCertFile
,
794 SinglePayloadDescriptor
.OpenSslTrustedPublicCertFile
,
795 Verbose
= args
.Verbose
797 except Exception as Msg
:
798 print ('GenerateCapsule: warning: payload verification failed Index = {Index} \n'.format (Index
= JsonIndex
+ 1) + str(Msg
))
802 print ('No EFI_FIRMWARE_IMAGE_AUTHENTICATION')
804 PayloadSignature
= struct
.unpack ('<I', SinglePayloadDescriptor
.Payload
[0:4])
805 if PayloadSignature
!= FmpPayloadHeader
.Signature
:
806 SinglePayloadDescriptor
.UseDependency
= True
808 SinglePayloadDescriptor
.Payload
= CapsuleDependency
.Decode (SinglePayloadDescriptor
.Payload
)
809 PayloadJsonDescriptorList
[JsonIndex
].DepexExp
= CapsuleDependency
.DepexExp
812 CapsuleDependency
.DumpInfo ()
813 except Exception as Msg
:
814 print ('GenerateCapsule: error: invalid dependency expression')
818 print ('No EFI_FIRMWARE_IMAGE_DEP')
821 SinglePayloadDescriptor
.Payload
= FmpPayloadHeader
.Decode (SinglePayloadDescriptor
.Payload
)
822 PayloadJsonDescriptorList
[JsonIndex
].FwVersion
= FmpPayloadHeader
.FwVersion
823 PayloadJsonDescriptorList
[JsonIndex
].LowestSupportedVersion
= FmpPayloadHeader
.LowestSupportedVersion
824 JsonIndex
= JsonIndex
+ 1
827 FmpPayloadHeader
.DumpInfo ()
832 print ('No FMP_PAYLOAD_HEADER')
836 # Write embedded driver file(s)
838 for Index
in range (0, FmpCapsuleHeader
.EmbeddedDriverCount
):
839 EmbeddedDriverBuffer
= FmpCapsuleHeader
.GetEmbeddedDriver (Index
)
840 EmbeddedDriverPath
= args
.OutputFile
.name
+ '.EmbeddedDriver.{Index:d}.efi'.format (Index
= Index
+ 1)
843 print ('Write embedded driver file {File}'.format (File
= EmbeddedDriverPath
))
844 with
open (EmbeddedDriverPath
, 'wb') as EmbeddedDriverFile
:
845 EmbeddedDriverFile
.write (EmbeddedDriverBuffer
)
847 print ('GenerateCapsule: error: can not write embedded driver file {File}'.format (File
= EmbeddedDriverPath
))
851 print ('GenerateCapsule: error: can not decode capsule')
853 GenerateOutputJson(PayloadJsonDescriptorList
)
855 for SinglePayloadDescriptor
in PayloadDescriptorList
:
856 if args
.OutputFile
is None:
857 print ('GenerateCapsule: Decode error: OutputFile is needed for decode output')
861 print ('Write binary output file {File}'.format (File
= args
.OutputFile
.name
))
862 PayloadDecodePath
= args
.OutputFile
.name
+ '.Payload.{Index:d}.bin'.format (Index
= PayloadIndex
+ 1)
863 with
open (PayloadDecodePath
, 'wb') as PayloadDecodeFile
:
864 PayloadDecodeFile
.write (SinglePayloadDescriptor
.Payload
)
865 PayloadIndex
= PayloadIndex
+ 1
867 print ('GenerateCapsule: error: can not write binary output file {File}'.format (File
= SinglePayloadDescriptor
.OutputFile
.name
))
870 def DumpInfo (Buffer
, args
):
871 if args
.OutputFile
is not None:
872 raise argparse
.ArgumentTypeError ('the following option is not supported for dumpinfo operations: --output')
874 Result
= UefiCapsuleHeader
.Decode (Buffer
)
876 UefiCapsuleHeader
.DumpInfo ()
878 FmpCapsuleHeader
.Decode (Result
)
880 FmpCapsuleHeader
.DumpInfo ()
881 for Index
in range (0, FmpCapsuleHeader
.PayloadItemCount
):
882 Result
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).Payload
884 Result
= FmpAuthHeader
.Decode (Result
)
886 FmpAuthHeader
.DumpInfo ()
889 print ('No EFI_FIRMWARE_IMAGE_AUTHENTICATION')
891 PayloadSignature
= struct
.unpack ('<I', Result
[0:4])
892 if PayloadSignature
!= FmpPayloadHeader
.Signature
:
894 Result
= CapsuleDependency
.Decode (Result
)
896 CapsuleDependency
.DumpInfo ()
898 print ('GenerateCapsule: error: invalid dependency expression')
901 print ('No EFI_FIRMWARE_IMAGE_DEP')
903 Result
= FmpPayloadHeader
.Decode (Result
)
905 FmpPayloadHeader
.DumpInfo ()
908 print ('No FMP_PAYLOAD_HEADER')
911 print ('GenerateCapsule: error: can not decode capsule')
914 # Create command line argument parser object
916 parser
= argparse
.ArgumentParser (
918 description
= __description__
+ __copyright__
,
919 conflict_handler
= 'resolve',
920 fromfile_prefix_chars
= '@'
922 parser
.convert_arg_line_to_args
= convert_arg_line_to_args
925 # Add input and output file arguments
927 parser
.add_argument("InputFile", type = argparse
.FileType('rb'), nargs
='?',
928 help = "Input binary payload filename.")
929 parser
.add_argument("-o", "--output", dest
= 'OutputFile', type = argparse
.FileType('wb'),
930 help = "Output filename.")
932 # Add group for -e and -d flags that are mutually exclusive and required
934 group
= parser
.add_mutually_exclusive_group (required
= True)
935 group
.add_argument ("-e", "--encode", dest
= 'Encode', action
= "store_true",
936 help = "Encode file")
937 group
.add_argument ("-d", "--decode", dest
= 'Decode', action
= "store_true",
938 help = "Decode file")
939 group
.add_argument ("--dump-info", dest
= 'DumpInfo', action
= "store_true",
940 help = "Display FMP Payload Header information")
942 # Add optional arguments for this command
944 parser
.add_argument ("-j", "--json-file", dest
= 'JsonFile', type=argparse
.FileType('r'),
945 help = "JSON configuration file for multiple payloads and embedded drivers.")
946 parser
.add_argument ("--capflag", dest
= 'CapsuleFlag', action
='append', default
= [],
947 choices
=['PersistAcrossReset', 'InitiateReset'],
948 help = "Capsule flag can be PersistAcrossReset or InitiateReset or not set")
949 parser
.add_argument ("--capoemflag", dest
= 'CapsuleOemFlag', type = ValidateUnsignedInteger
, default
= 0x0000,
950 help = "Capsule OEM Flag is an integer between 0x0000 and 0xffff.")
952 parser
.add_argument ("--guid", dest
= 'Guid', type = ValidateRegistryFormatGuid
,
953 help = "The FMP/ESRT GUID in registry format. Required for single payload encode operations.")
954 parser
.add_argument ("--hardware-instance", dest
= 'HardwareInstance', type = ValidateUnsignedInteger
, default
= 0x0000000000000000,
955 help = "The 64-bit hardware instance. The default is 0x0000000000000000")
958 parser
.add_argument ("--monotonic-count", dest
= 'MonotonicCount', type = ValidateUnsignedInteger
, default
= 0x0000000000000000,
959 help = "64-bit monotonic count value in header. Default is 0x0000000000000000.")
961 parser
.add_argument ("--fw-version", dest
= 'FwVersion', type = ValidateUnsignedInteger
,
962 help = "The 32-bit version of the binary payload (e.g. 0x11223344 or 5678). Required for encode operations.")
963 parser
.add_argument ("--lsv", dest
= 'LowestSupportedVersion', type = ValidateUnsignedInteger
,
964 help = "The 32-bit lowest supported version of the binary payload (e.g. 0x11223344 or 5678). Required for encode operations.")
966 parser
.add_argument ("--pfx-file", dest
='SignToolPfxFile', type=argparse
.FileType('rb'),
967 help="signtool PFX certificate filename.")
969 parser
.add_argument ("--signer-private-cert", dest
='OpenSslSignerPrivateCertFile', type=argparse
.FileType('rb'),
970 help="OpenSSL signer private certificate filename.")
971 parser
.add_argument ("--other-public-cert", dest
='OpenSslOtherPublicCertFile', type=argparse
.FileType('rb'),
972 help="OpenSSL other public certificate filename.")
973 parser
.add_argument ("--trusted-public-cert", dest
='OpenSslTrustedPublicCertFile', type=argparse
.FileType('rb'),
974 help="OpenSSL trusted public certificate filename.")
976 parser
.add_argument ("--signing-tool-path", dest
= 'SigningToolPath',
977 help = "Path to signtool or OpenSSL tool. Optional if path to tools are already in PATH.")
979 parser
.add_argument ("--embedded-driver", dest
= 'EmbeddedDriver', type = argparse
.FileType('rb'), action
='append', default
= [],
980 help = "Path to embedded UEFI driver to add to capsule.")
983 # Add optional arguments common to all operations
985 parser
.add_argument ('--version', action
='version', version
='%(prog)s ' + __version__
)
986 parser
.add_argument ("-v", "--verbose", dest
= 'Verbose', action
= "store_true",
987 help = "Turn on verbose output with informational messages printed, including capsule headers and warning messages.")
988 parser
.add_argument ("-q", "--quiet", dest
= 'Quiet', action
= "store_true",
989 help = "Disable all messages except fatal errors.")
990 parser
.add_argument ("--debug", dest
= 'Debug', type = int, metavar
= '[0-9]', choices
= range (0, 10), default
= 0,
991 help = "Set debug level")
992 parser
.add_argument ("--update-image-index", dest
= 'UpdateImageIndex', type = ValidateUnsignedInteger
, default
= 0x01, help = "unique number identifying the firmware image within the device ")
995 # Parse command line arguments
997 args
= parser
.parse_args()
1000 # Read binary input file
1004 if os
.path
.getsize (args
.InputFile
.name
) == 0:
1005 print ('GenerateCapsule: error: InputFile {File} is empty'.format (File
= args
.InputFile
.name
))
1009 print ('Read binary input file {File}'.format (File
= args
.InputFile
.name
))
1010 Buffer
= args
.InputFile
.read ()
1011 args
.InputFile
.close ()
1013 print ('GenerateCapsule: error: can not read binary input file {File}'.format (File
= args
.InputFile
.name
))
1019 UefiCapsuleHeader
= UefiCapsuleHeaderClass ()
1020 FmpCapsuleHeader
= FmpCapsuleHeaderClass ()
1021 FmpAuthHeader
= FmpAuthHeaderClass ()
1022 FmpPayloadHeader
= FmpPayloadHeaderClass ()
1023 CapsuleDependency
= CapsuleDependencyClass ()
1025 EmbeddedDriverDescriptorList
= []
1026 PayloadDescriptorList
= []
1027 PayloadJsonDescriptorList
= []
1033 Encode (PayloadDescriptorList
, EmbeddedDriverDescriptorList
, Buffer
)
1039 Decode (PayloadDescriptorList
, PayloadJsonDescriptorList
, Buffer
)
1042 #Dump Info Operation
1045 DumpInfo (Buffer
, args
)