REF: https://bugzilla.tianocore.org/show_bug.cgi?id=2412 Capsule generate tool support encode capsule dependencies through '-j' command with a JSON file. To enable dependency feature, "Dependencies" field for each payload in JSON file is required. The value of "Dependencies" field is C style infix notation expression. For example: "Dependencies":"72E2945A-00DA-448E-9AA7-075AD840F9D4 > 0x00000001" The relation of Dependency Expression Opcode in UEFI2.8 chap 23.2 and infix notation expression value is as follows: +-----------------------------+--------------------------+ | OPCODE | INFIX EXPRESSION VALUE | +-----------------------------+--------------------------+ | 0x00 (PUSH_GUID) | {GUID} | | 0x01 (PUSH_VERSION) | {UINT32} | | 0x02 (DECLEAR_VERSION_NAME} | DECLEAR "{VERSION_NAME}" | | 0x03 (AND) | && | | 0x04 (OR) | || | | 0x05 (NOT) | ~ | | 0x06 (TRUE) | TRUE | | 0x07 (FALSE) | FALSE | | 0x08 (EQ) | == | | 0x09 (GT) | > | | 0x0A (GTE) | >= | | 0x0B (LT) | < | | 0x0C (LTE) | <= | +-----------------------------+--------------------------+ Cc: Bob Feng <bob.c.feng@intel.com> Cc: Liming Gao <liming.gao@intel.com> Signed-off-by: Aaron Li <aaron.li@intel.com> Reviewed-by: Bob Feng <bob.c.feng@intel.com>
		
			
				
	
	
		
			1049 lines
		
	
	
		
			57 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			1049 lines
		
	
	
		
			57 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.Uefi.Capsule.CapsuleDependency import CapsuleDependencyClass
 | |
| 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)
 | |
|             DepexExp                     = ConvertJsonValue (Config, 'Dependencies', str, 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,
 | |
|                                             DepexExp
 | |
|                                             ))
 | |
| 
 | |
|     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),
 | |
|                                   "Dependencies" : str(PayloadDescriptor.DepexExp)
 | |
|                               }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,
 | |
|                      DepexExp                     = 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.DepexExp                     = DepexExp
 | |
| 
 | |
|             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)
 | |
|             self.UseDependency = self.DepexExp 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,
 | |
|                                             None
 | |
|                                             ))
 | |
|         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.UseDependency:
 | |
|                 CapsuleDependency.Payload = Result
 | |
|                 CapsuleDependency.DepexExp = SinglePayloadDescriptor.DepexExp
 | |
|                 Result = CapsuleDependency.Encode ()
 | |
|                 if args.Verbose:
 | |
|                     CapsuleDependency.DumpInfo ()
 | |
|             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,
 | |
|                                             None
 | |
|                                             ))
 | |
|         #
 | |
|         # 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,
 | |
|                                                                 None
 | |
|                                                                 ))
 | |
|                 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,
 | |
|                                                             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,
 | |
|                                                             None
 | |
|                                                             ))
 | |
|                 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')
 | |
| 
 | |
|                     PayloadSignature = struct.unpack ('<I', SinglePayloadDescriptor.Payload[0:4])
 | |
|                     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')
 | |
| 
 | |
|                     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')
 | |
| 
 | |
|                     PayloadSignature = struct.unpack ('<I', Result[0:4])
 | |
|                     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')
 | |
|                     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 ()
 | |
|     CapsuleDependency = CapsuleDependencyClass ()
 | |
| 
 | |
|     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')
 |