mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-24 11:45:27 +01:00
1001 lines
54 KiB
Python
1001 lines
54 KiB
Python
|
## @file
|
||
|
# Generate a capsule.
|
||
|
#
|
||
|
# This tool generates a UEFI Capsule around an FMP Capsule. The capsule payload
|
||
|
# 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
|
||
|
# system firmware or device firmware for integrated devices. In order to
|
||
|
# keep the tool as simple as possible, it has the following limitations:
|
||
|
# * Do not support vendor code bytes in a capsule.
|
||
|
#
|
||
|
# Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
|
||
|
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||
|
#
|
||
|
|
||
|
'''
|
||
|
GenerateCapsule
|
||
|
'''
|
||
|
|
||
|
import sys
|
||
|
import argparse
|
||
|
import uuid
|
||
|
import struct
|
||
|
import subprocess
|
||
|
import os
|
||
|
import tempfile
|
||
|
import shutil
|
||
|
import platform
|
||
|
import json
|
||
|
from Common.Uefi.Capsule.UefiCapsuleHeader import UefiCapsuleHeaderClass
|
||
|
from Common.Uefi.Capsule.FmpCapsuleHeader import FmpCapsuleHeaderClass
|
||
|
from Common.Uefi.Capsule.FmpAuthHeader import FmpAuthHeaderClass
|
||
|
from Common.Edk2.Capsule.FmpPayloadHeader import FmpPayloadHeaderClass
|
||
|
|
||
|
#
|
||
|
# Globals for help information
|
||
|
#
|
||
|
__prog__ = 'GenerateCapsule'
|
||
|
__version__ = '0.9'
|
||
|
__copyright__ = 'Copyright (c) 2018, Intel Corporation. All rights reserved.'
|
||
|
__description__ = 'Generate a capsule.\n'
|
||
|
|
||
|
def SignPayloadSignTool (Payload, ToolPath, PfxFile, Verbose = False):
|
||
|
#
|
||
|
# 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:
|
||
|
with open (TempFileName, 'wb') as File:
|
||
|
File.write (Payload)
|
||
|
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'))
|
||
|
Command = Command + 'sign /fd sha256 /p7ce DetachedSignedData /p7co 1.2.840.113549.1.7.2 '
|
||
|
Command = Command + '/p7 {TempDir} '.format (TempDir = TempDirectoryName)
|
||
|
Command = Command + '/f {PfxFile} '.format (PfxFile = PfxFile)
|
||
|
Command = Command + TempFileName
|
||
|
if Verbose:
|
||
|
print (Command)
|
||
|
|
||
|
#
|
||
|
# 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)
|
||
|
print (Result[1].decode())
|
||
|
raise ValueError ('GenerateCapsule: error: signtool failed.')
|
||
|
|
||
|
#
|
||
|
# Read the signature from the generated output file
|
||
|
#
|
||
|
try:
|
||
|
with open (TempFileName + '.p7', 'rb') as File:
|
||
|
Signature = File.read ()
|
||
|
except:
|
||
|
shutil.rmtree (TempDirectoryName)
|
||
|
raise ValueError ('GenerateCapsule: error: can not read signature file.')
|
||
|
|
||
|
shutil.rmtree (TempDirectoryName)
|
||
|
return Signature
|
||
|
|
||
|
def VerifyPayloadSignTool (Payload, CertData, ToolPath, PfxFile, Verbose = False):
|
||
|
print ('signtool verify is not supported.')
|
||
|
raise ValueError ('GenerateCapsule: error: signtool verify is not supported.')
|
||
|
|
||
|
def SignPayloadOpenSsl (Payload, ToolPath, SignerPrivateCertFile, OtherPublicCertFile, TrustedPublicCertFile, Verbose = False):
|
||
|
#
|
||
|
# Build openssl command
|
||
|
#
|
||
|
if ToolPath is None:
|
||
|
ToolPath = ''
|
||
|
Command = ''
|
||
|
Command = Command + '"{Path}" '.format (Path = os.path.join (ToolPath, 'openssl'))
|
||
|
Command = Command + 'smime -sign -binary -outform DER -md sha256 '
|
||
|
Command = Command + '-signer "{Private}" -certfile "{Public}"'.format (Private = SignerPrivateCertFile, Public = OtherPublicCertFile)
|
||
|
if Verbose:
|
||
|
print (Command)
|
||
|
|
||
|
#
|
||
|
# 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)
|
||
|
Signature = Result[0]
|
||
|
except:
|
||
|
raise ValueError ('GenerateCapsule: error: can not run openssl.')
|
||
|
|
||
|
if Process.returncode != 0:
|
||
|
print (Result[1].decode())
|
||
|
raise ValueError ('GenerateCapsule: error: openssl failed.')
|
||
|
|
||
|
return Signature
|
||
|
|
||
|
def VerifyPayloadOpenSsl (Payload, CertData, ToolPath, SignerPrivateCertFile, OtherPublicCertFile, TrustedPublicCertFile, Verbose = False):
|
||
|
#
|
||
|
# 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:
|
||
|
with open (TempFileName, 'wb') as File:
|
||
|
File.write (Payload)
|
||
|
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)
|
||
|
if Verbose:
|
||
|
print (Command)
|
||
|
|
||
|
#
|
||
|
# 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)
|
||
|
print (Result[1].decode())
|
||
|
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
|
||
|
|
||
|
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:
|
||
|
try:
|
||
|
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)
|
||
|
SignToolPfxFile = ConvertJsonValue (Config, 'SignToolPfxFile', os.path.expandvars, Required = False, Default = None, Open = True)
|
||
|
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,
|
||
|
SignToolPfxFile,
|
||
|
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)
|
||
|
SignToolPfxFile = ConvertJsonValue (Config, 'SignToolPfxFile', os.path.expandvars, Required = False, Default = None, Open = True)
|
||
|
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)
|
||
|
|
||
|
#
|
||
|
# 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,
|
||
|
SignToolPfxFile,
|
||
|
OpenSslSignerPrivateCertFile,
|
||
|
OpenSslOtherPublicCertFile,
|
||
|
OpenSslTrustedPublicCertFile,
|
||
|
SigningToolPath
|
||
|
))
|
||
|
|
||
|
def GenerateOutputJson (PayloadJsonDescriptorList):
|
||
|
PayloadJson = {
|
||
|
"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),
|
||
|
"SignToolPfxFile": str(PayloadDescriptor.SignToolPfxFile),
|
||
|
"OpenSslSignerPrivateCertFile": str(PayloadDescriptor.OpenSslSignerPrivateCertFile),
|
||
|
"OpenSslOtherPublicCertFile": str(PayloadDescriptor.OpenSslOtherPublicCertFile),
|
||
|
"OpenSslTrustedPublicCertFile": str(PayloadDescriptor.OpenSslTrustedPublicCertFile),
|
||
|
"SigningToolPath": str(PayloadDescriptor.SigningToolPath)
|
||
|
}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']
|
||
|
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']
|
||
|
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)
|
||
|
if args.SignToolPfxFile:
|
||
|
print ('GenerateCapsule: error: Argument --pfx-file conflicts with Argument -j')
|
||
|
sys.exit (1)
|
||
|
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,
|
||
|
SignToolPfxFile = None,
|
||
|
OpenSslSignerPrivateCertFile = None,
|
||
|
OpenSslOtherPublicCertFile = None,
|
||
|
OpenSslTrustedPublicCertFile = None,
|
||
|
SigningToolPath = None
|
||
|
):
|
||
|
self.Payload = Payload
|
||
|
self.Guid = Guid
|
||
|
self.FwVersion = FwVersion
|
||
|
self.LowestSupportedVersion = LowestSupportedVersion
|
||
|
self.MonotonicCount = MonotonicCount
|
||
|
self.HardwareInstance = HardwareInstance
|
||
|
self.UpdateImageIndex = UpdateImageIndex
|
||
|
self.SignToolPfxFile = SignToolPfxFile
|
||
|
self.OpenSslSignerPrivateCertFile = OpenSslSignerPrivateCertFile
|
||
|
self.OpenSslOtherPublicCertFile = OpenSslOtherPublicCertFile
|
||
|
self.OpenSslTrustedPublicCertFile = OpenSslTrustedPublicCertFile
|
||
|
self.SigningToolPath = SigningToolPath
|
||
|
|
||
|
self.UseSignTool = self.SignToolPfxFile is not None
|
||
|
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)
|
||
|
|
||
|
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')
|
||
|
if self.UpdateImageIndex >0xFF:
|
||
|
if args.JsonFile:
|
||
|
raise argparse.ArgumentTypeError ('JSON field UpdateImageIndex must be an integer in range 0x0..0xff')
|
||
|
else:
|
||
|
raise argparse.ArgumentTypeError ('--update-image-index must be an integer in range 0x0..0xff')
|
||
|
|
||
|
if self.UseSignTool:
|
||
|
self.SignToolPfxFile.close()
|
||
|
self.SignToolPfxFile = self.SignToolPfxFile.name
|
||
|
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,
|
||
|
args.SignToolPfxFile,
|
||
|
args.OpenSslSignerPrivateCertFile,
|
||
|
args.OpenSslOtherPublicCertFile,
|
||
|
args.OpenSslTrustedPublicCertFile,
|
||
|
args.SigningToolPath
|
||
|
))
|
||
|
for SinglePayloadDescriptor in PayloadDescriptorList:
|
||
|
try:
|
||
|
SinglePayloadDescriptor.Validate (args)
|
||
|
except Exception as Msg:
|
||
|
print ('GenerateCapsule: error:' + str(Msg))
|
||
|
sys.exit (1)
|
||
|
for SinglePayloadDescriptor in PayloadDescriptorList:
|
||
|
Result = SinglePayloadDescriptor.Payload
|
||
|
try:
|
||
|
FmpPayloadHeader.FwVersion = SinglePayloadDescriptor.FwVersion
|
||
|
FmpPayloadHeader.LowestSupportedVersion = SinglePayloadDescriptor.LowestSupportedVersion
|
||
|
FmpPayloadHeader.Payload = SinglePayloadDescriptor.Payload
|
||
|
Result = FmpPayloadHeader.Encode ()
|
||
|
if args.Verbose:
|
||
|
FmpPayloadHeader.DumpInfo ()
|
||
|
except:
|
||
|
print ('GenerateCapsule: error: can not encode FMP Payload Header')
|
||
|
sys.exit (1)
|
||
|
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,
|
||
|
Verbose = args.Verbose
|
||
|
)
|
||
|
else:
|
||
|
CertData = SignPayloadOpenSsl (
|
||
|
Result + struct.pack ('<Q', SinglePayloadDescriptor.MonotonicCount),
|
||
|
SinglePayloadDescriptor.SigningToolPath,
|
||
|
SinglePayloadDescriptor.OpenSslSignerPrivateCertFile,
|
||
|
SinglePayloadDescriptor.OpenSslOtherPublicCertFile,
|
||
|
SinglePayloadDescriptor.OpenSslTrustedPublicCertFile,
|
||
|
Verbose = args.Verbose
|
||
|
)
|
||
|
except Exception as Msg:
|
||
|
print ('GenerateCapsule: error: can not sign payload \n' + str(Msg))
|
||
|
sys.exit (1)
|
||
|
|
||
|
try:
|
||
|
FmpAuthHeader.MonotonicCount = SinglePayloadDescriptor.MonotonicCount
|
||
|
FmpAuthHeader.CertData = CertData
|
||
|
FmpAuthHeader.Payload = Result
|
||
|
Result = FmpAuthHeader.Encode ()
|
||
|
if args.Verbose:
|
||
|
FmpAuthHeader.DumpInfo ()
|
||
|
except:
|
||
|
print ('GenerateCapsule: error: can not encode FMP Auth Header')
|
||
|
sys.exit (1)
|
||
|
FmpCapsuleHeader.AddPayload (SinglePayloadDescriptor.Guid, Result, HardwareInstance = SinglePayloadDescriptor.HardwareInstance, UpdateImageIndex = SinglePayloadDescriptor.UpdateImageIndex)
|
||
|
try:
|
||
|
for EmbeddedDriver in EmbeddedDriverDescriptorList:
|
||
|
FmpCapsuleHeader.AddEmbeddedDriver(EmbeddedDriver)
|
||
|
|
||
|
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
|
||
|
UefiCapsuleHeader.PopulateSystemTable = False
|
||
|
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)
|
||
|
|
||
|
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,
|
||
|
args.SignToolPfxFile,
|
||
|
args.OpenSslSignerPrivateCertFile,
|
||
|
args.OpenSslOtherPublicCertFile,
|
||
|
args.OpenSslTrustedPublicCertFile,
|
||
|
args.SigningToolPath
|
||
|
))
|
||
|
#
|
||
|
# Perform additional verification on payload descriptors
|
||
|
#
|
||
|
for SinglePayloadDescriptor in PayloadDescriptorList:
|
||
|
try:
|
||
|
SinglePayloadDescriptor.Validate (args)
|
||
|
except Exception as Msg:
|
||
|
print ('GenerateCapsule: error:' + str(Msg))
|
||
|
sys.exit (1)
|
||
|
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,
|
||
|
PayloadDescriptorList[Index].SignToolPfxFile,
|
||
|
PayloadDescriptorList[Index].OpenSslSignerPrivateCertFile,
|
||
|
PayloadDescriptorList[Index].OpenSslOtherPublicCertFile,
|
||
|
PayloadDescriptorList[Index].OpenSslTrustedPublicCertFile,
|
||
|
PayloadDescriptorList[Index].SigningToolPath
|
||
|
))
|
||
|
else:
|
||
|
PayloadDescriptorList[0].Payload = FmpCapsuleHeader.GetFmpCapsuleImageHeader (0).Payload
|
||
|
for Index in range (0, FmpCapsuleHeader.PayloadItemCount):
|
||
|
if Index > 0:
|
||
|
PayloadDecodeFile = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).Payload
|
||
|
PayloadDescriptorList.append (PayloadDescriptor (PayloadDecodeFile,
|
||
|
None,
|
||
|
None,
|
||
|
None,
|
||
|
None,
|
||
|
None,
|
||
|
None,
|
||
|
None,
|
||
|
None,
|
||
|
None,
|
||
|
None,
|
||
|
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,
|
||
|
PayloadDescriptorList[Index].SignToolPfxFile,
|
||
|
PayloadDescriptorList[Index].OpenSslSignerPrivateCertFile,
|
||
|
PayloadDescriptorList[Index].OpenSslOtherPublicCertFile,
|
||
|
PayloadDescriptorList[Index].OpenSslTrustedPublicCertFile,
|
||
|
PayloadDescriptorList[Index].SigningToolPath
|
||
|
))
|
||
|
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,
|
||
|
Verbose = args.Verbose
|
||
|
)
|
||
|
else:
|
||
|
CertData = VerifyPayloadOpenSsl (
|
||
|
FmpAuthHeader.Payload + struct.pack ('<Q', FmpAuthHeader.MonotonicCount),
|
||
|
FmpAuthHeader.CertData,
|
||
|
SinglePayloadDescriptor.SigningToolPath,
|
||
|
SinglePayloadDescriptor.OpenSslSignerPrivateCertFile,
|
||
|
SinglePayloadDescriptor.OpenSslOtherPublicCertFile,
|
||
|
SinglePayloadDescriptor.OpenSslTrustedPublicCertFile,
|
||
|
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')
|
||
|
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)
|
||
|
|
||
|
except:
|
||
|
print ('GenerateCapsule: error: can not decode capsule')
|
||
|
sys.exit (1)
|
||
|
GenerateOutputJson(PayloadJsonDescriptorList)
|
||
|
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')
|
||
|
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.")
|
||
|
|
||
|
parser.add_argument ("--pfx-file", dest='SignToolPfxFile', type=argparse.FileType('rb'),
|
||
|
help="signtool PFX certificate filename.")
|
||
|
|
||
|
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 ()
|
||
|
|
||
|
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)
|
||
|
|
||
|
if args.Verbose:
|
||
|
print('Success')
|