1## @file 2# This tool encodes and decodes GUIDed FFS sections or FMP capsule for a GUID type of 3# EFI_CERT_TYPE_RSA2048_SHA256_GUID defined in the UEFI 2.4 Specification as 4# {0xa7717414, 0xc616, 0x4977, {0x94, 0x20, 0x84, 0x47, 0x12, 0xa7, 0x35, 0xbf}} 5# This tool has been tested with OpenSSL 1.0.1e 11 Feb 2013 6# 7# Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.<BR> 8# This program and the accompanying materials 9# are licensed and made available under the terms and conditions of the BSD License 10# which accompanies this distribution. The full text of the license may be found at 11# http://opensource.org/licenses/bsd-license.php 12# 13# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 14# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 15# 16 17''' 18Rsa2048Sha256Sign 19''' 20 21import os 22import sys 23import argparse 24import subprocess 25import uuid 26import struct 27import collections 28from Common.BuildVersion import gBUILD_VERSION 29 30# 31# Globals for help information 32# 33__prog__ = 'Rsa2048Sha256Sign' 34__version__ = '%s Version %s' % (__prog__, '0.9 ' + gBUILD_VERSION) 35__copyright__ = 'Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.' 36__usage__ = '%s -e|-d [options] <input_file>' % (__prog__) 37 38# 39# GUID for SHA 256 Hash Algorithm from UEFI Specification 40# 41EFI_HASH_ALGORITHM_SHA256_GUID = uuid.UUID('{51aa59de-fdf2-4ea3-bc63-875fb7842ee9}') 42 43# 44# Structure defintion to unpack EFI_CERT_BLOCK_RSA_2048_SHA256 from UEFI 2.4 Specification 45# 46# typedef struct _EFI_CERT_BLOCK_RSA_2048_SHA256 { 47# EFI_GUID HashType; 48# UINT8 PublicKey[256]; 49# UINT8 Signature[256]; 50# } EFI_CERT_BLOCK_RSA_2048_SHA256; 51# 52EFI_CERT_BLOCK_RSA_2048_SHA256 = collections.namedtuple('EFI_CERT_BLOCK_RSA_2048_SHA256', ['HashType','PublicKey','Signature']) 53EFI_CERT_BLOCK_RSA_2048_SHA256_STRUCT = struct.Struct('16s256s256s') 54 55# 56# Filename of test signing private key that is stored in same directory as this tool 57# 58TEST_SIGNING_PRIVATE_KEY_FILENAME = 'TestSigningPrivateKey.pem' 59 60if __name__ == '__main__': 61 # 62 # Create command line argument parser object 63 # 64 parser = argparse.ArgumentParser(prog=__prog__, version=__version__, usage=__usage__, description=__copyright__, conflict_handler='resolve') 65 group = parser.add_mutually_exclusive_group(required=True) 66 group.add_argument("-e", action="store_true", dest='Encode', help='encode file') 67 group.add_argument("-d", action="store_true", dest='Decode', help='decode file') 68 parser.add_argument("-o", "--output", dest='OutputFile', type=str, metavar='filename', help="specify the output filename", required=True) 69 parser.add_argument("--monotonic-count", dest='MonotonicCountStr', type=str, help="specify the MonotonicCount in FMP capsule.") 70 parser.add_argument("--private-key", dest='PrivateKeyFile', type=argparse.FileType('rb'), help="specify the private key filename. If not specified, a test signing key is used.") 71 parser.add_argument("-v", "--verbose", dest='Verbose', action="store_true", help="increase output messages") 72 parser.add_argument("-q", "--quiet", dest='Quiet', action="store_true", help="reduce output messages") 73 parser.add_argument("--debug", dest='Debug', type=int, metavar='[0-9]', choices=range(0,10), default=0, help="set debug level") 74 parser.add_argument(metavar="input_file", dest='InputFile', type=argparse.FileType('rb'), help="specify the input filename") 75 76 # 77 # Parse command line arguments 78 # 79 args = parser.parse_args() 80 81 # 82 # Generate file path to Open SSL command 83 # 84 OpenSslCommand = 'openssl' 85 try: 86 OpenSslPath = os.environ['OPENSSL_PATH'] 87 OpenSslCommand = os.path.join(OpenSslPath, OpenSslCommand) 88 except: 89 pass 90 91 # 92 # Verify that Open SSL command is available 93 # 94 try: 95 Process = subprocess.Popen('%s version' % (OpenSslCommand), stdout=subprocess.PIPE, stderr=subprocess.PIPE) 96 except: 97 print 'ERROR: Open SSL command not available. Please verify PATH or set OPENSSL_PATH' 98 sys.exit(1) 99 100 Version = Process.communicate() 101 if Process.returncode <> 0: 102 print 'ERROR: Open SSL command not available. Please verify PATH or set OPENSSL_PATH' 103 sys.exit(Process.returncode) 104 print Version[0] 105 106 # 107 # Read input file into a buffer and save input filename 108 # 109 args.InputFileName = args.InputFile.name 110 args.InputFileBuffer = args.InputFile.read() 111 args.InputFile.close() 112 113 # 114 # Save output filename and check if path exists 115 # 116 OutputDir = os.path.dirname(args.OutputFile) 117 if not os.path.exists(OutputDir): 118 print 'ERROR: The output path does not exist: %s' % OutputDir 119 sys.exit(1) 120 args.OutputFileName = args.OutputFile 121 122 # 123 # Save private key filename and close private key file 124 # 125 try: 126 args.PrivateKeyFileName = args.PrivateKeyFile.name 127 args.PrivateKeyFile.close() 128 except: 129 try: 130 # 131 # Get path to currently executing script or executable 132 # 133 if hasattr(sys, 'frozen'): 134 RsaToolPath = sys.executable 135 else: 136 RsaToolPath = sys.argv[0] 137 if RsaToolPath.startswith('"'): 138 RsaToolPath = RsaToolPath[1:] 139 if RsaToolPath.endswith('"'): 140 RsaToolPath = RsaToolPath[:-1] 141 args.PrivateKeyFileName = os.path.join(os.path.dirname(os.path.realpath(RsaToolPath)), TEST_SIGNING_PRIVATE_KEY_FILENAME) 142 args.PrivateKeyFile = open(args.PrivateKeyFileName, 'rb') 143 args.PrivateKeyFile.close() 144 except: 145 print 'ERROR: test signing private key file %s missing' % (args.PrivateKeyFileName) 146 sys.exit(1) 147 148 # 149 # Extract public key from private key into STDOUT 150 # 151 Process = subprocess.Popen('%s rsa -in "%s" -modulus -noout' % (OpenSslCommand, args.PrivateKeyFileName), stdout=subprocess.PIPE, stderr=subprocess.PIPE) 152 PublicKeyHexString = Process.communicate()[0].split('=')[1].strip() 153 PublicKey = '' 154 while len(PublicKeyHexString) > 0: 155 PublicKey = PublicKey + chr(int(PublicKeyHexString[0:2],16)) 156 PublicKeyHexString=PublicKeyHexString[2:] 157 if Process.returncode <> 0: 158 sys.exit(Process.returncode) 159 160 if args.MonotonicCountStr: 161 try: 162 if args.MonotonicCountStr.upper().startswith('0X'): 163 args.MonotonicCountValue = (long)(args.MonotonicCountStr, 16) 164 else: 165 args.MonotonicCountValue = (long)(args.MonotonicCountStr) 166 except: 167 pass 168 169 if args.Encode: 170 FullInputFileBuffer = args.InputFileBuffer 171 if args.MonotonicCountStr: 172 format = "%dsQ" % len(args.InputFileBuffer) 173 FullInputFileBuffer = struct.pack(format, args.InputFileBuffer, args.MonotonicCountValue) 174 # 175 # Sign the input file using the specified private key and capture signature from STDOUT 176 # 177 Process = subprocess.Popen('%s sha256 -sign "%s"' % (OpenSslCommand, args.PrivateKeyFileName), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 178 Signature = Process.communicate(input=FullInputFileBuffer)[0] 179 if Process.returncode <> 0: 180 sys.exit(Process.returncode) 181 182 # 183 # Write output file that contains hash GUID, Public Key, Signature, and Input data 184 # 185 args.OutputFile = open(args.OutputFileName, 'wb') 186 args.OutputFile.write(EFI_HASH_ALGORITHM_SHA256_GUID.get_bytes_le()) 187 args.OutputFile.write(PublicKey) 188 args.OutputFile.write(Signature) 189 args.OutputFile.write(args.InputFileBuffer) 190 args.OutputFile.close() 191 192 if args.Decode: 193 # 194 # Parse Hash Type, Public Key, and Signature from the section header 195 # 196 Header = EFI_CERT_BLOCK_RSA_2048_SHA256._make(EFI_CERT_BLOCK_RSA_2048_SHA256_STRUCT.unpack_from(args.InputFileBuffer)) 197 args.InputFileBuffer = args.InputFileBuffer[EFI_CERT_BLOCK_RSA_2048_SHA256_STRUCT.size:] 198 199 # 200 # Verify that the Hash Type matches the expected SHA256 type 201 # 202 if uuid.UUID(bytes_le = Header.HashType) <> EFI_HASH_ALGORITHM_SHA256_GUID: 203 print 'ERROR: unsupport hash GUID' 204 sys.exit(1) 205 206 # 207 # Verify the public key 208 # 209 if Header.PublicKey <> PublicKey: 210 print 'ERROR: Public key in input file does not match public key from private key file' 211 sys.exit(1) 212 213 FullInputFileBuffer = args.InputFileBuffer 214 if args.MonotonicCountStr: 215 format = "%dsQ" % len(args.InputFileBuffer) 216 FullInputFileBuffer = struct.pack(format, args.InputFileBuffer, args.MonotonicCountValue) 217 218 # 219 # Write Signature to output file 220 # 221 open(args.OutputFileName, 'wb').write(Header.Signature) 222 223 # 224 # Verify signature 225 # 226 Process = subprocess.Popen('%s sha256 -prverify "%s" -signature %s' % (OpenSslCommand, args.PrivateKeyFileName, args.OutputFileName), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 227 Process.communicate(input=FullInputFileBuffer) 228 if Process.returncode <> 0: 229 print 'ERROR: Verification failed' 230 os.remove (args.OutputFileName) 231 sys.exit(Process.returncode) 232 233 # 234 # Save output file contents from input file 235 # 236 open(args.OutputFileName, 'wb').write(args.InputFileBuffer) 237