summaryrefslogtreecommitdiffstats
path: root/IntelFsp2Pkg/Tools/ConfigEditor/SingleSign.py
diff options
context:
space:
mode:
Diffstat (limited to 'IntelFsp2Pkg/Tools/ConfigEditor/SingleSign.py')
-rw-r--r--IntelFsp2Pkg/Tools/ConfigEditor/SingleSign.py324
1 files changed, 324 insertions, 0 deletions
diff --git a/IntelFsp2Pkg/Tools/ConfigEditor/SingleSign.py b/IntelFsp2Pkg/Tools/ConfigEditor/SingleSign.py
new file mode 100644
index 0000000000..7e008aa68a
--- /dev/null
+++ b/IntelFsp2Pkg/Tools/ConfigEditor/SingleSign.py
@@ -0,0 +1,324 @@
+#!/usr/bin/env python
+# @ SingleSign.py
+# Single signing script
+#
+# Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+import os
+import sys
+import re
+import shutil
+import subprocess
+
+SIGNING_KEY = {
+ # Key Id | Key File Name start |
+ # =================================================================
+ # KEY_ID_MASTER is used for signing Slimboot Key Hash Manifest \
+ # container (KEYH Component)
+ "KEY_ID_MASTER_RSA2048": "MasterTestKey_Priv_RSA2048.pem",
+ "KEY_ID_MASTER_RSA3072": "MasterTestKey_Priv_RSA3072.pem",
+
+ # KEY_ID_CFGDATA is used for signing external Config data blob)
+ "KEY_ID_CFGDATA_RSA2048": "ConfigTestKey_Priv_RSA2048.pem",
+ "KEY_ID_CFGDATA_RSA3072": "ConfigTestKey_Priv_RSA3072.pem",
+
+ # KEY_ID_FIRMWAREUPDATE is used for signing capsule firmware update image)
+ "KEY_ID_FIRMWAREUPDATE_RSA2048": "FirmwareUpdateTestKey_Priv_RSA2048.pem",
+ "KEY_ID_FIRMWAREUPDATE_RSA3072": "FirmwareUpdateTestKey_Priv_RSA3072.pem",
+
+ # KEY_ID_CONTAINER is used for signing container header with mono signature
+ "KEY_ID_CONTAINER_RSA2048": "ContainerTestKey_Priv_RSA2048.pem",
+ "KEY_ID_CONTAINER_RSA3072": "ContainerTestKey_Priv_RSA3072.pem",
+
+ # CONTAINER_COMP1_KEY_ID is used for signing container components
+ "KEY_ID_CONTAINER_COMP_RSA2048": "ContainerCompTestKey_Priv_RSA2048.pem",
+ "KEY_ID_CONTAINER_COMP_RSA3072": "ContainerCompTestKey_Priv_RSA3072.pem",
+
+ # KEY_ID_OS1_PUBLIC, KEY_ID_OS2_PUBLIC is used for referencing \
+ # Boot OS public keys
+ "KEY_ID_OS1_PUBLIC_RSA2048": "OS1_TestKey_Pub_RSA2048.pem",
+ "KEY_ID_OS1_PUBLIC_RSA3072": "OS1_TestKey_Pub_RSA3072.pem",
+
+ "KEY_ID_OS2_PUBLIC_RSA2048": "OS2_TestKey_Pub_RSA2048.pem",
+ "KEY_ID_OS2_PUBLIC_RSA3072": "OS2_TestKey_Pub_RSA3072.pem",
+
+ }
+
+MESSAGE_SBL_KEY_DIR = """!!! PRE-REQUISITE: Path to SBL_KEY_DIR has.
+to be set with SBL KEYS DIRECTORY !!! \n!!! Generate keys.
+using GenerateKeys.py available in BootloaderCorePkg/Tools.
+directory !!! \n !!! Run $python.
+BootloaderCorePkg/Tools/GenerateKeys.py -k $PATH_TO_SBL_KEY_DIR !!!\n
+!!! Set SBL_KEY_DIR environ with path to SBL KEYS DIR !!!\n"
+!!! Windows $set SBL_KEY_DIR=$PATH_TO_SBL_KEY_DIR !!!\n
+!!! Linux $export SBL_KEY_DIR=$PATH_TO_SBL_KEY_DIR !!!\n"""
+
+
+def get_openssl_path():
+ if os.name == 'nt':
+ if 'OPENSSL_PATH' not in os.environ:
+ openssl_dir = "C:\\Openssl\\bin\\"
+ if os.path.exists(openssl_dir):
+ os.environ['OPENSSL_PATH'] = openssl_dir
+ else:
+ os.environ['OPENSSL_PATH'] = "C:\\Openssl\\"
+ if 'OPENSSL_CONF' not in os.environ:
+ openssl_cfg = "C:\\Openssl\\openssl.cfg"
+ if os.path.exists(openssl_cfg):
+ os.environ['OPENSSL_CONF'] = openssl_cfg
+ openssl = os.path.join(
+ os.environ.get('OPENSSL_PATH', ''),
+ 'openssl.exe')
+ else:
+ # Get openssl path for Linux cases
+ openssl = shutil.which('openssl')
+
+ return openssl
+
+
+def run_process(arg_list, print_cmd=False, capture_out=False):
+ sys.stdout.flush()
+ if print_cmd:
+ print(' '.join(arg_list))
+
+ exc = None
+ result = 0
+ output = ''
+ try:
+ if capture_out:
+ output = subprocess.check_output(arg_list).decode()
+ else:
+ result = subprocess.call(arg_list)
+ except Exception as ex:
+ result = 1
+ exc = ex
+
+ if result:
+ if not print_cmd:
+ print('Error in running process:\n %s' % ' '.join(arg_list))
+ if exc is None:
+ sys.exit(1)
+ else:
+ raise exc
+
+ return output
+
+
+def check_file_pem_format(priv_key):
+ # Check for file .pem format
+ key_name = os.path.basename(priv_key)
+ if os.path.splitext(key_name)[1] == ".pem":
+ return True
+ else:
+ return False
+
+
+def get_key_id(priv_key):
+ # Extract base name if path is provided.
+ key_name = os.path.basename(priv_key)
+ # Check for KEY_ID in key naming.
+ if key_name.startswith('KEY_ID'):
+ return key_name
+ else:
+ return None
+
+
+def get_sbl_key_dir():
+ # Check Key store setting SBL_KEY_DIR path
+ if 'SBL_KEY_DIR' not in os.environ:
+ exception_string = "ERROR: SBL_KEY_DIR is not defined." \
+ " Set SBL_KEY_DIR with SBL Keys directory!!\n"
+ raise Exception(exception_string + MESSAGE_SBL_KEY_DIR)
+
+ sbl_key_dir = os.environ.get('SBL_KEY_DIR')
+ if not os.path.exists(sbl_key_dir):
+ exception_string = "ERROR:SBL_KEY_DIR set " + sbl_key_dir \
+ + " is not valid." \
+ " Set the correct SBL_KEY_DIR path !!\n" \
+ + MESSAGE_SBL_KEY_DIR
+ raise Exception(exception_string)
+ else:
+ return sbl_key_dir
+
+
+def get_key_from_store(in_key):
+
+ # Check in_key is path to key
+ if os.path.exists(in_key):
+ return in_key
+
+ # Get Slimboot key dir path
+ sbl_key_dir = get_sbl_key_dir()
+
+ # Extract if in_key is key_id
+ priv_key = get_key_id(in_key)
+ if priv_key is not None:
+ if (priv_key in SIGNING_KEY):
+ # Generate key file name from key id
+ priv_key_file = SIGNING_KEY[priv_key]
+ else:
+ exception_string = "KEY_ID" + priv_key + "is not found " \
+ "is not found in supported KEY IDs!!"
+ raise Exception(exception_string)
+ elif check_file_pem_format(in_key):
+ # check if file name is provided in pem format
+ priv_key_file = in_key
+ else:
+ priv_key_file = None
+ raise Exception('key provided %s is not valid!' % in_key)
+
+ # Create a file path
+ # Join Key Dir and priv_key_file
+ try:
+ priv_key = os.path.join(sbl_key_dir, priv_key_file)
+ except Exception:
+ raise Exception('priv_key is not found %s!' % priv_key)
+
+ # Check for priv_key construted based on KEY ID exists in specified path
+ if not os.path.isfile(priv_key):
+ exception_string = "!!! ERROR: Key file corresponding to" \
+ + in_key + "do not exist in Sbl key " \
+ "directory at" + sbl_key_dir + "!!! \n" \
+ + MESSAGE_SBL_KEY_DIR
+ raise Exception(exception_string)
+
+ return priv_key
+
+#
+# Sign an file using openssl
+#
+# priv_key [Input] Key Id or Path to Private key
+# hash_type [Input] Signing hash
+# sign_scheme[Input] Sign/padding scheme
+# in_file [Input] Input file to be signed
+# out_file [Input/Output] Signed data file
+#
+
+
+def single_sign_file(priv_key, hash_type, sign_scheme, in_file, out_file):
+
+ _hash_type_string = {
+ "SHA2_256": 'sha256',
+ "SHA2_384": 'sha384',
+ "SHA2_512": 'sha512',
+ }
+
+ _hash_digest_Size = {
+ # Hash_string : Hash_Size
+ "SHA2_256": 32,
+ "SHA2_384": 48,
+ "SHA2_512": 64,
+ "SM3_256": 32,
+ }
+
+ _sign_scheme_string = {
+ "RSA_PKCS1": 'pkcs1',
+ "RSA_PSS": 'pss',
+ }
+
+ priv_key = get_key_from_store(priv_key)
+
+ # Temporary files to store hash generated
+ hash_file_tmp = out_file+'.hash.tmp'
+ hash_file = out_file+'.hash'
+
+ # Generate hash using openssl dgst in hex format
+ cmdargs = [get_openssl_path(),
+ 'dgst',
+ '-'+'%s' % _hash_type_string[hash_type],
+ '-out', '%s' % hash_file_tmp, '%s' % in_file]
+ run_process(cmdargs)
+
+ # Extract hash form dgst command output and convert to ascii
+ with open(hash_file_tmp, 'r') as fin:
+ hashdata = fin.read()
+ fin.close()
+
+ try:
+ hashdata = hashdata.rsplit('=', 1)[1].strip()
+ except Exception:
+ raise Exception('Hash Data not found for signing!')
+
+ if len(hashdata) != (_hash_digest_Size[hash_type] * 2):
+ raise Exception('Hash Data size do match with for hash type!')
+
+ hashdata_bytes = bytearray.fromhex(hashdata)
+ open(hash_file, 'wb').write(hashdata_bytes)
+
+ print("Key used for Singing %s !!" % priv_key)
+
+ # sign using Openssl pkeyutl
+ cmdargs = [get_openssl_path(),
+ 'pkeyutl', '-sign', '-in', '%s' % hash_file,
+ '-inkey', '%s' % priv_key, '-out',
+ '%s' % out_file, '-pkeyopt',
+ 'digest:%s' % _hash_type_string[hash_type],
+ '-pkeyopt', 'rsa_padding_mode:%s' %
+ _sign_scheme_string[sign_scheme]]
+
+ run_process(cmdargs)
+
+ return
+
+#
+# Extract public key using openssl
+#
+# in_key [Input] Private key or public key in pem format
+# pub_key_file [Input/Output] Public Key to a file
+#
+# return keydata (mod, exp) in bin format
+#
+
+
+def single_sign_gen_pub_key(in_key, pub_key_file=None):
+
+ in_key = get_key_from_store(in_key)
+
+ # Expect key to be in PEM format
+ is_prv_key = False
+ cmdline = [get_openssl_path(), 'rsa', '-pubout', '-text', '-noout',
+ '-in', '%s' % in_key]
+ # Check if it is public key or private key
+ text = open(in_key, 'r').read()
+ if '-BEGIN RSA PRIVATE KEY-' in text:
+ is_prv_key = True
+ elif '-BEGIN PUBLIC KEY-' in text:
+ cmdline.extend(['-pubin'])
+ else:
+ raise Exception('Unknown key format "%s" !' % in_key)
+
+ if pub_key_file:
+ cmdline.extend(['-out', '%s' % pub_key_file])
+ capture = False
+ else:
+ capture = True
+
+ output = run_process(cmdline, capture_out=capture)
+ if not capture:
+ output = text = open(pub_key_file, 'r').read()
+ data = output.replace('\r', '')
+ data = data.replace('\n', '')
+ data = data.replace(' ', '')
+
+ # Extract the modulus
+ if is_prv_key:
+ match = re.search('modulus(.*)publicExponent:\\s+(\\d+)\\s+', data)
+ else:
+ match = re.search('Modulus(?:.*?):(.*)Exponent:\\s+(\\d+)\\s+', data)
+ if not match:
+ raise Exception('Public key not found!')
+ modulus = match.group(1).replace(':', '')
+ exponent = int(match.group(2))
+
+ mod = bytearray.fromhex(modulus)
+ # Remove the '00' from the front if the MSB is 1
+ if mod[0] == 0 and (mod[1] & 0x80):
+ mod = mod[1:]
+ exp = bytearray.fromhex('{:08x}'.format(exponent))
+
+ keydata = mod + exp
+
+ return keydata