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