]>
git.proxmox.com Git - mirror_edk2.git/blob - IntelFsp2Pkg/Tools/ConfigEditor/SingleSign.py
3 # Single signing script
5 # Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
6 # SPDX-License-Identifier: BSD-2-Clause-Patent
17 # Key Id | Key File Name start |
18 # =================================================================
19 # KEY_ID_MASTER is used for signing Slimboot Key Hash Manifest \
20 # container (KEYH Component)
21 "KEY_ID_MASTER_RSA2048": "MasterTestKey_Priv_RSA2048.pem",
22 "KEY_ID_MASTER_RSA3072": "MasterTestKey_Priv_RSA3072.pem",
24 # KEY_ID_CFGDATA is used for signing external Config data blob)
25 "KEY_ID_CFGDATA_RSA2048": "ConfigTestKey_Priv_RSA2048.pem",
26 "KEY_ID_CFGDATA_RSA3072": "ConfigTestKey_Priv_RSA3072.pem",
28 # KEY_ID_FIRMWAREUPDATE is used for signing capsule firmware update image)
29 "KEY_ID_FIRMWAREUPDATE_RSA2048": "FirmwareUpdateTestKey_Priv_RSA2048.pem",
30 "KEY_ID_FIRMWAREUPDATE_RSA3072": "FirmwareUpdateTestKey_Priv_RSA3072.pem",
32 # KEY_ID_CONTAINER is used for signing container header with mono signature
33 "KEY_ID_CONTAINER_RSA2048": "ContainerTestKey_Priv_RSA2048.pem",
34 "KEY_ID_CONTAINER_RSA3072": "ContainerTestKey_Priv_RSA3072.pem",
36 # CONTAINER_COMP1_KEY_ID is used for signing container components
37 "KEY_ID_CONTAINER_COMP_RSA2048": "ContainerCompTestKey_Priv_RSA2048.pem",
38 "KEY_ID_CONTAINER_COMP_RSA3072": "ContainerCompTestKey_Priv_RSA3072.pem",
40 # KEY_ID_OS1_PUBLIC, KEY_ID_OS2_PUBLIC is used for referencing \
42 "KEY_ID_OS1_PUBLIC_RSA2048": "OS1_TestKey_Pub_RSA2048.pem",
43 "KEY_ID_OS1_PUBLIC_RSA3072": "OS1_TestKey_Pub_RSA3072.pem",
45 "KEY_ID_OS2_PUBLIC_RSA2048": "OS2_TestKey_Pub_RSA2048.pem",
46 "KEY_ID_OS2_PUBLIC_RSA3072": "OS2_TestKey_Pub_RSA3072.pem",
50 MESSAGE_SBL_KEY_DIR
= """!!! PRE-REQUISITE: Path to SBL_KEY_DIR has.
51 to be set with SBL KEYS DIRECTORY !!! \n!!! Generate keys.
52 using GenerateKeys.py available in BootloaderCorePkg/Tools.
53 directory !!! \n !!! Run $python.
54 BootloaderCorePkg/Tools/GenerateKeys.py -k $PATH_TO_SBL_KEY_DIR !!!\n
55 !!! Set SBL_KEY_DIR environ with path to SBL KEYS DIR !!!\n"
56 !!! Windows $set SBL_KEY_DIR=$PATH_TO_SBL_KEY_DIR !!!\n
57 !!! Linux $export SBL_KEY_DIR=$PATH_TO_SBL_KEY_DIR !!!\n"""
60 def get_openssl_path():
62 if 'OPENSSL_PATH' not in os
.environ
:
63 openssl_dir
= "C:\\Openssl\\bin\\"
64 if os
.path
.exists(openssl_dir
):
65 os
.environ
['OPENSSL_PATH'] = openssl_dir
67 os
.environ
['OPENSSL_PATH'] = "C:\\Openssl\\"
68 if 'OPENSSL_CONF' not in os
.environ
:
69 openssl_cfg
= "C:\\Openssl\\openssl.cfg"
70 if os
.path
.exists(openssl_cfg
):
71 os
.environ
['OPENSSL_CONF'] = openssl_cfg
72 openssl
= os
.path
.join(
73 os
.environ
.get('OPENSSL_PATH', ''),
76 # Get openssl path for Linux cases
77 openssl
= shutil
.which('openssl')
82 def run_process(arg_list
, print_cmd
=False, capture_out
=False):
85 print(' '.join(arg_list
))
92 output
= subprocess
.check_output(arg_list
).decode()
94 result
= subprocess
.call(arg_list
)
95 except Exception as ex
:
101 print('Error in running process:\n %s' % ' '.join(arg_list
))
110 def check_file_pem_format(priv_key
):
111 # Check for file .pem format
112 key_name
= os
.path
.basename(priv_key
)
113 if os
.path
.splitext(key_name
)[1] == ".pem":
119 def get_key_id(priv_key
):
120 # Extract base name if path is provided.
121 key_name
= os
.path
.basename(priv_key
)
122 # Check for KEY_ID in key naming.
123 if key_name
.startswith('KEY_ID'):
129 def get_sbl_key_dir():
130 # Check Key store setting SBL_KEY_DIR path
131 if 'SBL_KEY_DIR' not in os
.environ
:
132 exception_string
= "ERROR: SBL_KEY_DIR is not defined." \
133 " Set SBL_KEY_DIR with SBL Keys directory!!\n"
134 raise Exception(exception_string
+ MESSAGE_SBL_KEY_DIR
)
136 sbl_key_dir
= os
.environ
.get('SBL_KEY_DIR')
137 if not os
.path
.exists(sbl_key_dir
):
138 exception_string
= "ERROR:SBL_KEY_DIR set " + sbl_key_dir \
140 " Set the correct SBL_KEY_DIR path !!\n" \
141 + MESSAGE_SBL_KEY_DIR
142 raise Exception(exception_string
)
147 def get_key_from_store(in_key
):
149 # Check in_key is path to key
150 if os
.path
.exists(in_key
):
153 # Get Slimboot key dir path
154 sbl_key_dir
= get_sbl_key_dir()
156 # Extract if in_key is key_id
157 priv_key
= get_key_id(in_key
)
158 if priv_key
is not None:
159 if (priv_key
in SIGNING_KEY
):
160 # Generate key file name from key id
161 priv_key_file
= SIGNING_KEY
[priv_key
]
163 exception_string
= "KEY_ID" + priv_key
+ "is not found " \
164 "is not found in supported KEY IDs!!"
165 raise Exception(exception_string
)
166 elif check_file_pem_format(in_key
):
167 # check if file name is provided in pem format
168 priv_key_file
= in_key
171 raise Exception('key provided %s is not valid!' % in_key
)
174 # Join Key Dir and priv_key_file
176 priv_key
= os
.path
.join(sbl_key_dir
, priv_key_file
)
178 raise Exception('priv_key is not found %s!' % priv_key
)
180 # Check for priv_key construted based on KEY ID exists in specified path
181 if not os
.path
.isfile(priv_key
):
182 exception_string
= "!!! ERROR: Key file corresponding to" \
183 + in_key
+ "do not exist in Sbl key " \
184 "directory at" + sbl_key_dir
+ "!!! \n" \
185 + MESSAGE_SBL_KEY_DIR
186 raise Exception(exception_string
)
191 # Sign an file using openssl
193 # priv_key [Input] Key Id or Path to Private key
194 # hash_type [Input] Signing hash
195 # sign_scheme[Input] Sign/padding scheme
196 # in_file [Input] Input file to be signed
197 # out_file [Input/Output] Signed data file
201 def single_sign_file(priv_key
, hash_type
, sign_scheme
, in_file
, out_file
):
203 _hash_type_string
= {
204 "SHA2_256": 'sha256',
205 "SHA2_384": 'sha384',
206 "SHA2_512": 'sha512',
209 _hash_digest_Size
= {
210 # Hash_string : Hash_Size
217 _sign_scheme_string
= {
218 "RSA_PKCS1": 'pkcs1',
222 priv_key
= get_key_from_store(priv_key
)
224 # Temporary files to store hash generated
225 hash_file_tmp
= out_file
+'.hash.tmp'
226 hash_file
= out_file
+'.hash'
228 # Generate hash using openssl dgst in hex format
229 cmdargs
= [get_openssl_path(),
231 '-'+'%s' % _hash_type_string
[hash_type
],
232 '-out', '%s' % hash_file_tmp
, '%s' % in_file
]
235 # Extract hash form dgst command output and convert to ascii
236 with
open(hash_file_tmp
, 'r') as fin
:
237 hashdata
= fin
.read()
241 hashdata
= hashdata
.rsplit('=', 1)[1].strip()
243 raise Exception('Hash Data not found for signing!')
245 if len(hashdata
) != (_hash_digest_Size
[hash_type
] * 2):
246 raise Exception('Hash Data size do match with for hash type!')
248 hashdata_bytes
= bytearray
.fromhex(hashdata
)
249 open(hash_file
, 'wb').write(hashdata_bytes
)
251 print("Key used for Singing %s !!" % priv_key
)
253 # sign using Openssl pkeyutl
254 cmdargs
= [get_openssl_path(),
255 'pkeyutl', '-sign', '-in', '%s' % hash_file
,
256 '-inkey', '%s' % priv_key
, '-out',
257 '%s' % out_file
, '-pkeyopt',
258 'digest:%s' % _hash_type_string
[hash_type
],
259 '-pkeyopt', 'rsa_padding_mode:%s' %
260 _sign_scheme_string
[sign_scheme
]]
267 # Extract public key using openssl
269 # in_key [Input] Private key or public key in pem format
270 # pub_key_file [Input/Output] Public Key to a file
272 # return keydata (mod, exp) in bin format
276 def single_sign_gen_pub_key(in_key
, pub_key_file
=None):
278 in_key
= get_key_from_store(in_key
)
280 # Expect key to be in PEM format
282 cmdline
= [get_openssl_path(), 'rsa', '-pubout', '-text', '-noout',
283 '-in', '%s' % in_key
]
284 # Check if it is public key or private key
285 text
= open(in_key
, 'r').read()
286 if '-BEGIN RSA PRIVATE KEY-' in text
:
288 elif '-BEGIN PUBLIC KEY-' in text
:
289 cmdline
.extend(['-pubin'])
291 raise Exception('Unknown key format "%s" !' % in_key
)
294 cmdline
.extend(['-out', '%s' % pub_key_file
])
299 output
= run_process(cmdline
, capture_out
=capture
)
301 output
= text
= open(pub_key_file
, 'r').read()
302 data
= output
.replace('\r', '')
303 data
= data
.replace('\n', '')
304 data
= data
.replace(' ', '')
306 # Extract the modulus
308 match
= re
.search('modulus(.*)publicExponent:\\s+(\\d+)\\s+', data
)
310 match
= re
.search('Modulus(?:.*?):(.*)Exponent:\\s+(\\d+)\\s+', data
)
312 raise Exception('Public key not found!')
313 modulus
= match
.group(1).replace(':', '')
314 exponent
= int(match
.group(2))
316 mod
= bytearray
.fromhex(modulus
)
317 # Remove the '00' from the front if the MSB is 1
318 if mod
[0] == 0 and (mod
[1] & 0x80):
320 exp
= bytearray
.fromhex('{:08x}'.format(exponent
))