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 - 2022, 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) 2022, Intel Corporation. All rights reserved.'
43 __description__
= 'Generate a capsule.\n'
45 def SignPayloadSignTool (Payload
, ToolPath
, PfxFile
, SubjectName
, 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 if PfxFile
is not None:
76 Command
= Command
+ '/f {PfxFile} '.format (PfxFile
= PfxFile
)
77 if SubjectName
is not None:
78 Command
= Command
+ '/n {SubjectName} '.format (SubjectName
= SubjectName
)
79 Command
= Command
+ TempFileName
84 # Sign the input file using the specified private key
87 Process
= subprocess
.Popen (Command
, stdin
= subprocess
.PIPE
, stdout
= subprocess
.PIPE
, stderr
= subprocess
.PIPE
, shell
= True)
88 Result
= Process
.communicate('')
90 shutil
.rmtree (TempDirectoryName
)
91 raise ValueError ('GenerateCapsule: error: can not run signtool.')
93 if Process
.returncode
!= 0:
94 shutil
.rmtree (TempDirectoryName
)
95 print (Result
[1].decode())
96 raise ValueError ('GenerateCapsule: error: signtool failed.')
99 # Read the signature from the generated output file
102 with
open (TempFileName
+ '.p7', 'rb') as File
:
103 Signature
= File
.read ()
105 shutil
.rmtree (TempDirectoryName
)
106 raise ValueError ('GenerateCapsule: error: can not read signature file.')
108 shutil
.rmtree (TempDirectoryName
)
111 def VerifyPayloadSignTool (Payload
, CertData
, ToolPath
, PfxFile
, SubjectName
, Verbose
= False):
112 print ('signtool verify is not supported.')
113 raise ValueError ('GenerateCapsule: error: signtool verify is not supported.')
115 def SignPayloadOpenSsl (Payload
, ToolPath
, SignerPrivateCertFile
, OtherPublicCertFile
, TrustedPublicCertFile
, Verbose
= False):
117 # Build openssl command
122 Command
= Command
+ '"{Path}" '.format (Path
= os
.path
.join (ToolPath
, 'openssl'))
123 Command
= Command
+ 'smime -sign -binary -outform DER -md sha256 '
124 Command
= Command
+ '-signer "{Private}" -certfile "{Public}"'.format (Private
= SignerPrivateCertFile
, Public
= OtherPublicCertFile
)
129 # Sign the input file using the specified private key and capture signature from STDOUT
132 Process
= subprocess
.Popen (Command
, stdin
= subprocess
.PIPE
, stdout
= subprocess
.PIPE
, stderr
= subprocess
.PIPE
, shell
= True)
133 Result
= Process
.communicate(input = Payload
)
134 Signature
= Result
[0]
136 raise ValueError ('GenerateCapsule: error: can not run openssl.')
138 if Process
.returncode
!= 0:
139 print (Result
[1].decode())
140 raise ValueError ('GenerateCapsule: error: openssl failed.')
144 def VerifyPayloadOpenSsl (Payload
, CertData
, ToolPath
, SignerPrivateCertFile
, OtherPublicCertFile
, TrustedPublicCertFile
, Verbose
= False):
146 # Create a temporary directory
148 TempDirectoryName
= tempfile
.mkdtemp()
151 # Generate temp file name for the payload contents
153 TempFileName
= os
.path
.join (TempDirectoryName
, 'Payload.bin')
156 # Create temporary payload file for verification
159 with
open (TempFileName
, 'wb') as File
:
162 shutil
.rmtree (TempDirectoryName
)
163 raise ValueError ('GenerateCapsule: error: can not write temporary payload file.')
166 # Build openssl command
171 Command
= Command
+ '"{Path}" '.format (Path
= os
.path
.join (ToolPath
, 'openssl'))
172 Command
= Command
+ 'smime -verify -inform DER '
173 Command
= Command
+ '-content {Content} -CAfile "{Public}"'.format (Content
= TempFileName
, Public
= TrustedPublicCertFile
)
181 Process
= subprocess
.Popen (Command
, stdin
= subprocess
.PIPE
, stdout
= subprocess
.PIPE
, stderr
= subprocess
.PIPE
, shell
= True)
182 Result
= Process
.communicate(input = CertData
)
184 shutil
.rmtree (TempDirectoryName
)
185 raise ValueError ('GenerateCapsule: error: can not run openssl.')
187 if Process
.returncode
!= 0:
188 shutil
.rmtree (TempDirectoryName
)
189 print (Result
[1].decode())
190 raise ValueError ('GenerateCapsule: error: openssl failed.')
192 shutil
.rmtree (TempDirectoryName
)
195 if __name__
== '__main__':
196 def convert_arg_line_to_args(arg_line
):
197 for arg
in arg_line
.split():
202 def ValidateUnsignedInteger (Argument
):
204 Value
= int (Argument
, 0)
206 Message
= '{Argument} is not a valid integer value.'.format (Argument
= Argument
)
207 raise argparse
.ArgumentTypeError (Message
)
209 Message
= '{Argument} is a negative value.'.format (Argument
= Argument
)
210 raise argparse
.ArgumentTypeError (Message
)
213 def ValidateRegistryFormatGuid (Argument
):
215 Value
= uuid
.UUID (Argument
)
217 Message
= '{Argument} is not a valid registry format GUID value.'.format (Argument
= Argument
)
218 raise argparse
.ArgumentTypeError (Message
)
221 def ConvertJsonValue (Config
, FieldName
, Convert
, Required
= True, Default
= None, Open
= False):
222 if FieldName
not in Config
:
224 print ('GenerateCapsule: error: Payload descriptor invalid syntax. Could not find {Key} in payload descriptor.'.format(Key
= FieldName
))
228 Value
= Convert (Config
[FieldName
])
230 print ('GenerateCapsule: error: {Key} in payload descriptor has invalid syntax.'.format (Key
= FieldName
))
234 Value
= open (Value
, "rb")
236 print ('GenerateCapsule: error: can not open file {File}'.format (File
= FieldName
))
240 def DecodeJsonFileParse (Json
):
241 if 'Payloads' not in Json
:
242 print ('GenerateCapsule: error "Payloads" section not found in JSON file {File}'.format (File
= args
.JsonFile
.name
))
244 for Config
in Json
['Payloads']:
246 # Parse fields from JSON
248 PayloadFile
= ConvertJsonValue (Config
, 'Payload', os
.path
.expandvars
, Required
= False)
249 Guid
= ConvertJsonValue (Config
, 'Guid', ValidateRegistryFormatGuid
, Required
= False)
250 FwVersion
= ConvertJsonValue (Config
, 'FwVersion', ValidateUnsignedInteger
, Required
= False)
251 LowestSupportedVersion
= ConvertJsonValue (Config
, 'LowestSupportedVersion', ValidateUnsignedInteger
, Required
= False)
252 HardwareInstance
= ConvertJsonValue (Config
, 'HardwareInstance', ValidateUnsignedInteger
, Required
= False, Default
= 0)
253 MonotonicCount
= ConvertJsonValue (Config
, 'MonotonicCount', ValidateUnsignedInteger
, Required
= False, Default
= 0)
254 SignToolPfxFile
= ConvertJsonValue (Config
, 'SignToolPfxFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
255 SignToolSubjectName
= ConvertJsonValue (Config
, 'SignToolSubjectName', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
256 OpenSslSignerPrivateCertFile
= ConvertJsonValue (Config
, 'OpenSslSignerPrivateCertFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
257 OpenSslOtherPublicCertFile
= ConvertJsonValue (Config
, 'OpenSslOtherPublicCertFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
258 OpenSslTrustedPublicCertFile
= ConvertJsonValue (Config
, 'OpenSslTrustedPublicCertFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
259 SigningToolPath
= ConvertJsonValue (Config
, 'SigningToolPath', os
.path
.expandvars
, Required
= False, Default
= None)
260 UpdateImageIndex
= ConvertJsonValue (Config
, 'UpdateImageIndex', ValidateUnsignedInteger
, Required
= False, Default
= 1)
262 PayloadDescriptorList
.append (PayloadDescriptor (
266 LowestSupportedVersion
,
272 OpenSslSignerPrivateCertFile
,
273 OpenSslOtherPublicCertFile
,
274 OpenSslTrustedPublicCertFile
,
278 def EncodeJsonFileParse (Json
):
279 if 'EmbeddedDrivers' not in Json
:
280 print ('GenerateCapsule: warning "EmbeddedDrivers" section not found in JSON file {File}'.format (File
= args
.JsonFile
.name
))
282 for Config
in Json
['EmbeddedDrivers']:
283 EmbeddedDriverFile
= ConvertJsonValue(Config
, 'Driver', os
.path
.expandvars
, Open
= True)
285 #Read EmbeddedDriver file
289 print ('Read EmbeddedDriver file {File}'.format (File
= EmbeddedDriverFile
.name
))
290 Driver
= EmbeddedDriverFile
.read()
292 print ('GenerateCapsule: error: can not read EmbeddedDriver file {File}'.format (File
= EmbeddedDriverFile
.name
))
294 EmbeddedDriverDescriptorList
.append (Driver
)
296 if 'Payloads' not in Json
:
297 print ('GenerateCapsule: error: "Payloads" section not found in JSON file {File}'.format (File
= args
.JsonFile
.name
))
299 for Config
in Json
['Payloads']:
301 # Parse fields from JSON
303 PayloadFile
= ConvertJsonValue (Config
, 'Payload', os
.path
.expandvars
, Open
= True)
304 Guid
= ConvertJsonValue (Config
, 'Guid', ValidateRegistryFormatGuid
)
305 FwVersion
= ConvertJsonValue (Config
, 'FwVersion', ValidateUnsignedInteger
)
306 LowestSupportedVersion
= ConvertJsonValue (Config
, 'LowestSupportedVersion', ValidateUnsignedInteger
)
307 HardwareInstance
= ConvertJsonValue (Config
, 'HardwareInstance', ValidateUnsignedInteger
, Required
= False, Default
= 0)
308 UpdateImageIndex
= ConvertJsonValue (Config
, 'UpdateImageIndex', ValidateUnsignedInteger
, Required
= False, Default
= 1)
309 MonotonicCount
= ConvertJsonValue (Config
, 'MonotonicCount', ValidateUnsignedInteger
, Required
= False, Default
= 0)
310 SignToolPfxFile
= ConvertJsonValue (Config
, 'SignToolPfxFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
311 SignToolSubjectName
= ConvertJsonValue (Config
, 'SignToolSubjectName', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
312 OpenSslSignerPrivateCertFile
= ConvertJsonValue (Config
, 'OpenSslSignerPrivateCertFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
313 OpenSslOtherPublicCertFile
= ConvertJsonValue (Config
, 'OpenSslOtherPublicCertFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
314 OpenSslTrustedPublicCertFile
= ConvertJsonValue (Config
, 'OpenSslTrustedPublicCertFile', os
.path
.expandvars
, Required
= False, Default
= None, Open
= True)
315 SigningToolPath
= ConvertJsonValue (Config
, 'SigningToolPath', os
.path
.expandvars
, Required
= False, Default
= None)
316 DepexExp
= ConvertJsonValue (Config
, 'Dependencies', str, Required
= False, Default
= None)
319 # Read binary input file
323 print ('Read binary input file {File}'.format (File
= PayloadFile
.name
))
324 Payload
= PayloadFile
.read()
327 print ('GenerateCapsule: error: can not read binary input file {File}'.format (File
= PayloadFile
.name
))
329 PayloadDescriptorList
.append (PayloadDescriptor (
333 LowestSupportedVersion
,
339 OpenSslSignerPrivateCertFile
,
340 OpenSslOtherPublicCertFile
,
341 OpenSslTrustedPublicCertFile
,
346 def GenerateOutputJson (PayloadJsonDescriptorList
):
350 "Guid": str(PayloadDescriptor
.Guid
).upper(),
351 "FwVersion": str(PayloadDescriptor
.FwVersion
),
352 "LowestSupportedVersion": str(PayloadDescriptor
.LowestSupportedVersion
),
353 "MonotonicCount": str(PayloadDescriptor
.MonotonicCount
),
354 "Payload": PayloadDescriptor
.Payload
,
355 "HardwareInstance": str(PayloadDescriptor
.HardwareInstance
),
356 "UpdateImageIndex": str(PayloadDescriptor
.UpdateImageIndex
),
357 "SignToolPfxFile": str(PayloadDescriptor
.SignToolPfxFile
),
358 "SignToolSubjectName": str(PayloadDescriptor
.SignToolSubjectName
),
359 "OpenSslSignerPrivateCertFile": str(PayloadDescriptor
.OpenSslSignerPrivateCertFile
),
360 "OpenSslOtherPublicCertFile": str(PayloadDescriptor
.OpenSslOtherPublicCertFile
),
361 "OpenSslTrustedPublicCertFile": str(PayloadDescriptor
.OpenSslTrustedPublicCertFile
),
362 "SigningToolPath": str(PayloadDescriptor
.SigningToolPath
),
363 "Dependencies" : str(PayloadDescriptor
.DepexExp
)
364 }for PayloadDescriptor
in PayloadJsonDescriptorList
367 OutputJsonFile
= args
.OutputFile
.name
+ '.json'
368 if 'Payloads' in PayloadJson
:
369 PayloadSection
= PayloadJson
['Payloads']
371 for PayloadField
in PayloadSection
:
372 if PayloadJsonDescriptorList
[Index
].SignToolPfxFile
is None:
373 del PayloadField
['SignToolPfxFile']
374 if PayloadJsonDescriptorList
[Index
].SignToolSubjectName
is None:
375 del PayloadField
['SignToolSubjectName']
376 if PayloadJsonDescriptorList
[Index
].OpenSslSignerPrivateCertFile
is None:
377 del PayloadField
['OpenSslSignerPrivateCertFile']
378 if PayloadJsonDescriptorList
[Index
].OpenSslOtherPublicCertFile
is None:
379 del PayloadField
['OpenSslOtherPublicCertFile']
380 if PayloadJsonDescriptorList
[Index
].OpenSslTrustedPublicCertFile
is None:
381 del PayloadField
['OpenSslTrustedPublicCertFile']
382 if PayloadJsonDescriptorList
[Index
].SigningToolPath
is None:
383 del PayloadField
['SigningToolPath']
385 Result
= json
.dumps (PayloadJson
, indent
=4, sort_keys
=True, separators
=(',', ': '))
386 with
open (OutputJsonFile
, 'w') as OutputFile
:
387 OutputFile
.write (Result
)
389 def CheckArgumentConflict (args
):
392 print ('GenerateCapsule: error: Argument InputFile conflicts with Argument -j')
394 if args
.EmbeddedDriver
:
395 print ('GenerateCapsule: error: Argument --embedded-driver conflicts with Argument -j')
398 print ('GenerateCapsule: error: Argument --guid conflicts with Argument -j')
401 print ('GenerateCapsule: error: Argument --fw-version conflicts with Argument -j')
403 if args
.LowestSupportedVersion
:
404 print ('GenerateCapsule: error: Argument --lsv conflicts with Argument -j')
406 if args
.MonotonicCount
:
407 print ('GenerateCapsule: error: Argument --monotonic-count conflicts with Argument -j')
409 if args
.HardwareInstance
:
410 print ('GenerateCapsule: error: Argument --hardware-instance conflicts with Argument -j')
412 if args
.SignToolPfxFile
:
413 print ('GenerateCapsule: error: Argument --pfx-file conflicts with Argument -j')
415 if args
.SignToolSubjectName
:
416 print ('GenerateCapsule: error: Argument --SubjectName conflicts with Argument -j')
418 if args
.OpenSslSignerPrivateCertFile
:
419 print ('GenerateCapsule: error: Argument --signer-private-cert conflicts with Argument -j')
421 if args
.OpenSslOtherPublicCertFile
:
422 print ('GenerateCapsule: error: Argument --other-public-cert conflicts with Argument -j')
424 if args
.OpenSslTrustedPublicCertFile
:
425 print ('GenerateCapsule: error: Argument --trusted-public-cert conflicts with Argument -j')
427 if args
.SigningToolPath
:
428 print ('GenerateCapsule: error: Argument --signing-tool-path conflicts with Argument -j')
431 class PayloadDescriptor (object):
436 LowestSupportedVersion
,
438 HardwareInstance
= 0,
439 UpdateImageIndex
= 1,
440 SignToolPfxFile
= None,
441 SignToolSubjectName
= None,
442 OpenSslSignerPrivateCertFile
= None,
443 OpenSslOtherPublicCertFile
= None,
444 OpenSslTrustedPublicCertFile
= None,
445 SigningToolPath
= None,
448 self
.Payload
= Payload
450 self
.FwVersion
= FwVersion
451 self
.LowestSupportedVersion
= LowestSupportedVersion
452 self
.MonotonicCount
= MonotonicCount
453 self
.HardwareInstance
= HardwareInstance
454 self
.UpdateImageIndex
= UpdateImageIndex
455 self
.SignToolPfxFile
= SignToolPfxFile
456 self
.SignToolSubjectName
= SignToolSubjectName
457 self
.OpenSslSignerPrivateCertFile
= OpenSslSignerPrivateCertFile
458 self
.OpenSslOtherPublicCertFile
= OpenSslOtherPublicCertFile
459 self
.OpenSslTrustedPublicCertFile
= OpenSslTrustedPublicCertFile
460 self
.SigningToolPath
= SigningToolPath
461 self
.DepexExp
= DepexExp
463 self
.UseSignTool
= (self
.SignToolPfxFile
is not None or
464 self
.SignToolSubjectName
is not None)
465 self
.UseOpenSsl
= (self
.OpenSslSignerPrivateCertFile
is not None and
466 self
.OpenSslOtherPublicCertFile
is not None and
467 self
.OpenSslTrustedPublicCertFile
is not None)
468 self
.AnyOpenSsl
= (self
.OpenSslSignerPrivateCertFile
is not None or
469 self
.OpenSslOtherPublicCertFile
is not None or
470 self
.OpenSslTrustedPublicCertFile
is not None)
471 self
.UseDependency
= self
.DepexExp
is not None
473 def Validate(self
, args
):
474 if self
.UseSignTool
and self
.AnyOpenSsl
:
475 raise argparse
.ArgumentTypeError ('Providing both signtool and OpenSSL options is not supported')
476 if not self
.UseSignTool
and not self
.UseOpenSsl
and self
.AnyOpenSsl
:
478 raise argparse
.ArgumentTypeError ('the following JSON fields are required for OpenSSL: OpenSslSignerPrivateCertFile, OpenSslOtherPublicCertFile, OpenSslTrustedPublicCertFile')
480 raise argparse
.ArgumentTypeError ('the following options are required for OpenSSL: --signer-private-cert, --other-public-cert, --trusted-public-cert')
481 if self
.UseSignTool
and platform
.system() != 'Windows':
482 raise argparse
.ArgumentTypeError ('Use of signtool is not supported on this operating system.')
484 if self
.FwVersion
is None or self
.LowestSupportedVersion
is None:
486 raise argparse
.ArgumentTypeError ('the following JSON fields are required: FwVersion, LowestSupportedVersion')
488 raise argparse
.ArgumentTypeError ('the following options are required: --fw-version, --lsv')
489 if self
.FwVersion
> 0xFFFFFFFF:
491 raise argparse
.ArgumentTypeError ('JSON field FwVersion must be an integer in range 0x0..0xffffffff')
493 raise argparse
.ArgumentTypeError ('--fw-version must be an integer in range 0x0..0xffffffff')
494 if self
.LowestSupportedVersion
> 0xFFFFFFFF:
496 raise argparse
.ArgumentTypeError ('JSON field LowestSupportedVersion must be an integer in range 0x0..0xffffffff')
498 raise argparse
.ArgumentTypeError ('--lsv must be an integer in range 0x0..0xffffffff')
501 if self
.Guid
is None:
503 raise argparse
.ArgumentTypeError ('the following JSON field is required: Guid')
505 raise argparse
.ArgumentTypeError ('the following option is required: --guid')
506 if self
.HardwareInstance
> 0xFFFFFFFFFFFFFFFF:
508 raise argparse
.ArgumentTypeError ('JSON field HardwareInstance must be an integer in range 0x0..0xffffffffffffffff')
510 raise argparse
.ArgumentTypeError ('--hardware-instance must be an integer in range 0x0..0xffffffffffffffff')
511 if self
.MonotonicCount
> 0xFFFFFFFFFFFFFFFF:
513 raise argparse
.ArgumentTypeError ('JSON field MonotonicCount must be an integer in range 0x0..0xffffffffffffffff')
515 raise argparse
.ArgumentTypeError ('--monotonic-count must be an integer in range 0x0..0xffffffffffffffff')
516 if self
.UpdateImageIndex
>0xFF:
518 raise argparse
.ArgumentTypeError ('JSON field UpdateImageIndex must be an integer in range 0x0..0xff')
520 raise argparse
.ArgumentTypeError ('--update-image-index must be an integer in range 0x0..0xff')
523 if self
.SignToolPfxFile
is not None:
524 self
.SignToolPfxFile
.close()
525 self
.SignToolPfxFile
= self
.SignToolPfxFile
.name
527 self
.OpenSslSignerPrivateCertFile
.close()
528 self
.OpenSslOtherPublicCertFile
.close()
529 self
.OpenSslTrustedPublicCertFile
.close()
530 self
.OpenSslSignerPrivateCertFile
= self
.OpenSslSignerPrivateCertFile
.name
531 self
.OpenSslOtherPublicCertFile
= self
.OpenSslOtherPublicCertFile
.name
532 self
.OpenSslTrustedPublicCertFile
= self
.OpenSslTrustedPublicCertFile
.name
535 # Perform additional argument verification
538 if 'PersistAcrossReset' not in args
.CapsuleFlag
:
539 if 'InitiateReset' in args
.CapsuleFlag
:
540 raise argparse
.ArgumentTypeError ('--capflag InitiateReset also requires --capflag PersistAcrossReset')
541 if args
.CapsuleOemFlag
> 0xFFFF:
542 raise argparse
.ArgumentTypeError ('--capoemflag must be an integer between 0x0000 and 0xffff')
547 def Encode (PayloadDescriptorList
, EmbeddedDriverDescriptorList
, Buffer
):
549 CheckArgumentConflict(args
)
551 Json
= json
.loads (args
.JsonFile
.read ())
553 print ('GenerateCapsule: error: {JSONFile} loads failure. '.format (JSONFile
= args
.JsonFile
))
555 EncodeJsonFileParse(Json
)
557 for Driver
in args
.EmbeddedDriver
:
558 EmbeddedDriverDescriptorList
.append (Driver
.read())
559 PayloadDescriptorList
.append (PayloadDescriptor (
563 args
.LowestSupportedVersion
,
565 args
.HardwareInstance
,
566 args
.UpdateImageIndex
,
567 args
.SignToolPfxFile
,
568 args
.SignToolSubjectName
,
569 args
.OpenSslSignerPrivateCertFile
,
570 args
.OpenSslOtherPublicCertFile
,
571 args
.OpenSslTrustedPublicCertFile
,
572 args
.SigningToolPath
,
575 for SinglePayloadDescriptor
in PayloadDescriptorList
:
577 SinglePayloadDescriptor
.Validate (args
)
578 except Exception as Msg
:
579 print ('GenerateCapsule: error:' + str(Msg
))
581 for SinglePayloadDescriptor
in PayloadDescriptorList
:
582 ImageCapsuleSupport
= 0x0000000000000000
583 Result
= SinglePayloadDescriptor
.Payload
585 FmpPayloadHeader
.FwVersion
= SinglePayloadDescriptor
.FwVersion
586 FmpPayloadHeader
.LowestSupportedVersion
= SinglePayloadDescriptor
.LowestSupportedVersion
587 FmpPayloadHeader
.Payload
= SinglePayloadDescriptor
.Payload
588 Result
= FmpPayloadHeader
.Encode ()
590 FmpPayloadHeader
.DumpInfo ()
592 print ('GenerateCapsule: error: can not encode FMP Payload Header')
594 if SinglePayloadDescriptor
.UseDependency
:
595 CapsuleDependency
.Payload
= Result
596 CapsuleDependency
.DepexExp
= SinglePayloadDescriptor
.DepexExp
597 ImageCapsuleSupport |
= FmpCapsuleHeader
.CAPSULE_SUPPORT_DEPENDENCY
598 Result
= CapsuleDependency
.Encode ()
600 CapsuleDependency
.DumpInfo ()
601 if SinglePayloadDescriptor
.UseOpenSsl
or SinglePayloadDescriptor
.UseSignTool
:
603 # Sign image with 64-bit MonotonicCount appended to end of image
606 if SinglePayloadDescriptor
.UseSignTool
:
607 CertData
= SignPayloadSignTool (
608 Result
+ struct
.pack ('<Q', SinglePayloadDescriptor
.MonotonicCount
),
609 SinglePayloadDescriptor
.SigningToolPath
,
610 SinglePayloadDescriptor
.SignToolPfxFile
,
611 SinglePayloadDescriptor
.SignToolSubjectName
,
612 Verbose
= args
.Verbose
615 CertData
= SignPayloadOpenSsl (
616 Result
+ struct
.pack ('<Q', SinglePayloadDescriptor
.MonotonicCount
),
617 SinglePayloadDescriptor
.SigningToolPath
,
618 SinglePayloadDescriptor
.OpenSslSignerPrivateCertFile
,
619 SinglePayloadDescriptor
.OpenSslOtherPublicCertFile
,
620 SinglePayloadDescriptor
.OpenSslTrustedPublicCertFile
,
621 Verbose
= args
.Verbose
623 except Exception as Msg
:
624 print ('GenerateCapsule: error: can not sign payload \n' + str(Msg
))
628 FmpAuthHeader
.MonotonicCount
= SinglePayloadDescriptor
.MonotonicCount
629 FmpAuthHeader
.CertData
= CertData
630 FmpAuthHeader
.Payload
= Result
631 ImageCapsuleSupport |
= FmpCapsuleHeader
.CAPSULE_SUPPORT_AUTHENTICATION
632 Result
= FmpAuthHeader
.Encode ()
634 FmpAuthHeader
.DumpInfo ()
636 print ('GenerateCapsule: error: can not encode FMP Auth Header')
638 FmpCapsuleHeader
.AddPayload (SinglePayloadDescriptor
.Guid
, Result
, HardwareInstance
= SinglePayloadDescriptor
.HardwareInstance
, UpdateImageIndex
= SinglePayloadDescriptor
.UpdateImageIndex
, CapsuleSupport
= ImageCapsuleSupport
)
640 for EmbeddedDriver
in EmbeddedDriverDescriptorList
:
641 FmpCapsuleHeader
.AddEmbeddedDriver(EmbeddedDriver
)
643 Result
= FmpCapsuleHeader
.Encode ()
645 FmpCapsuleHeader
.DumpInfo ()
647 print ('GenerateCapsule: error: can not encode FMP Capsule Header')
651 UefiCapsuleHeader
.OemFlags
= args
.CapsuleOemFlag
652 UefiCapsuleHeader
.PersistAcrossReset
= 'PersistAcrossReset' in args
.CapsuleFlag
653 UefiCapsuleHeader
.PopulateSystemTable
= False
654 UefiCapsuleHeader
.InitiateReset
= 'InitiateReset' in args
.CapsuleFlag
655 UefiCapsuleHeader
.Payload
= Result
656 Result
= UefiCapsuleHeader
.Encode ()
658 UefiCapsuleHeader
.DumpInfo ()
660 print ('GenerateCapsule: error: can not encode UEFI Capsule Header')
664 print ('Write binary output file {File}'.format (File
= args
.OutputFile
.name
))
665 args
.OutputFile
.write (Result
)
666 args
.OutputFile
.close ()
668 print ('GenerateCapsule: error: can not write binary output file {File}'.format (File
= args
.OutputFile
.name
))
671 def Decode (PayloadDescriptorList
, PayloadJsonDescriptorList
, Buffer
):
673 CheckArgumentConflict(args
)
675 # Parse payload descriptors from JSON
678 Json
= json
.loads (args
.JsonFile
.read())
680 print ('GenerateCapsule: error: {JSONFile} loads failure. '.format (JSONFile
= args
.JsonFile
))
682 DecodeJsonFileParse (Json
)
684 PayloadDescriptorList
.append (PayloadDescriptor (
688 args
.LowestSupportedVersion
,
690 args
.HardwareInstance
,
691 args
.UpdateImageIndex
,
692 args
.SignToolPfxFile
,
693 args
.SignSubjectName
,
694 args
.OpenSslSignerPrivateCertFile
,
695 args
.OpenSslOtherPublicCertFile
,
696 args
.OpenSslTrustedPublicCertFile
,
697 args
.SigningToolPath
,
701 # Perform additional verification on payload descriptors
703 for SinglePayloadDescriptor
in PayloadDescriptorList
:
705 SinglePayloadDescriptor
.Validate (args
)
706 except Exception as Msg
:
707 print ('GenerateCapsule: error:' + str(Msg
))
710 Result
= UefiCapsuleHeader
.Decode (Buffer
)
712 Result
= FmpCapsuleHeader
.Decode (Result
)
714 if FmpCapsuleHeader
.PayloadItemCount
!= len (PayloadDescriptorList
):
715 CapsulePayloadNum
= FmpCapsuleHeader
.PayloadItemCount
716 JsonPayloadNum
= len (PayloadDescriptorList
)
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
))
719 for Index
in range (0, FmpCapsuleHeader
.PayloadItemCount
):
720 if Index
< len (PayloadDescriptorList
):
721 GUID
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).UpdateImageTypeId
722 HardwareInstance
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).UpdateHardwareInstance
723 UpdateImageIndex
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).UpdateImageIndex
724 if PayloadDescriptorList
[Index
].Guid
!= GUID
or PayloadDescriptorList
[Index
].HardwareInstance
!= HardwareInstance
:
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
))
727 PayloadDescriptorList
[Index
].Payload
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).Payload
728 DecodeJsonOutput
= args
.OutputFile
.name
+ '.Payload.{Index:d}.bin'.format (Index
= Index
+ 1)
729 PayloadJsonDescriptorList
.append (PayloadDescriptor (
737 PayloadDescriptorList
[Index
].SignToolPfxFile
,
738 PayloadDescriptorList
[Index
].SignToolSubjectName
,
739 PayloadDescriptorList
[Index
].OpenSslSignerPrivateCertFile
,
740 PayloadDescriptorList
[Index
].OpenSslOtherPublicCertFile
,
741 PayloadDescriptorList
[Index
].OpenSslTrustedPublicCertFile
,
742 PayloadDescriptorList
[Index
].SigningToolPath
,
746 PayloadDescriptorList
[0].Payload
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (0).Payload
747 for Index
in range (0, FmpCapsuleHeader
.PayloadItemCount
):
749 PayloadDecodeFile
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).Payload
750 PayloadDescriptorList
.append (PayloadDescriptor (PayloadDecodeFile
,
764 GUID
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).UpdateImageTypeId
765 HardwareInstance
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).UpdateHardwareInstance
766 UpdateImageIndex
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).UpdateImageIndex
767 DecodeJsonOutput
= args
.OutputFile
.name
+ '.Payload.{Index:d}.bin'.format (Index
= Index
+ 1)
768 PayloadJsonDescriptorList
.append (PayloadDescriptor (
776 PayloadDescriptorList
[Index
].SignToolPfxFile
,
777 PayloadDescriptorList
[Index
].SignToolSubjectName
,
778 PayloadDescriptorList
[Index
].OpenSslSignerPrivateCertFile
,
779 PayloadDescriptorList
[Index
].OpenSslOtherPublicCertFile
,
780 PayloadDescriptorList
[Index
].OpenSslTrustedPublicCertFile
,
781 PayloadDescriptorList
[Index
].SigningToolPath
,
785 for SinglePayloadDescriptor
in PayloadDescriptorList
:
788 UefiCapsuleHeader
.DumpInfo ()
790 FmpCapsuleHeader
.DumpInfo ()
791 if FmpAuthHeader
.IsSigned(SinglePayloadDescriptor
.Payload
):
792 if not SinglePayloadDescriptor
.UseOpenSsl
and not SinglePayloadDescriptor
.UseSignTool
:
793 print ('GenerateCapsule: decode warning: can not verify singed payload without cert or pfx file. Index = {Index}'.format (Index
= JsonIndex
+ 1))
794 SinglePayloadDescriptor
.Payload
= FmpAuthHeader
.Decode (SinglePayloadDescriptor
.Payload
)
795 PayloadJsonDescriptorList
[JsonIndex
].MonotonicCount
= FmpAuthHeader
.MonotonicCount
798 FmpAuthHeader
.DumpInfo ()
801 # Verify Image with 64-bit MonotonicCount appended to end of image
804 if SinglePayloadDescriptor
.UseSignTool
:
805 CertData
= VerifyPayloadSignTool (
806 FmpAuthHeader
.Payload
+ struct
.pack ('<Q', FmpAuthHeader
.MonotonicCount
),
807 FmpAuthHeader
.CertData
,
808 SinglePayloadDescriptor
.SigningToolPath
,
809 SinglePayloadDescriptor
.SignToolPfxFile
,
810 SinglePayloadDescriptor
.SignToolSubjectName
,
811 Verbose
= args
.Verbose
814 CertData
= VerifyPayloadOpenSsl (
815 FmpAuthHeader
.Payload
+ struct
.pack ('<Q', FmpAuthHeader
.MonotonicCount
),
816 FmpAuthHeader
.CertData
,
817 SinglePayloadDescriptor
.SigningToolPath
,
818 SinglePayloadDescriptor
.OpenSslSignerPrivateCertFile
,
819 SinglePayloadDescriptor
.OpenSslOtherPublicCertFile
,
820 SinglePayloadDescriptor
.OpenSslTrustedPublicCertFile
,
821 Verbose
= args
.Verbose
823 except Exception as Msg
:
824 print ('GenerateCapsule: warning: payload verification failed Index = {Index} \n'.format (Index
= JsonIndex
+ 1) + str(Msg
))
828 print ('No EFI_FIRMWARE_IMAGE_AUTHENTICATION')
830 PayloadSignature
= struct
.unpack ('<I', SinglePayloadDescriptor
.Payload
[0:4])
831 if PayloadSignature
!= FmpPayloadHeader
.Signature
:
832 SinglePayloadDescriptor
.UseDependency
= True
834 SinglePayloadDescriptor
.Payload
= CapsuleDependency
.Decode (SinglePayloadDescriptor
.Payload
)
835 PayloadJsonDescriptorList
[JsonIndex
].DepexExp
= CapsuleDependency
.DepexExp
838 CapsuleDependency
.DumpInfo ()
839 except Exception as Msg
:
840 print ('GenerateCapsule: error: invalid dependency expression')
844 print ('No EFI_FIRMWARE_IMAGE_DEP')
847 SinglePayloadDescriptor
.Payload
= FmpPayloadHeader
.Decode (SinglePayloadDescriptor
.Payload
)
848 PayloadJsonDescriptorList
[JsonIndex
].FwVersion
= FmpPayloadHeader
.FwVersion
849 PayloadJsonDescriptorList
[JsonIndex
].LowestSupportedVersion
= FmpPayloadHeader
.LowestSupportedVersion
850 JsonIndex
= JsonIndex
+ 1
853 FmpPayloadHeader
.DumpInfo ()
858 print ('No FMP_PAYLOAD_HEADER')
862 # Write embedded driver file(s)
864 for Index
in range (0, FmpCapsuleHeader
.EmbeddedDriverCount
):
865 EmbeddedDriverBuffer
= FmpCapsuleHeader
.GetEmbeddedDriver (Index
)
866 EmbeddedDriverPath
= args
.OutputFile
.name
+ '.EmbeddedDriver.{Index:d}.efi'.format (Index
= Index
+ 1)
869 print ('Write embedded driver file {File}'.format (File
= EmbeddedDriverPath
))
870 with
open (EmbeddedDriverPath
, 'wb') as EmbeddedDriverFile
:
871 EmbeddedDriverFile
.write (EmbeddedDriverBuffer
)
873 print ('GenerateCapsule: error: can not write embedded driver file {File}'.format (File
= EmbeddedDriverPath
))
877 print ('GenerateCapsule: error: can not decode capsule')
879 GenerateOutputJson(PayloadJsonDescriptorList
)
881 for SinglePayloadDescriptor
in PayloadDescriptorList
:
882 if args
.OutputFile
is None:
883 print ('GenerateCapsule: Decode error: OutputFile is needed for decode output')
887 print ('Write binary output file {File}'.format (File
= args
.OutputFile
.name
))
888 PayloadDecodePath
= args
.OutputFile
.name
+ '.Payload.{Index:d}.bin'.format (Index
= PayloadIndex
+ 1)
889 with
open (PayloadDecodePath
, 'wb') as PayloadDecodeFile
:
890 PayloadDecodeFile
.write (SinglePayloadDescriptor
.Payload
)
891 PayloadIndex
= PayloadIndex
+ 1
893 print ('GenerateCapsule: error: can not write binary output file {File}'.format (File
= SinglePayloadDescriptor
.OutputFile
.name
))
896 def DumpInfo (Buffer
, args
):
897 if args
.OutputFile
is not None:
898 raise argparse
.ArgumentTypeError ('the following option is not supported for dumpinfo operations: --output')
900 Result
= UefiCapsuleHeader
.Decode (Buffer
)
902 UefiCapsuleHeader
.DumpInfo ()
904 FmpCapsuleHeader
.Decode (Result
)
906 FmpCapsuleHeader
.DumpInfo ()
907 for Index
in range (0, FmpCapsuleHeader
.PayloadItemCount
):
908 Result
= FmpCapsuleHeader
.GetFmpCapsuleImageHeader (Index
).Payload
910 Result
= FmpAuthHeader
.Decode (Result
)
912 FmpAuthHeader
.DumpInfo ()
915 print ('No EFI_FIRMWARE_IMAGE_AUTHENTICATION')
917 PayloadSignature
= struct
.unpack ('<I', Result
[0:4])
918 if PayloadSignature
!= FmpPayloadHeader
.Signature
:
920 Result
= CapsuleDependency
.Decode (Result
)
922 CapsuleDependency
.DumpInfo ()
924 print ('GenerateCapsule: error: invalid dependency expression')
927 print ('No EFI_FIRMWARE_IMAGE_DEP')
929 Result
= FmpPayloadHeader
.Decode (Result
)
931 FmpPayloadHeader
.DumpInfo ()
934 print ('No FMP_PAYLOAD_HEADER')
937 print ('GenerateCapsule: error: can not decode capsule')
940 # Create command line argument parser object
942 parser
= argparse
.ArgumentParser (
944 description
= __description__
+ __copyright__
,
945 conflict_handler
= 'resolve',
946 fromfile_prefix_chars
= '@'
948 parser
.convert_arg_line_to_args
= convert_arg_line_to_args
951 # Add input and output file arguments
953 parser
.add_argument("InputFile", type = argparse
.FileType('rb'), nargs
='?',
954 help = "Input binary payload filename.")
955 parser
.add_argument("-o", "--output", dest
= 'OutputFile', type = argparse
.FileType('wb'),
956 help = "Output filename.")
958 # Add group for -e and -d flags that are mutually exclusive and required
960 group
= parser
.add_mutually_exclusive_group (required
= True)
961 group
.add_argument ("-e", "--encode", dest
= 'Encode', action
= "store_true",
962 help = "Encode file")
963 group
.add_argument ("-d", "--decode", dest
= 'Decode', action
= "store_true",
964 help = "Decode file")
965 group
.add_argument ("--dump-info", dest
= 'DumpInfo', action
= "store_true",
966 help = "Display FMP Payload Header information")
968 # Add optional arguments for this command
970 parser
.add_argument ("-j", "--json-file", dest
= 'JsonFile', type=argparse
.FileType('r'),
971 help = "JSON configuration file for multiple payloads and embedded drivers.")
972 parser
.add_argument ("--capflag", dest
= 'CapsuleFlag', action
='append', default
= [],
973 choices
=['PersistAcrossReset', 'InitiateReset'],
974 help = "Capsule flag can be PersistAcrossReset or InitiateReset or not set")
975 parser
.add_argument ("--capoemflag", dest
= 'CapsuleOemFlag', type = ValidateUnsignedInteger
, default
= 0x0000,
976 help = "Capsule OEM Flag is an integer between 0x0000 and 0xffff.")
978 parser
.add_argument ("--guid", dest
= 'Guid', type = ValidateRegistryFormatGuid
,
979 help = "The FMP/ESRT GUID in registry format. Required for single payload encode operations.")
980 parser
.add_argument ("--hardware-instance", dest
= 'HardwareInstance', type = ValidateUnsignedInteger
, default
= 0x0000000000000000,
981 help = "The 64-bit hardware instance. The default is 0x0000000000000000")
984 parser
.add_argument ("--monotonic-count", dest
= 'MonotonicCount', type = ValidateUnsignedInteger
, default
= 0x0000000000000000,
985 help = "64-bit monotonic count value in header. Default is 0x0000000000000000.")
987 parser
.add_argument ("--fw-version", dest
= 'FwVersion', type = ValidateUnsignedInteger
,
988 help = "The 32-bit version of the binary payload (e.g. 0x11223344 or 5678). Required for encode operations.")
989 parser
.add_argument ("--lsv", dest
= 'LowestSupportedVersion', type = ValidateUnsignedInteger
,
990 help = "The 32-bit lowest supported version of the binary payload (e.g. 0x11223344 or 5678). Required for encode operations.")
992 parser
.add_argument ("--pfx-file", dest
='SignToolPfxFile', type=argparse
.FileType('rb'),
993 help="signtool PFX certificate filename.")
994 parser
.add_argument ("--subject-name", dest
='SignToolSubjectName',
995 help="signtool certificate subject name.")
997 parser
.add_argument ("--signer-private-cert", dest
='OpenSslSignerPrivateCertFile', type=argparse
.FileType('rb'),
998 help="OpenSSL signer private certificate filename.")
999 parser
.add_argument ("--other-public-cert", dest
='OpenSslOtherPublicCertFile', type=argparse
.FileType('rb'),
1000 help="OpenSSL other public certificate filename.")
1001 parser
.add_argument ("--trusted-public-cert", dest
='OpenSslTrustedPublicCertFile', type=argparse
.FileType('rb'),
1002 help="OpenSSL trusted public certificate filename.")
1004 parser
.add_argument ("--signing-tool-path", dest
= 'SigningToolPath',
1005 help = "Path to signtool or OpenSSL tool. Optional if path to tools are already in PATH.")
1007 parser
.add_argument ("--embedded-driver", dest
= 'EmbeddedDriver', type = argparse
.FileType('rb'), action
='append', default
= [],
1008 help = "Path to embedded UEFI driver to add to capsule.")
1011 # Add optional arguments common to all operations
1013 parser
.add_argument ('--version', action
='version', version
='%(prog)s ' + __version__
)
1014 parser
.add_argument ("-v", "--verbose", dest
= 'Verbose', action
= "store_true",
1015 help = "Turn on verbose output with informational messages printed, including capsule headers and warning messages.")
1016 parser
.add_argument ("-q", "--quiet", dest
= 'Quiet', action
= "store_true",
1017 help = "Disable all messages except fatal errors.")
1018 parser
.add_argument ("--debug", dest
= 'Debug', type = int, metavar
= '[0-9]', choices
= range (0, 10), default
= 0,
1019 help = "Set debug level")
1020 parser
.add_argument ("--update-image-index", dest
= 'UpdateImageIndex', type = ValidateUnsignedInteger
, default
= 0x01, help = "unique number identifying the firmware image within the device ")
1023 # Parse command line arguments
1025 args
= parser
.parse_args()
1028 # Read binary input file
1032 if os
.path
.getsize (args
.InputFile
.name
) == 0:
1033 print ('GenerateCapsule: error: InputFile {File} is empty'.format (File
= args
.InputFile
.name
))
1037 print ('Read binary input file {File}'.format (File
= args
.InputFile
.name
))
1038 Buffer
= args
.InputFile
.read ()
1039 args
.InputFile
.close ()
1041 print ('GenerateCapsule: error: can not read binary input file {File}'.format (File
= args
.InputFile
.name
))
1047 UefiCapsuleHeader
= UefiCapsuleHeaderClass ()
1048 FmpCapsuleHeader
= FmpCapsuleHeaderClass ()
1049 FmpAuthHeader
= FmpAuthHeaderClass ()
1050 FmpPayloadHeader
= FmpPayloadHeaderClass ()
1051 CapsuleDependency
= CapsuleDependencyClass ()
1053 EmbeddedDriverDescriptorList
= []
1054 PayloadDescriptorList
= []
1055 PayloadJsonDescriptorList
= []
1061 Encode (PayloadDescriptorList
, EmbeddedDriverDescriptorList
, Buffer
)
1067 Decode (PayloadDescriptorList
, PayloadJsonDescriptorList
, Buffer
)
1070 #Dump Info Operation
1073 DumpInfo (Buffer
, args
)