2018-05-01 20:54:46 -07:00
## @file
# Generate a capsule.
#
2019-06-26 13:55:35 +08:00
# This tool generates a UEFI Capsule around an FMP Capsule. The capsule payload
2018-07-28 00:32:34 -07:00
# be signed using signtool or OpenSSL and if it is signed the signed content
# includes an FMP Payload Header.
#
# This tool is intended to be used to generate UEFI Capsules to update the
2019-06-26 13:55:35 +08:00
# system firmware or device firmware for integrated devices. In order to
2018-07-28 00:32:34 -07:00
# keep the tool as simple as possible, it has the following limitations:
# * Do not support vendor code bytes in a capsule.
#
2024-07-31 17:05:48 +08:00
# Copyright (c) 2018 - 2024, Intel Corporation. All rights reserved.<BR>
2019-04-03 16:03:11 -07:00
# SPDX-License-Identifier: BSD-2-Clause-Patent
2018-05-01 20:54:46 -07:00
#
'''
GenerateCapsule
'''
import sys
import argparse
import uuid
import struct
import subprocess
import os
import tempfile
import shutil
import platform
2019-06-26 13:55:35 +08:00
import json
2018-05-01 20:54:46 -07:00
from Common.Uefi.Capsule.UefiCapsuleHeader import UefiCapsuleHeaderClass
from Common.Uefi.Capsule.FmpCapsuleHeader import FmpCapsuleHeaderClass
from Common.Uefi.Capsule.FmpAuthHeader import FmpAuthHeaderClass
2020-01-10 09:57:35 +08:00
from Common.Uefi.Capsule.CapsuleDependency import CapsuleDependencyClass
2018-05-01 20:54:46 -07:00
from Common.Edk2.Capsule.FmpPayloadHeader import FmpPayloadHeaderClass
#
# Globals for help information
#
__prog__ = 'GenerateCapsule'
2024-07-31 17:05:48 +08:00
__version__ = '0.11'
__copyright__ = 'Copyright (c) 2024, Intel Corporation. All rights reserved.'
2018-05-01 20:54:46 -07:00
__description__ = 'Generate a capsule. \n '
2024-07-31 17:05:48 +08:00
#
# Globals definitions
#
HASH_ALG_MD5 = 'md5'
HASH_ALG_SHA1 = 'sha1'
HASH_ALG_SHA256 = 'sha256'
HASH_ALG_SHA384 = 'sha384'
HASH_ALG_SHA512 = 'sha512'
DEFAULT_HASH_ALGORITHM = HASH_ALG_SHA256
TOOL_SIGN_TOOL = 0x0
TOOL_OPENSSL = 0x1
SIGN_TOOL_HASH_ALG_EOL_LIST = [
HASH_ALG_MD5 ,
HASH_ALG_SHA1 ,
]
SIGN_TOOL_HASH_ALG_SUPPORT_LIST = [
HASH_ALG_SHA256 ,
HASH_ALG_SHA384 ,
HASH_ALG_SHA512 ,
]
OPENSSL_HASH_ALG_EOL_LIST = [
HASH_ALG_MD5 ,
HASH_ALG_SHA1 ,
]
OPENSSL_HASH_ALG_SUPPORT_LIST = [
HASH_ALG_SHA256 ,
HASH_ALG_SHA384 ,
HASH_ALG_SHA512 ,
]
def CheckHashAlgorithmSupported ( ToolType , HashAlgorithm ):
if ToolType == TOOL_SIGN_TOOL :
EolList = SIGN_TOOL_HASH_ALG_EOL_LIST
SupportList = SIGN_TOOL_HASH_ALG_SUPPORT_LIST
elif ToolType == TOOL_OPENSSL :
EolList = OPENSSL_HASH_ALG_EOL_LIST
SupportList = OPENSSL_HASH_ALG_SUPPORT_LIST
else :
raise ValueError ( 'GenerateCapsule: error: unsupported type of tool.' )
if HashAlgorithm . lower () in EolList :
raise ValueError ( 'GenerateCapsule: error: hash algorithm [ {HashAlgorithm} ] had been EOL.' . format ( HashAlgorithm = HashAlgorithm ))
elif HashAlgorithm . lower () not in SupportList :
raise ValueError ( 'GenerateCapsule: error: hash algorithm [ {HashAlgorithm} ] is not supported.' . format ( HashAlgorithm = HashAlgorithm ))
return
def SignPayloadSignTool ( Payload , ToolPath , PfxFile , SubjectName , HashAlgorithm = DEFAULT_HASH_ALGORITHM , Verbose = False ):
#
# Check the hash algorithm is supported
#
CheckHashAlgorithmSupported ( TOOL_SIGN_TOOL , HashAlgorithm )
2018-05-01 20:54:46 -07:00
#
# Create a temporary directory
#
TempDirectoryName = tempfile . mkdtemp ()
#
# Generate temp file name for the payload contents
#
TempFileName = os . path . join ( TempDirectoryName , 'Payload.bin' )
#
# Create temporary payload file for signing
#
try :
2019-06-26 13:55:35 +08:00
with open ( TempFileName , 'wb' ) as File :
File . write ( Payload )
2018-05-01 20:54:46 -07:00
except :
shutil . rmtree ( TempDirectoryName )
raise ValueError ( 'GenerateCapsule: error: can not write temporary payload file.' )
#
# Build signtool command
#
if ToolPath is None :
ToolPath = ''
Command = ''
Command = Command + '" {Path} " ' . format ( Path = os . path . join ( ToolPath , 'signtool.exe' ))
2024-07-31 17:05:48 +08:00
Command = Command + 'sign /fd {HashAlgorithm} /p7ce DetachedSignedData /p7co 1.2.840.113549.1.7.2 ' . format ( HashAlgorithm = HashAlgorithm )
2018-05-01 20:54:46 -07:00
Command = Command + '/p7 {TempDir} ' . format ( TempDir = TempDirectoryName )
2022-07-25 23:31:08 +08:00
if PfxFile is not None :
Command = Command + '/f {PfxFile} ' . format ( PfxFile = PfxFile )
if SubjectName is not None :
2024-07-31 17:05:48 +08:00
Command = Command + '/n " {SubjectName} " ' . format ( SubjectName = SubjectName )
2018-05-01 20:54:46 -07:00
Command = Command + TempFileName
2019-06-26 13:55:35 +08:00
if Verbose :
print ( Command )
2018-05-01 20:54:46 -07:00
#
# Sign the input file using the specified private key
#
try :
Process = subprocess . Popen ( Command , stdin = subprocess . PIPE , stdout = subprocess . PIPE , stderr = subprocess . PIPE , shell = True )
Result = Process . communicate ( '' )
except :
shutil . rmtree ( TempDirectoryName )
raise ValueError ( 'GenerateCapsule: error: can not run signtool.' )
if Process . returncode != 0 :
shutil . rmtree ( TempDirectoryName )
2019-06-26 13:55:35 +08:00
print ( Result [ 1 ] . decode ())
2018-05-01 20:54:46 -07:00
raise ValueError ( 'GenerateCapsule: error: signtool failed.' )
#
# Read the signature from the generated output file
#
try :
2019-06-26 13:55:35 +08:00
with open ( TempFileName + '.p7' , 'rb' ) as File :
Signature = File . read ()
2018-05-01 20:54:46 -07:00
except :
shutil . rmtree ( TempDirectoryName )
raise ValueError ( 'GenerateCapsule: error: can not read signature file.' )
shutil . rmtree ( TempDirectoryName )
return Signature
2024-07-31 17:05:48 +08:00
def VerifyPayloadSignTool ( Payload , CertData , ToolPath , PfxFile , SubjectName , HashAlgorithm = DEFAULT_HASH_ALGORITHM , Verbose = False ):
2018-05-01 20:54:46 -07:00
print ( 'signtool verify is not supported.' )
raise ValueError ( 'GenerateCapsule: error: signtool verify is not supported.' )
2024-07-31 17:05:48 +08:00
def SignPayloadOpenSsl ( Payload , ToolPath , SignerPrivateCertFile , OtherPublicCertFile , TrustedPublicCertFile , HashAlgorithm = DEFAULT_HASH_ALGORITHM , Verbose = False ):
#
# Check the hash algorithm is supported
#
CheckHashAlgorithmSupported ( TOOL_OPENSSL , HashAlgorithm )
2018-05-01 20:54:46 -07:00
#
# Build openssl command
#
if ToolPath is None :
ToolPath = ''
Command = ''
Command = Command + '" {Path} " ' . format ( Path = os . path . join ( ToolPath , 'openssl' ))
2026-05-18 11:00:44 +02:00
Command = Command + 'smime -sign -binary -noattr -outform DER -md {HashAlgorithm} ' . format ( HashAlgorithm = HashAlgorithm )
2018-05-01 20:54:46 -07:00
Command = Command + '-signer " {Private} " -certfile " {Public} "' . format ( Private = SignerPrivateCertFile , Public = OtherPublicCertFile )
2019-06-26 13:55:35 +08:00
if Verbose :
print ( Command )
2018-05-01 20:54:46 -07:00
#
# Sign the input file using the specified private key and capture signature from STDOUT
#
try :
Process = subprocess . Popen ( Command , stdin = subprocess . PIPE , stdout = subprocess . PIPE , stderr = subprocess . PIPE , shell = True )
Result = Process . communicate ( input = Payload )
2019-04-10 16:40:33 +08:00
Signature = Result [ 0 ]
2018-05-01 20:54:46 -07:00
except :
raise ValueError ( 'GenerateCapsule: error: can not run openssl.' )
if Process . returncode != 0 :
2019-06-26 13:55:35 +08:00
print ( Result [ 1 ] . decode ())
2018-05-01 20:54:46 -07:00
raise ValueError ( 'GenerateCapsule: error: openssl failed.' )
return Signature
2024-07-31 17:05:48 +08:00
def VerifyPayloadOpenSsl ( Payload , CertData , ToolPath , SignerPrivateCertFile , OtherPublicCertFile , TrustedPublicCertFile , HashAlgorithm = DEFAULT_HASH_ALGORITHM , Verbose = False ):
2018-05-01 20:54:46 -07:00
#
# Create a temporary directory
#
TempDirectoryName = tempfile . mkdtemp ()
#
# Generate temp file name for the payload contents
#
TempFileName = os . path . join ( TempDirectoryName , 'Payload.bin' )
#
# Create temporary payload file for verification
#
try :
2019-06-26 13:55:35 +08:00
with open ( TempFileName , 'wb' ) as File :
File . write ( Payload )
2018-05-01 20:54:46 -07:00
except :
shutil . rmtree ( TempDirectoryName )
raise ValueError ( 'GenerateCapsule: error: can not write temporary payload file.' )
#
# Build openssl command
#
if ToolPath is None :
ToolPath = ''
Command = ''
Command = Command + '" {Path} " ' . format ( Path = os . path . join ( ToolPath , 'openssl' ))
Command = Command + 'smime -verify -inform DER '
Command = Command + '-content {Content} -CAfile " {Public} "' . format ( Content = TempFileName , Public = TrustedPublicCertFile )
2019-06-26 13:55:35 +08:00
if Verbose :
print ( Command )
2018-05-01 20:54:46 -07:00
#
# Verify signature
#
try :
Process = subprocess . Popen ( Command , stdin = subprocess . PIPE , stdout = subprocess . PIPE , stderr = subprocess . PIPE , shell = True )
Result = Process . communicate ( input = CertData )
except :
shutil . rmtree ( TempDirectoryName )
raise ValueError ( 'GenerateCapsule: error: can not run openssl.' )
if Process . returncode != 0 :
shutil . rmtree ( TempDirectoryName )
2019-06-26 13:55:35 +08:00
print ( Result [ 1 ] . decode ())
2018-05-01 20:54:46 -07:00
raise ValueError ( 'GenerateCapsule: error: openssl failed.' )
shutil . rmtree ( TempDirectoryName )
return Payload
if __name__ == '__main__' :
def convert_arg_line_to_args ( arg_line ):
for arg in arg_line . split ():
if not arg . strip ():
continue
yield arg
def ValidateUnsignedInteger ( Argument ):
try :
Value = int ( Argument , 0 )
except :
Message = ' {Argument} is not a valid integer value.' . format ( Argument = Argument )
raise argparse . ArgumentTypeError ( Message )
if Value < 0 :
Message = ' {Argument} is a negative value.' . format ( Argument = Argument )
raise argparse . ArgumentTypeError ( Message )
return Value
def ValidateRegistryFormatGuid ( Argument ):
try :
Value = uuid . UUID ( Argument )
except :
Message = ' {Argument} is not a valid registry format GUID value.' . format ( Argument = Argument )
raise argparse . ArgumentTypeError ( Message )
return Value
2019-06-26 13:55:35 +08:00
def ConvertJsonValue ( Config , FieldName , Convert , Required = True , Default = None , Open = False ):
if FieldName not in Config :
if Required :
print ( 'GenerateCapsule: error: Payload descriptor invalid syntax. Could not find {Key} in payload descriptor.' . format ( Key = FieldName ))
sys . exit ( 1 )
return Default
try :
Value = Convert ( Config [ FieldName ])
except :
print ( 'GenerateCapsule: error: {Key} in payload descriptor has invalid syntax.' . format ( Key = FieldName ))
sys . exit ( 1 )
if Open :
2018-05-01 20:54:46 -07:00
try :
2019-06-26 13:55:35 +08:00
Value = open ( Value , "rb" )
except :
print ( 'GenerateCapsule: error: can not open file {File} ' . format ( File = FieldName ))
sys . exit ( 1 )
return Value
def DecodeJsonFileParse ( Json ):
if 'Payloads' not in Json :
print ( 'GenerateCapsule: error "Payloads" section not found in JSON file {File} ' . format ( File = args . JsonFile . name ))
sys . exit ( 1 )
for Config in Json [ 'Payloads' ]:
#
# Parse fields from JSON
#
PayloadFile = ConvertJsonValue ( Config , 'Payload' , os . path . expandvars , Required = False )
Guid = ConvertJsonValue ( Config , 'Guid' , ValidateRegistryFormatGuid , Required = False )
FwVersion = ConvertJsonValue ( Config , 'FwVersion' , ValidateUnsignedInteger , Required = False )
LowestSupportedVersion = ConvertJsonValue ( Config , 'LowestSupportedVersion' , ValidateUnsignedInteger , Required = False )
HardwareInstance = ConvertJsonValue ( Config , 'HardwareInstance' , ValidateUnsignedInteger , Required = False , Default = 0 )
MonotonicCount = ConvertJsonValue ( Config , 'MonotonicCount' , ValidateUnsignedInteger , Required = False , Default = 0 )
2024-07-31 17:05:48 +08:00
HashAlgorithm = ConvertJsonValue ( Config , 'HashAlgorithm' , str , Required = False , Default = DEFAULT_HASH_ALGORITHM )
2019-06-26 13:55:35 +08:00
SignToolPfxFile = ConvertJsonValue ( Config , 'SignToolPfxFile' , os . path . expandvars , Required = False , Default = None , Open = True )
2024-07-31 17:05:48 +08:00
SignToolSubjectName = ConvertJsonValue ( Config , 'SignToolSubjectName' , str , Required = False , Default = None , Open = False )
2019-06-26 13:55:35 +08:00
OpenSslSignerPrivateCertFile = ConvertJsonValue ( Config , 'OpenSslSignerPrivateCertFile' , os . path . expandvars , Required = False , Default = None , Open = True )
OpenSslOtherPublicCertFile = ConvertJsonValue ( Config , 'OpenSslOtherPublicCertFile' , os . path . expandvars , Required = False , Default = None , Open = True )
OpenSslTrustedPublicCertFile = ConvertJsonValue ( Config , 'OpenSslTrustedPublicCertFile' , os . path . expandvars , Required = False , Default = None , Open = True )
SigningToolPath = ConvertJsonValue ( Config , 'SigningToolPath' , os . path . expandvars , Required = False , Default = None )
UpdateImageIndex = ConvertJsonValue ( Config , 'UpdateImageIndex' , ValidateUnsignedInteger , Required = False , Default = 1 )
PayloadDescriptorList . append ( PayloadDescriptor (
PayloadFile ,
Guid ,
FwVersion ,
LowestSupportedVersion ,
MonotonicCount ,
HardwareInstance ,
UpdateImageIndex ,
2024-07-31 17:05:48 +08:00
HashAlgorithm ,
2019-06-26 13:55:35 +08:00
SignToolPfxFile ,
2022-07-25 23:31:08 +08:00
SignToolSubjectName ,
2019-06-26 13:55:35 +08:00
OpenSslSignerPrivateCertFile ,
OpenSslOtherPublicCertFile ,
OpenSslTrustedPublicCertFile ,
SigningToolPath
))
def EncodeJsonFileParse ( Json ):
if 'EmbeddedDrivers' not in Json :
print ( 'GenerateCapsule: warning "EmbeddedDrivers" section not found in JSON file {File} ' . format ( File = args . JsonFile . name ))
else :
for Config in Json [ 'EmbeddedDrivers' ]:
EmbeddedDriverFile = ConvertJsonValue ( Config , 'Driver' , os . path . expandvars , Open = True )
#
#Read EmbeddedDriver file
#
try :
if args . Verbose :
print ( 'Read EmbeddedDriver file {File} ' . format ( File = EmbeddedDriverFile . name ))
Driver = EmbeddedDriverFile . read ()
except :
print ( 'GenerateCapsule: error: can not read EmbeddedDriver file {File} ' . format ( File = EmbeddedDriverFile . name ))
sys . exit ( 1 )
EmbeddedDriverDescriptorList . append ( Driver )
if 'Payloads' not in Json :
print ( 'GenerateCapsule: error: "Payloads" section not found in JSON file {File} ' . format ( File = args . JsonFile . name ))
sys . exit ( 1 )
for Config in Json [ 'Payloads' ]:
#
# Parse fields from JSON
#
PayloadFile = ConvertJsonValue ( Config , 'Payload' , os . path . expandvars , Open = True )
Guid = ConvertJsonValue ( Config , 'Guid' , ValidateRegistryFormatGuid )
FwVersion = ConvertJsonValue ( Config , 'FwVersion' , ValidateUnsignedInteger )
LowestSupportedVersion = ConvertJsonValue ( Config , 'LowestSupportedVersion' , ValidateUnsignedInteger )
HardwareInstance = ConvertJsonValue ( Config , 'HardwareInstance' , ValidateUnsignedInteger , Required = False , Default = 0 )
UpdateImageIndex = ConvertJsonValue ( Config , 'UpdateImageIndex' , ValidateUnsignedInteger , Required = False , Default = 1 )
MonotonicCount = ConvertJsonValue ( Config , 'MonotonicCount' , ValidateUnsignedInteger , Required = False , Default = 0 )
2024-07-31 17:05:48 +08:00
HashAlgorithm = ConvertJsonValue ( Config , 'HashAlgorithm' , str , Required = False , Default = DEFAULT_HASH_ALGORITHM )
2019-06-26 13:55:35 +08:00
SignToolPfxFile = ConvertJsonValue ( Config , 'SignToolPfxFile' , os . path . expandvars , Required = False , Default = None , Open = True )
2024-07-31 17:05:48 +08:00
SignToolSubjectName = ConvertJsonValue ( Config , 'SignToolSubjectName' , str , Required = False , Default = None , Open = False )
2019-06-26 13:55:35 +08:00
OpenSslSignerPrivateCertFile = ConvertJsonValue ( Config , 'OpenSslSignerPrivateCertFile' , os . path . expandvars , Required = False , Default = None , Open = True )
OpenSslOtherPublicCertFile = ConvertJsonValue ( Config , 'OpenSslOtherPublicCertFile' , os . path . expandvars , Required = False , Default = None , Open = True )
OpenSslTrustedPublicCertFile = ConvertJsonValue ( Config , 'OpenSslTrustedPublicCertFile' , os . path . expandvars , Required = False , Default = None , Open = True )
SigningToolPath = ConvertJsonValue ( Config , 'SigningToolPath' , os . path . expandvars , Required = False , Default = None )
2020-01-10 09:57:35 +08:00
DepexExp = ConvertJsonValue ( Config , 'Dependencies' , str , Required = False , Default = None )
2019-06-26 13:55:35 +08:00
#
# Read binary input file
#
try :
if args . Verbose :
print ( 'Read binary input file {File} ' . format ( File = PayloadFile . name ))
Payload = PayloadFile . read ()
PayloadFile . close ()
except :
print ( 'GenerateCapsule: error: can not read binary input file {File} ' . format ( File = PayloadFile . name ))
sys . exit ( 1 )
PayloadDescriptorList . append ( PayloadDescriptor (
Payload ,
Guid ,
FwVersion ,
LowestSupportedVersion ,
MonotonicCount ,
HardwareInstance ,
UpdateImageIndex ,
2024-07-31 17:05:48 +08:00
HashAlgorithm ,
2019-06-26 13:55:35 +08:00
SignToolPfxFile ,
2022-07-25 23:31:08 +08:00
SignToolSubjectName ,
2019-06-26 13:55:35 +08:00
OpenSslSignerPrivateCertFile ,
OpenSslOtherPublicCertFile ,
OpenSslTrustedPublicCertFile ,
2020-01-10 09:57:35 +08:00
SigningToolPath ,
DepexExp
2019-06-26 13:55:35 +08:00
))
2025-07-27 16:11:59 +03:00
def GenerateOutputJson ( PayloadJsonDescriptorList , EmbeddedDriverPaths ):
2019-06-26 13:55:35 +08:00
PayloadJson = {
2025-07-27 16:11:59 +03:00
"EmbeddedDrivers" : [
{
"Driver" : DriverPath
} for DriverPath in EmbeddedDriverPaths
],
2019-06-26 13:55:35 +08:00
"Payloads" : [
{
"Guid" : str ( PayloadDescriptor . Guid ) . upper (),
"FwVersion" : str ( PayloadDescriptor . FwVersion ),
"LowestSupportedVersion" : str ( PayloadDescriptor . LowestSupportedVersion ),
"MonotonicCount" : str ( PayloadDescriptor . MonotonicCount ),
"Payload" : PayloadDescriptor . Payload ,
"HardwareInstance" : str ( PayloadDescriptor . HardwareInstance ),
"UpdateImageIndex" : str ( PayloadDescriptor . UpdateImageIndex ),
2024-07-31 17:05:48 +08:00
"HashAlgorithm" : str ( PayloadDescriptor . HashAlgorithm ),
2019-06-26 13:55:35 +08:00
"SignToolPfxFile" : str ( PayloadDescriptor . SignToolPfxFile ),
2022-07-25 23:31:08 +08:00
"SignToolSubjectName" : str ( PayloadDescriptor . SignToolSubjectName ),
2019-06-26 13:55:35 +08:00
"OpenSslSignerPrivateCertFile" : str ( PayloadDescriptor . OpenSslSignerPrivateCertFile ),
"OpenSslOtherPublicCertFile" : str ( PayloadDescriptor . OpenSslOtherPublicCertFile ),
"OpenSslTrustedPublicCertFile" : str ( PayloadDescriptor . OpenSslTrustedPublicCertFile ),
2020-01-10 09:57:35 +08:00
"SigningToolPath" : str ( PayloadDescriptor . SigningToolPath ),
"Dependencies" : str ( PayloadDescriptor . DepexExp )
2019-06-26 13:55:35 +08:00
} for PayloadDescriptor in PayloadJsonDescriptorList
]
}
OutputJsonFile = args . OutputFile . name + '.json'
if 'Payloads' in PayloadJson :
PayloadSection = PayloadJson [ 'Payloads' ]
Index = 0
for PayloadField in PayloadSection :
if PayloadJsonDescriptorList [ Index ] . SignToolPfxFile is None :
del PayloadField [ 'SignToolPfxFile' ]
2022-07-25 23:31:08 +08:00
if PayloadJsonDescriptorList [ Index ] . SignToolSubjectName is None :
del PayloadField [ 'SignToolSubjectName' ]
2019-06-26 13:55:35 +08:00
if PayloadJsonDescriptorList [ Index ] . OpenSslSignerPrivateCertFile is None :
del PayloadField [ 'OpenSslSignerPrivateCertFile' ]
if PayloadJsonDescriptorList [ Index ] . OpenSslOtherPublicCertFile is None :
del PayloadField [ 'OpenSslOtherPublicCertFile' ]
if PayloadJsonDescriptorList [ Index ] . OpenSslTrustedPublicCertFile is None :
del PayloadField [ 'OpenSslTrustedPublicCertFile' ]
if PayloadJsonDescriptorList [ Index ] . SigningToolPath is None :
del PayloadField [ 'SigningToolPath' ]
2025-07-27 15:57:00 +03:00
if PayloadJsonDescriptorList [ Index ] . DepexExp is None :
del PayloadField [ 'Dependencies' ]
2019-06-26 13:55:35 +08:00
Index = Index + 1
Result = json . dumps ( PayloadJson , indent = 4 , sort_keys = True , separators = ( ',' , ': ' ))
with open ( OutputJsonFile , 'w' ) as OutputFile :
OutputFile . write ( Result )
def CheckArgumentConflict ( args ):
if args . Encode :
if args . InputFile :
print ( 'GenerateCapsule: error: Argument InputFile conflicts with Argument -j' )
sys . exit ( 1 )
if args . EmbeddedDriver :
print ( 'GenerateCapsule: error: Argument --embedded-driver conflicts with Argument -j' )
sys . exit ( 1 )
if args . Guid :
print ( 'GenerateCapsule: error: Argument --guid conflicts with Argument -j' )
sys . exit ( 1 )
if args . FwVersion :
print ( 'GenerateCapsule: error: Argument --fw-version conflicts with Argument -j' )
sys . exit ( 1 )
if args . LowestSupportedVersion :
print ( 'GenerateCapsule: error: Argument --lsv conflicts with Argument -j' )
sys . exit ( 1 )
if args . MonotonicCount :
print ( 'GenerateCapsule: error: Argument --monotonic-count conflicts with Argument -j' )
sys . exit ( 1 )
if args . HardwareInstance :
print ( 'GenerateCapsule: error: Argument --hardware-instance conflicts with Argument -j' )
sys . exit ( 1 )
2024-07-31 17:05:48 +08:00
if args . HashAlgorithm :
print ( 'GenerateCapsule: error: Argument --hash-algorithm conflicts with Argument -j' )
sys . exit ( 1 )
2019-06-26 13:55:35 +08:00
if args . SignToolPfxFile :
print ( 'GenerateCapsule: error: Argument --pfx-file conflicts with Argument -j' )
sys . exit ( 1 )
2022-07-25 23:31:08 +08:00
if args . SignToolSubjectName :
2024-07-31 17:05:48 +08:00
print ( 'GenerateCapsule: error: Argument --subject-name conflicts with Argument -j' )
2022-07-25 23:31:08 +08:00
sys . exit ( 1 )
2019-06-26 13:55:35 +08:00
if args . OpenSslSignerPrivateCertFile :
print ( 'GenerateCapsule: error: Argument --signer-private-cert conflicts with Argument -j' )
sys . exit ( 1 )
if args . OpenSslOtherPublicCertFile :
print ( 'GenerateCapsule: error: Argument --other-public-cert conflicts with Argument -j' )
sys . exit ( 1 )
if args . OpenSslTrustedPublicCertFile :
print ( 'GenerateCapsule: error: Argument --trusted-public-cert conflicts with Argument -j' )
sys . exit ( 1 )
if args . SigningToolPath :
print ( 'GenerateCapsule: error: Argument --signing-tool-path conflicts with Argument -j' )
sys . exit ( 1 )
class PayloadDescriptor ( object ):
def __init__ ( self ,
Payload ,
Guid ,
FwVersion ,
LowestSupportedVersion ,
MonotonicCount = 0 ,
HardwareInstance = 0 ,
UpdateImageIndex = 1 ,
2024-07-31 17:05:48 +08:00
HashAlgorithm = None ,
2019-06-26 13:55:35 +08:00
SignToolPfxFile = None ,
2022-07-25 23:31:08 +08:00
SignToolSubjectName = None ,
2019-06-26 13:55:35 +08:00
OpenSslSignerPrivateCertFile = None ,
OpenSslOtherPublicCertFile = None ,
OpenSslTrustedPublicCertFile = None ,
2020-01-10 09:57:35 +08:00
SigningToolPath = None ,
DepexExp = None
2019-06-26 13:55:35 +08:00
):
self . Payload = Payload
self . Guid = Guid
self . FwVersion = FwVersion
self . LowestSupportedVersion = LowestSupportedVersion
self . MonotonicCount = MonotonicCount
self . HardwareInstance = HardwareInstance
self . UpdateImageIndex = UpdateImageIndex
2024-07-31 17:05:48 +08:00
self . HashAlgorithm = HashAlgorithm
2019-06-26 13:55:35 +08:00
self . SignToolPfxFile = SignToolPfxFile
2022-07-25 23:31:08 +08:00
self . SignToolSubjectName = SignToolSubjectName
2019-06-26 13:55:35 +08:00
self . OpenSslSignerPrivateCertFile = OpenSslSignerPrivateCertFile
self . OpenSslOtherPublicCertFile = OpenSslOtherPublicCertFile
self . OpenSslTrustedPublicCertFile = OpenSslTrustedPublicCertFile
self . SigningToolPath = SigningToolPath
2020-01-10 09:57:35 +08:00
self . DepexExp = DepexExp
2019-06-26 13:55:35 +08:00
2022-07-25 23:31:08 +08:00
self . UseSignTool = ( self . SignToolPfxFile is not None or
self . SignToolSubjectName is not None )
2019-06-26 13:55:35 +08:00
self . UseOpenSsl = ( self . OpenSslSignerPrivateCertFile is not None and
self . OpenSslOtherPublicCertFile is not None and
self . OpenSslTrustedPublicCertFile is not None )
self . AnyOpenSsl = ( self . OpenSslSignerPrivateCertFile is not None or
self . OpenSslOtherPublicCertFile is not None or
self . OpenSslTrustedPublicCertFile is not None )
2020-01-10 09:57:35 +08:00
self . UseDependency = self . DepexExp is not None
2019-06-26 13:55:35 +08:00
def Validate ( self , args ):
if self . UseSignTool and self . AnyOpenSsl :
raise argparse . ArgumentTypeError ( 'Providing both signtool and OpenSSL options is not supported' )
if not self . UseSignTool and not self . UseOpenSsl and self . AnyOpenSsl :
if args . JsonFile :
raise argparse . ArgumentTypeError ( 'the following JSON fields are required for OpenSSL: OpenSslSignerPrivateCertFile, OpenSslOtherPublicCertFile, OpenSslTrustedPublicCertFile' )
else :
raise argparse . ArgumentTypeError ( 'the following options are required for OpenSSL: --signer-private-cert, --other-public-cert, --trusted-public-cert' )
if self . UseSignTool and platform . system () != 'Windows' :
raise argparse . ArgumentTypeError ( 'Use of signtool is not supported on this operating system.' )
if args . Encode :
if self . FwVersion is None or self . LowestSupportedVersion is None :
if args . JsonFile :
raise argparse . ArgumentTypeError ( 'the following JSON fields are required: FwVersion, LowestSupportedVersion' )
else :
raise argparse . ArgumentTypeError ( 'the following options are required: --fw-version, --lsv' )
if self . FwVersion > 0xFFFFFFFF :
if args . JsonFile :
raise argparse . ArgumentTypeError ( 'JSON field FwVersion must be an integer in range 0x0..0xffffffff' )
else :
raise argparse . ArgumentTypeError ( '--fw-version must be an integer in range 0x0..0xffffffff' )
if self . LowestSupportedVersion > 0xFFFFFFFF :
if args . JsonFile :
raise argparse . ArgumentTypeError ( 'JSON field LowestSupportedVersion must be an integer in range 0x0..0xffffffff' )
else :
raise argparse . ArgumentTypeError ( '--lsv must be an integer in range 0x0..0xffffffff' )
if args . Encode :
if self . Guid is None :
if args . JsonFile :
raise argparse . ArgumentTypeError ( 'the following JSON field is required: Guid' )
else :
raise argparse . ArgumentTypeError ( 'the following option is required: --guid' )
if self . HardwareInstance > 0xFFFFFFFFFFFFFFFF :
if args . JsonFile :
raise argparse . ArgumentTypeError ( 'JSON field HardwareInstance must be an integer in range 0x0..0xffffffffffffffff' )
else :
raise argparse . ArgumentTypeError ( '--hardware-instance must be an integer in range 0x0..0xffffffffffffffff' )
if self . MonotonicCount > 0xFFFFFFFFFFFFFFFF :
if args . JsonFile :
raise argparse . ArgumentTypeError ( 'JSON field MonotonicCount must be an integer in range 0x0..0xffffffffffffffff' )
else :
raise argparse . ArgumentTypeError ( '--monotonic-count must be an integer in range 0x0..0xffffffffffffffff' )
2024-06-22 20:38:03 +03:00
if self . UpdateImageIndex < 0x1 or self . UpdateImageIndex > 0xFF :
2019-06-26 13:55:35 +08:00
if args . JsonFile :
2024-06-22 20:38:03 +03:00
raise argparse . ArgumentTypeError ( 'JSON field UpdateImageIndex must be an integer in range 0x1..0xff' )
2019-06-26 13:55:35 +08:00
else :
2024-06-22 20:38:03 +03:00
raise argparse . ArgumentTypeError ( '--update-image-index must be an integer in range 0x1..0xff' )
2019-06-26 13:55:35 +08:00
2024-06-22 20:43:13 +03:00
if args . Decode :
if args . OutputFile is None :
raise argparse . ArgumentTypeError ( '--decode requires --output' )
2024-07-31 17:05:48 +08:00
if self . HashAlgorithm is None :
self . HashAlgorithm = DEFAULT_HASH_ALGORITHM
2019-06-26 13:55:35 +08:00
if self . UseSignTool :
2022-07-25 23:31:08 +08:00
if self . SignToolPfxFile is not None :
self . SignToolPfxFile . close ()
self . SignToolPfxFile = self . SignToolPfxFile . name
2019-06-26 13:55:35 +08:00
if self . UseOpenSsl :
self . OpenSslSignerPrivateCertFile . close ()
self . OpenSslOtherPublicCertFile . close ()
self . OpenSslTrustedPublicCertFile . close ()
self . OpenSslSignerPrivateCertFile = self . OpenSslSignerPrivateCertFile . name
self . OpenSslOtherPublicCertFile = self . OpenSslOtherPublicCertFile . name
self . OpenSslTrustedPublicCertFile = self . OpenSslTrustedPublicCertFile . name
#
# Perform additional argument verification
#
if args . Encode :
if 'PersistAcrossReset' not in args . CapsuleFlag :
if 'InitiateReset' in args . CapsuleFlag :
raise argparse . ArgumentTypeError ( '--capflag InitiateReset also requires --capflag PersistAcrossReset' )
if args . CapsuleOemFlag > 0xFFFF :
raise argparse . ArgumentTypeError ( '--capoemflag must be an integer between 0x0000 and 0xffff' )
return True
def Encode ( PayloadDescriptorList , EmbeddedDriverDescriptorList , Buffer ):
if args . JsonFile :
CheckArgumentConflict ( args )
try :
Json = json . loads ( args . JsonFile . read ())
except :
print ( 'GenerateCapsule: error: {JSONFile} loads failure. ' . format ( JSONFile = args . JsonFile ))
sys . exit ( 1 )
EncodeJsonFileParse ( Json )
else :
for Driver in args . EmbeddedDriver :
EmbeddedDriverDescriptorList . append ( Driver . read ())
PayloadDescriptorList . append ( PayloadDescriptor (
Buffer ,
args . Guid ,
args . FwVersion ,
args . LowestSupportedVersion ,
args . MonotonicCount ,
args . HardwareInstance ,
args . UpdateImageIndex ,
2024-07-31 17:05:48 +08:00
args . HashAlgorithm ,
2019-06-26 13:55:35 +08:00
args . SignToolPfxFile ,
2022-07-25 23:31:08 +08:00
args . SignToolSubjectName ,
2019-06-26 13:55:35 +08:00
args . OpenSslSignerPrivateCertFile ,
args . OpenSslOtherPublicCertFile ,
args . OpenSslTrustedPublicCertFile ,
2020-01-10 09:57:35 +08:00
args . SigningToolPath ,
None
2019-06-26 13:55:35 +08:00
))
for SinglePayloadDescriptor in PayloadDescriptorList :
try :
SinglePayloadDescriptor . Validate ( args )
except Exception as Msg :
2024-06-22 20:46:44 +03:00
print ( 'GenerateCapsule: error: ' + str ( Msg ))
2019-06-26 13:55:35 +08:00
sys . exit ( 1 )
for SinglePayloadDescriptor in PayloadDescriptorList :
2021-04-22 15:50:59 +08:00
ImageCapsuleSupport = 0x0000000000000000
2019-06-26 13:55:35 +08:00
Result = SinglePayloadDescriptor . Payload
try :
FmpPayloadHeader . FwVersion = SinglePayloadDescriptor . FwVersion
FmpPayloadHeader . LowestSupportedVersion = SinglePayloadDescriptor . LowestSupportedVersion
FmpPayloadHeader . Payload = SinglePayloadDescriptor . Payload
2018-05-01 20:54:46 -07:00
Result = FmpPayloadHeader . Encode ()
if args . Verbose :
FmpPayloadHeader . DumpInfo ()
except :
print ( 'GenerateCapsule: error: can not encode FMP Payload Header' )
sys . exit ( 1 )
2020-01-10 09:57:35 +08:00
if SinglePayloadDescriptor . UseDependency :
CapsuleDependency . Payload = Result
CapsuleDependency . DepexExp = SinglePayloadDescriptor . DepexExp
2021-04-22 15:50:59 +08:00
ImageCapsuleSupport |= FmpCapsuleHeader . CAPSULE_SUPPORT_DEPENDENCY
2020-01-10 09:57:35 +08:00
Result = CapsuleDependency . Encode ()
if args . Verbose :
CapsuleDependency . DumpInfo ()
2019-06-26 13:55:35 +08:00
if SinglePayloadDescriptor . UseOpenSsl or SinglePayloadDescriptor . UseSignTool :
#
# Sign image with 64-bit MonotonicCount appended to end of image
#
try :
if SinglePayloadDescriptor . UseSignTool :
CertData = SignPayloadSignTool (
Result + struct . pack ( '<Q' , SinglePayloadDescriptor . MonotonicCount ),
SinglePayloadDescriptor . SigningToolPath ,
SinglePayloadDescriptor . SignToolPfxFile ,
2022-07-25 23:31:08 +08:00
SinglePayloadDescriptor . SignToolSubjectName ,
2024-07-31 17:05:48 +08:00
HashAlgorithm = SinglePayloadDescriptor . HashAlgorithm ,
2019-06-26 13:55:35 +08:00
Verbose = args . Verbose
)
else :
CertData = SignPayloadOpenSsl (
Result + struct . pack ( '<Q' , SinglePayloadDescriptor . MonotonicCount ),
SinglePayloadDescriptor . SigningToolPath ,
SinglePayloadDescriptor . OpenSslSignerPrivateCertFile ,
SinglePayloadDescriptor . OpenSslOtherPublicCertFile ,
SinglePayloadDescriptor . OpenSslTrustedPublicCertFile ,
2024-07-31 17:05:48 +08:00
HashAlgorithm = SinglePayloadDescriptor . HashAlgorithm ,
2019-06-26 13:55:35 +08:00
Verbose = args . Verbose
)
except Exception as Msg :
print ( 'GenerateCapsule: error: can not sign payload \n ' + str ( Msg ))
sys . exit ( 1 )
2018-05-01 20:54:46 -07:00
2019-06-26 13:55:35 +08:00
try :
FmpAuthHeader . MonotonicCount = SinglePayloadDescriptor . MonotonicCount
FmpAuthHeader . CertData = CertData
FmpAuthHeader . Payload = Result
2021-04-22 15:50:59 +08:00
ImageCapsuleSupport |= FmpCapsuleHeader . CAPSULE_SUPPORT_AUTHENTICATION
2019-06-26 13:55:35 +08:00
Result = FmpAuthHeader . Encode ()
if args . Verbose :
FmpAuthHeader . DumpInfo ()
except :
print ( 'GenerateCapsule: error: can not encode FMP Auth Header' )
sys . exit ( 1 )
2021-04-22 15:50:59 +08:00
FmpCapsuleHeader . AddPayload ( SinglePayloadDescriptor . Guid , Result , HardwareInstance = SinglePayloadDescriptor . HardwareInstance , UpdateImageIndex = SinglePayloadDescriptor . UpdateImageIndex , CapsuleSupport = ImageCapsuleSupport )
2018-05-01 20:54:46 -07:00
try :
2019-06-26 13:55:35 +08:00
for EmbeddedDriver in EmbeddedDriverDescriptorList :
FmpCapsuleHeader . AddEmbeddedDriver ( EmbeddedDriver )
2018-05-01 20:54:46 -07:00
Result = FmpCapsuleHeader . Encode ()
if args . Verbose :
FmpCapsuleHeader . DumpInfo ()
except :
print ( 'GenerateCapsule: error: can not encode FMP Capsule Header' )
sys . exit ( 1 )
try :
UefiCapsuleHeader . OemFlags = args . CapsuleOemFlag
UefiCapsuleHeader . PersistAcrossReset = 'PersistAcrossReset' in args . CapsuleFlag
2018-07-27 12:31:22 -07:00
UefiCapsuleHeader . PopulateSystemTable = False
2018-05-01 20:54:46 -07:00
UefiCapsuleHeader . InitiateReset = 'InitiateReset' in args . CapsuleFlag
UefiCapsuleHeader . Payload = Result
Result = UefiCapsuleHeader . Encode ()
if args . Verbose :
UefiCapsuleHeader . DumpInfo ()
except :
print ( 'GenerateCapsule: error: can not encode UEFI Capsule Header' )
sys . exit ( 1 )
try :
if args . Verbose :
print ( 'Write binary output file {File} ' . format ( File = args . OutputFile . name ))
args . OutputFile . write ( Result )
args . OutputFile . close ()
except :
print ( 'GenerateCapsule: error: can not write binary output file {File} ' . format ( File = args . OutputFile . name ))
sys . exit ( 1 )
2019-06-26 13:55:35 +08:00
def Decode ( PayloadDescriptorList , PayloadJsonDescriptorList , Buffer ):
if args . JsonFile :
CheckArgumentConflict ( args )
#
# Parse payload descriptors from JSON
#
try :
Json = json . loads ( args . JsonFile . read ())
except :
print ( 'GenerateCapsule: error: {JSONFile} loads failure. ' . format ( JSONFile = args . JsonFile ))
sys . exit ( 1 )
DecodeJsonFileParse ( Json )
else :
PayloadDescriptorList . append ( PayloadDescriptor (
Buffer ,
args . Guid ,
args . FwVersion ,
args . LowestSupportedVersion ,
args . MonotonicCount ,
args . HardwareInstance ,
args . UpdateImageIndex ,
2024-07-31 17:05:48 +08:00
args . HashAlgorithm ,
2019-06-26 13:55:35 +08:00
args . SignToolPfxFile ,
2024-06-22 20:31:39 +03:00
args . SignToolSubjectName ,
2019-06-26 13:55:35 +08:00
args . OpenSslSignerPrivateCertFile ,
args . OpenSslOtherPublicCertFile ,
args . OpenSslTrustedPublicCertFile ,
2020-01-10 09:57:35 +08:00
args . SigningToolPath ,
None
2019-06-26 13:55:35 +08:00
))
#
# Perform additional verification on payload descriptors
#
for SinglePayloadDescriptor in PayloadDescriptorList :
try :
SinglePayloadDescriptor . Validate ( args )
except Exception as Msg :
2024-06-22 20:46:44 +03:00
print ( 'GenerateCapsule: error: ' + str ( Msg ))
2019-06-26 13:55:35 +08:00
sys . exit ( 1 )
2025-07-27 16:11:59 +03:00
EmbeddedDriverPaths = []
2019-06-26 13:55:35 +08:00
try :
Result = UefiCapsuleHeader . Decode ( Buffer )
if len ( Result ) > 0 :
Result = FmpCapsuleHeader . Decode ( Result )
if args . JsonFile :
if FmpCapsuleHeader . PayloadItemCount != len ( PayloadDescriptorList ):
CapsulePayloadNum = FmpCapsuleHeader . PayloadItemCount
JsonPayloadNum = len ( PayloadDescriptorList )
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 ))
sys . exit ( 1 )
for Index in range ( 0 , FmpCapsuleHeader . PayloadItemCount ):
if Index < len ( PayloadDescriptorList ):
GUID = FmpCapsuleHeader . GetFmpCapsuleImageHeader ( Index ) . UpdateImageTypeId
HardwareInstance = FmpCapsuleHeader . GetFmpCapsuleImageHeader ( Index ) . UpdateHardwareInstance
UpdateImageIndex = FmpCapsuleHeader . GetFmpCapsuleImageHeader ( Index ) . UpdateImageIndex
if PayloadDescriptorList [ Index ] . Guid != GUID or PayloadDescriptorList [ Index ] . HardwareInstance != HardwareInstance :
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 ))
sys . exit ( 1 )
PayloadDescriptorList [ Index ] . Payload = FmpCapsuleHeader . GetFmpCapsuleImageHeader ( Index ) . Payload
DecodeJsonOutput = args . OutputFile . name + '.Payload. {Index:d} .bin' . format ( Index = Index + 1 )
PayloadJsonDescriptorList . append ( PayloadDescriptor (
DecodeJsonOutput ,
GUID ,
None ,
None ,
None ,
HardwareInstance ,
UpdateImageIndex ,
2024-07-31 17:05:48 +08:00
PayloadDescriptorList [ Index ] . HashAlgorithm ,
2019-06-26 13:55:35 +08:00
PayloadDescriptorList [ Index ] . SignToolPfxFile ,
2022-07-25 23:31:08 +08:00
PayloadDescriptorList [ Index ] . SignToolSubjectName ,
2019-06-26 13:55:35 +08:00
PayloadDescriptorList [ Index ] . OpenSslSignerPrivateCertFile ,
PayloadDescriptorList [ Index ] . OpenSslOtherPublicCertFile ,
PayloadDescriptorList [ Index ] . OpenSslTrustedPublicCertFile ,
2020-01-10 09:57:35 +08:00
PayloadDescriptorList [ Index ] . SigningToolPath ,
None
2019-06-26 13:55:35 +08:00
))
else :
PayloadDescriptorList [ 0 ] . Payload = FmpCapsuleHeader . GetFmpCapsuleImageHeader ( 0 ) . Payload
for Index in range ( 0 , FmpCapsuleHeader . PayloadItemCount ):
if Index > 0 :
PayloadDecodeFile = FmpCapsuleHeader . GetFmpCapsuleImageHeader ( Index ) . Payload
2024-07-31 17:05:48 +08:00
PayloadDescriptorList . append ( PayloadDescriptor (
PayloadDecodeFile ,
None ,
None ,
2019-06-26 13:55:35 +08:00
None ,
None ,
None ,
None ,
None ,
None ,
None ,
None ,
None ,
None ,
2020-01-10 09:57:35 +08:00
None ,
2019-06-26 13:55:35 +08:00
None
))
GUID = FmpCapsuleHeader . GetFmpCapsuleImageHeader ( Index ) . UpdateImageTypeId
HardwareInstance = FmpCapsuleHeader . GetFmpCapsuleImageHeader ( Index ) . UpdateHardwareInstance
UpdateImageIndex = FmpCapsuleHeader . GetFmpCapsuleImageHeader ( Index ) . UpdateImageIndex
DecodeJsonOutput = args . OutputFile . name + '.Payload. {Index:d} .bin' . format ( Index = Index + 1 )
PayloadJsonDescriptorList . append ( PayloadDescriptor (
DecodeJsonOutput ,
GUID ,
None ,
None ,
None ,
HardwareInstance ,
UpdateImageIndex ,
2024-07-31 17:05:48 +08:00
PayloadDescriptorList [ Index ] . HashAlgorithm ,
2019-06-26 13:55:35 +08:00
PayloadDescriptorList [ Index ] . SignToolPfxFile ,
2022-07-25 23:31:08 +08:00
PayloadDescriptorList [ Index ] . SignToolSubjectName ,
2019-06-26 13:55:35 +08:00
PayloadDescriptorList [ Index ] . OpenSslSignerPrivateCertFile ,
PayloadDescriptorList [ Index ] . OpenSslOtherPublicCertFile ,
PayloadDescriptorList [ Index ] . OpenSslTrustedPublicCertFile ,
2020-01-10 09:57:35 +08:00
PayloadDescriptorList [ Index ] . SigningToolPath ,
None
2019-06-26 13:55:35 +08:00
))
JsonIndex = 0
for SinglePayloadDescriptor in PayloadDescriptorList :
if args . Verbose :
print ( '========' )
UefiCapsuleHeader . DumpInfo ()
print ( '--------' )
FmpCapsuleHeader . DumpInfo ()
if FmpAuthHeader . IsSigned ( SinglePayloadDescriptor . Payload ):
if not SinglePayloadDescriptor . UseOpenSsl and not SinglePayloadDescriptor . UseSignTool :
print ( 'GenerateCapsule: decode warning: can not verify singed payload without cert or pfx file. Index = {Index} ' . format ( Index = JsonIndex + 1 ))
SinglePayloadDescriptor . Payload = FmpAuthHeader . Decode ( SinglePayloadDescriptor . Payload )
PayloadJsonDescriptorList [ JsonIndex ] . MonotonicCount = FmpAuthHeader . MonotonicCount
if args . Verbose :
print ( '--------' )
FmpAuthHeader . DumpInfo ()
#
# Verify Image with 64-bit MonotonicCount appended to end of image
#
try :
if SinglePayloadDescriptor . UseSignTool :
CertData = VerifyPayloadSignTool (
FmpAuthHeader . Payload + struct . pack ( '<Q' , FmpAuthHeader . MonotonicCount ),
FmpAuthHeader . CertData ,
SinglePayloadDescriptor . SigningToolPath ,
SinglePayloadDescriptor . SignToolPfxFile ,
2022-07-25 23:31:08 +08:00
SinglePayloadDescriptor . SignToolSubjectName ,
2024-07-31 17:05:48 +08:00
HashAlgorithm = SinglePayloadDescriptor . HashAlgorithm ,
2019-06-26 13:55:35 +08:00
Verbose = args . Verbose
)
2025-07-27 16:04:30 +03:00
elif SinglePayloadDescriptor . UseOpenSsl :
2019-06-26 13:55:35 +08:00
CertData = VerifyPayloadOpenSsl (
FmpAuthHeader . Payload + struct . pack ( '<Q' , FmpAuthHeader . MonotonicCount ),
FmpAuthHeader . CertData ,
SinglePayloadDescriptor . SigningToolPath ,
SinglePayloadDescriptor . OpenSslSignerPrivateCertFile ,
SinglePayloadDescriptor . OpenSslOtherPublicCertFile ,
SinglePayloadDescriptor . OpenSslTrustedPublicCertFile ,
2024-07-31 17:05:48 +08:00
HashAlgorithm = SinglePayloadDescriptor . HashAlgorithm ,
2019-06-26 13:55:35 +08:00
Verbose = args . Verbose
)
except Exception as Msg :
print ( 'GenerateCapsule: warning: payload verification failed Index = {Index} \n ' . format ( Index = JsonIndex + 1 ) + str ( Msg ))
else :
if args . Verbose :
print ( '--------' )
print ( 'No EFI_FIRMWARE_IMAGE_AUTHENTICATION' )
2020-01-10 09:57:35 +08:00
2024-06-22 20:47:56 +03:00
( PayloadSignature ,) = struct . unpack ( '<I' , SinglePayloadDescriptor . Payload [ 0 : 4 ])
2020-01-10 09:57:35 +08:00
if PayloadSignature != FmpPayloadHeader . Signature :
SinglePayloadDescriptor . UseDependency = True
try :
SinglePayloadDescriptor . Payload = CapsuleDependency . Decode ( SinglePayloadDescriptor . Payload )
PayloadJsonDescriptorList [ JsonIndex ] . DepexExp = CapsuleDependency . DepexExp
if args . Verbose :
print ( '--------' )
CapsuleDependency . DumpInfo ()
except Exception as Msg :
print ( 'GenerateCapsule: error: invalid dependency expression' )
else :
if args . Verbose :
print ( '--------' )
print ( 'No EFI_FIRMWARE_IMAGE_DEP' )
2019-06-26 13:55:35 +08:00
try :
SinglePayloadDescriptor . Payload = FmpPayloadHeader . Decode ( SinglePayloadDescriptor . Payload )
PayloadJsonDescriptorList [ JsonIndex ] . FwVersion = FmpPayloadHeader . FwVersion
PayloadJsonDescriptorList [ JsonIndex ] . LowestSupportedVersion = FmpPayloadHeader . LowestSupportedVersion
JsonIndex = JsonIndex + 1
if args . Verbose :
print ( '--------' )
FmpPayloadHeader . DumpInfo ()
print ( '========' )
except :
if args . Verbose :
print ( '--------' )
print ( 'No FMP_PAYLOAD_HEADER' )
print ( '========' )
sys . exit ( 1 )
#
# Write embedded driver file(s)
#
for Index in range ( 0 , FmpCapsuleHeader . EmbeddedDriverCount ):
EmbeddedDriverBuffer = FmpCapsuleHeader . GetEmbeddedDriver ( Index )
EmbeddedDriverPath = args . OutputFile . name + '.EmbeddedDriver. {Index:d} .efi' . format ( Index = Index + 1 )
try :
if args . Verbose :
print ( 'Write embedded driver file {File} ' . format ( File = EmbeddedDriverPath ))
with open ( EmbeddedDriverPath , 'wb' ) as EmbeddedDriverFile :
EmbeddedDriverFile . write ( EmbeddedDriverBuffer )
except :
print ( 'GenerateCapsule: error: can not write embedded driver file {File} ' . format ( File = EmbeddedDriverPath ))
sys . exit ( 1 )
2025-07-27 16:11:59 +03:00
EmbeddedDriverPaths . append ( EmbeddedDriverPath )
2024-06-22 20:42:16 +03:00
except Exception as Msg :
print ( 'GenerateCapsule: error: can not decode capsule: ' + str ( Msg ))
2019-06-26 13:55:35 +08:00
sys . exit ( 1 )
2025-07-27 16:11:59 +03:00
GenerateOutputJson ( PayloadJsonDescriptorList , EmbeddedDriverPaths )
2019-06-26 13:55:35 +08:00
PayloadIndex = 0
for SinglePayloadDescriptor in PayloadDescriptorList :
if args . OutputFile is None :
print ( 'GenerateCapsule: Decode error: OutputFile is needed for decode output' )
sys . exit ( 1 )
try :
if args . Verbose :
print ( 'Write binary output file {File} ' . format ( File = args . OutputFile . name ))
PayloadDecodePath = args . OutputFile . name + '.Payload. {Index:d} .bin' . format ( Index = PayloadIndex + 1 )
with open ( PayloadDecodePath , 'wb' ) as PayloadDecodeFile :
PayloadDecodeFile . write ( SinglePayloadDescriptor . Payload )
PayloadIndex = PayloadIndex + 1
except :
print ( 'GenerateCapsule: error: can not write binary output file {File} ' . format ( File = SinglePayloadDescriptor . OutputFile . name ))
sys . exit ( 1 )
def DumpInfo ( Buffer , args ):
if args . OutputFile is not None :
raise argparse . ArgumentTypeError ( 'the following option is not supported for dumpinfo operations: --output' )
try :
Result = UefiCapsuleHeader . Decode ( Buffer )
print ( '========' )
UefiCapsuleHeader . DumpInfo ()
if len ( Result ) > 0 :
FmpCapsuleHeader . Decode ( Result )
print ( '--------' )
FmpCapsuleHeader . DumpInfo ()
for Index in range ( 0 , FmpCapsuleHeader . PayloadItemCount ):
Result = FmpCapsuleHeader . GetFmpCapsuleImageHeader ( Index ) . Payload
try :
Result = FmpAuthHeader . Decode ( Result )
print ( '--------' )
FmpAuthHeader . DumpInfo ()
except :
print ( '--------' )
print ( 'No EFI_FIRMWARE_IMAGE_AUTHENTICATION' )
2020-01-10 09:57:35 +08:00
2024-06-22 20:47:56 +03:00
( PayloadSignature ,) = struct . unpack ( '<I' , Result [ 0 : 4 ])
2020-01-10 09:57:35 +08:00
if PayloadSignature != FmpPayloadHeader . Signature :
try :
Result = CapsuleDependency . Decode ( Result )
print ( '--------' )
CapsuleDependency . DumpInfo ()
except :
print ( 'GenerateCapsule: error: invalid dependency expression' )
else :
print ( '--------' )
print ( 'No EFI_FIRMWARE_IMAGE_DEP' )
2019-06-26 13:55:35 +08:00
try :
Result = FmpPayloadHeader . Decode ( Result )
print ( '--------' )
FmpPayloadHeader . DumpInfo ()
except :
print ( '--------' )
print ( 'No FMP_PAYLOAD_HEADER' )
print ( '========' )
except :
print ( 'GenerateCapsule: error: can not decode capsule' )
sys . exit ( 1 )
#
# Create command line argument parser object
#
parser = argparse . ArgumentParser (
prog = __prog__ ,
description = __description__ + __copyright__ ,
conflict_handler = 'resolve' ,
fromfile_prefix_chars = '@'
)
parser . convert_arg_line_to_args = convert_arg_line_to_args
#
# Add input and output file arguments
#
parser . add_argument ( "InputFile" , type = argparse . FileType ( 'rb' ), nargs = '?' ,
help = "Input binary payload filename." )
parser . add_argument ( "-o" , "--output" , dest = 'OutputFile' , type = argparse . FileType ( 'wb' ),
help = "Output filename." )
#
# Add group for -e and -d flags that are mutually exclusive and required
#
group = parser . add_mutually_exclusive_group ( required = True )
group . add_argument ( "-e" , "--encode" , dest = 'Encode' , action = "store_true" ,
help = "Encode file" )
group . add_argument ( "-d" , "--decode" , dest = 'Decode' , action = "store_true" ,
help = "Decode file" )
group . add_argument ( "--dump-info" , dest = 'DumpInfo' , action = "store_true" ,
help = "Display FMP Payload Header information" )
#
# Add optional arguments for this command
#
parser . add_argument ( "-j" , "--json-file" , dest = 'JsonFile' , type = argparse . FileType ( 'r' ),
help = "JSON configuration file for multiple payloads and embedded drivers." )
parser . add_argument ( "--capflag" , dest = 'CapsuleFlag' , action = 'append' , default = [],
choices = [ 'PersistAcrossReset' , 'InitiateReset' ],
help = "Capsule flag can be PersistAcrossReset or InitiateReset or not set" )
parser . add_argument ( "--capoemflag" , dest = 'CapsuleOemFlag' , type = ValidateUnsignedInteger , default = 0x0000 ,
help = "Capsule OEM Flag is an integer between 0x0000 and 0xffff." )
parser . add_argument ( "--guid" , dest = 'Guid' , type = ValidateRegistryFormatGuid ,
help = "The FMP/ESRT GUID in registry format. Required for single payload encode operations." )
parser . add_argument ( "--hardware-instance" , dest = 'HardwareInstance' , type = ValidateUnsignedInteger , default = 0x0000000000000000 ,
help = "The 64-bit hardware instance. The default is 0x0000000000000000" )
parser . add_argument ( "--monotonic-count" , dest = 'MonotonicCount' , type = ValidateUnsignedInteger , default = 0x0000000000000000 ,
help = "64-bit monotonic count value in header. Default is 0x0000000000000000." )
parser . add_argument ( "--fw-version" , dest = 'FwVersion' , type = ValidateUnsignedInteger ,
help = "The 32-bit version of the binary payload (e.g. 0x11223344 or 5678). Required for encode operations." )
parser . add_argument ( "--lsv" , dest = 'LowestSupportedVersion' , type = ValidateUnsignedInteger ,
help = "The 32-bit lowest supported version of the binary payload (e.g. 0x11223344 or 5678). Required for encode operations." )
2024-07-31 17:05:48 +08:00
parser . add_argument ( "--hash-algorithm" , dest = 'HashAlgorithm' , type = str ,
help = "Hash algorithm for the payload digest." )
2019-06-26 13:55:35 +08:00
parser . add_argument ( "--pfx-file" , dest = 'SignToolPfxFile' , type = argparse . FileType ( 'rb' ),
help = "signtool PFX certificate filename." )
2022-07-25 23:31:08 +08:00
parser . add_argument ( "--subject-name" , dest = 'SignToolSubjectName' ,
help = "signtool certificate subject name." )
2019-06-26 13:55:35 +08:00
parser . add_argument ( "--signer-private-cert" , dest = 'OpenSslSignerPrivateCertFile' , type = argparse . FileType ( 'rb' ),
help = "OpenSSL signer private certificate filename." )
parser . add_argument ( "--other-public-cert" , dest = 'OpenSslOtherPublicCertFile' , type = argparse . FileType ( 'rb' ),
help = "OpenSSL other public certificate filename." )
parser . add_argument ( "--trusted-public-cert" , dest = 'OpenSslTrustedPublicCertFile' , type = argparse . FileType ( 'rb' ),
help = "OpenSSL trusted public certificate filename." )
parser . add_argument ( "--signing-tool-path" , dest = 'SigningToolPath' ,
help = "Path to signtool or OpenSSL tool. Optional if path to tools are already in PATH." )
parser . add_argument ( "--embedded-driver" , dest = 'EmbeddedDriver' , type = argparse . FileType ( 'rb' ), action = 'append' , default = [],
help = "Path to embedded UEFI driver to add to capsule." )
#
# Add optional arguments common to all operations
#
parser . add_argument ( '--version' , action = 'version' , version = ' %(prog)s ' + __version__ )
parser . add_argument ( "-v" , "--verbose" , dest = 'Verbose' , action = "store_true" ,
help = "Turn on verbose output with informational messages printed, including capsule headers and warning messages." )
parser . add_argument ( "-q" , "--quiet" , dest = 'Quiet' , action = "store_true" ,
help = "Disable all messages except fatal errors." )
parser . add_argument ( "--debug" , dest = 'Debug' , type = int , metavar = '[0-9]' , choices = range ( 0 , 10 ), default = 0 ,
help = "Set debug level" )
parser . add_argument ( "--update-image-index" , dest = 'UpdateImageIndex' , type = ValidateUnsignedInteger , default = 0x01 , help = "unique number identifying the firmware image within the device " )
#
# Parse command line arguments
#
args = parser . parse_args ()
#
# Read binary input file
#
Buffer = ''
if args . InputFile :
if os . path . getsize ( args . InputFile . name ) == 0 :
print ( 'GenerateCapsule: error: InputFile {File} is empty' . format ( File = args . InputFile . name ))
sys . exit ( 1 )
try :
if args . Verbose :
print ( 'Read binary input file {File} ' . format ( File = args . InputFile . name ))
Buffer = args . InputFile . read ()
args . InputFile . close ()
except :
print ( 'GenerateCapsule: error: can not read binary input file {File} ' . format ( File = args . InputFile . name ))
sys . exit ( 1 )
#
# Create objects
#
UefiCapsuleHeader = UefiCapsuleHeaderClass ()
FmpCapsuleHeader = FmpCapsuleHeaderClass ()
FmpAuthHeader = FmpAuthHeaderClass ()
FmpPayloadHeader = FmpPayloadHeaderClass ()
2020-01-10 09:57:35 +08:00
CapsuleDependency = CapsuleDependencyClass ()
2019-06-26 13:55:35 +08:00
EmbeddedDriverDescriptorList = []
PayloadDescriptorList = []
PayloadJsonDescriptorList = []
#
#Encode Operation
#
if args . Encode :
Encode ( PayloadDescriptorList , EmbeddedDriverDescriptorList , Buffer )
#
#Decode Operation
#
if args . Decode :
Decode ( PayloadDescriptorList , PayloadJsonDescriptorList , Buffer )
#
#Dump Info Operation
#
if args . DumpInfo :
DumpInfo ( Buffer , args )
2018-05-01 20:54:46 -07:00
if args . Verbose :
print ( 'Success' )