blob: 5f32bd4717d14ec0423e52794d057ca22c7c4e75 [file] [log] [blame]
#
# Copyright 2018-2020 NXP
# SPDX-License-Identifier: Apache-2.0
#
#
"""
Provision attached secure element with ECC/RSA keys
Preconditions:
- Secure element attached
- Virtual environment should be activated (not for iMX platform.
Refer ssscli installation steps: Plug & Trust MW, Section 8.3 :ref:`cli-doc-pre-steps`)
Postconditions:
- Key pair injected on id referred by KEYPAIR_INDEX_CLIENT_PRIVATE variable
- Ref pem created
- Client certificate injected on id referred by CERTIFICATE_INDEX variable.
"""
import os
import sys
import logging
import traceback
import argparse
logging.basicConfig(format='%(message)s', level=logging.DEBUG)
log = logging.getLogger(__name__)
# GLOBAL VARIABLES
# ----------------
SE050_PROVISIONING_SCRIPT = "0.9"
EC_KEYPAIR_INDEX_CLIENT_PRIVATE = 0x7DCCBB10
EC_CERTIFICATE_INDEX = 0x7DCCBB11
RSA_KEYPAIR_INDEX_CLIENT_PRIVATE = 0x7DCCBB30
RSA_CERTIFICATE_INDEX = 0x7DCCBB31
example_text = '''
Example invocation::
python %s --key_type ecc
python %s --key_type ecc --connection_data 169.254.0.1:8050
python %s --key_type rsa --connection_data 127.0.0.1:8050 --connection_type jrcpv2
python %s --key_type rsa --connection_data COM3
python %s --key_type ecc --subsystem a71ch
''' % (__file__, __file__, __file__, __file__, __file__,)
SUPPORTED_CONNECTION_TYPES = [
"t1oi2c",
"sci2c",
"vcom",
"jrcpv1",
"jrcpv2",
"pcsc"
]
def session_open(subsystem, connection_data, connection_type, auth_type, scpkey):
''' Open session based on IOT Secure Element selected. '''
import sss.const as const
import sss.connect as connect
import sss.session as session
log.info("###############################################################")
log.info("#")
log.info("# SUBSYSTEM : %s" % subsystem)
log.info("# CONNECTION_TYPE : %s" % connection_type)
log.info("# CONNECTION_PARAMETER : %s" % connection_data)
log.info("#")
log.info("###############################################################")
connect.do_open_session(const.SUBSYSTEM_TYPE[subsystem],
const.CONNECTION_TYPE[connection_type], connection_data,
auth_type=const.AUTH_TYPE_MAP[auth_type][0],
scpkey=scpkey)
session_obj = session.Session()
try:
session_obj.session_open()
except Exception as exc:
error_log_file = os.path.abspath(os.path.dirname(__file__)) + os.sep + "error_log.txt"
if not os.path.isfile(error_log_file):
err_write = open(error_log_file, 'w+')
else:
err_write = open(error_log_file, 'a+')
traceback.print_exc(None, err_write)
err_write.close()
return None
return session_obj
def session_close(session):
''' Close opened session. '''
import sss.connect as connect
import sss.util as util
if session:
session.session_close()
if os.path.isfile(util.get_session_pkl_path()):
connect.do_close_session()
def reset(session):
''' Reset the Secure Module to the initial state. '''
from sss.se05x import Se05x
from sss.a71ch import A71CH
import sss.sss_api as apis
if session.subsystem == apis.kType_SSS_SE_SE05x:
se05x_obj = Se05x(session)
se05x_obj.debug_reset()
elif session.subsystem == apis.kType_SSS_SE_A71CH:
a71ch_obj = A71CH(session)
a71ch_obj.debug_reset()
def refpem_ecc(session, keyid, file_name):
''' Creates reference PEM file for ECC Pair.
keyid = 32bit Key ID. Should be in hex format. Example: 20E8A001 \n
filename = File name to store reference key. Can be in PEM or DER or PKCS12 format based on file extension.
By default filename with extension .pem in PEM format, .pfx or .p12 in PKCS12 format and others in DER format.
'''
from sss.refkey import RefPem
import sss.sss_api as apis
refpem_obj = RefPem(session)
status = refpem_obj.do_ecc_refpem_pair(keyid, file_name)
if status != apis.kStatus_SSS_Success:
log.error("Refpem creation failed..!")
session_close(session)
return status
log.info("Successfully Created reference key.")
return status
def refpem_rsa(session, keyid, file_name):
''' Creates reference PEM file for RSA Pair.
keyid = 32bit Key ID. Should be in hex format. Example: 20E8A001 \n
filename = File name to store reference key. Can be in PEM or DER or PKCS12 format based on file extension.
By default filename with extension .pem in PEM format, .pfx or .p12 in PKCS12 format and others in DER format.
'''
from sss.refkey import RefPem
import sss.sss_api as apis
refpem_obj = RefPem(session)
status = refpem_obj.do_rsa_refpem_pair(keyid, file_name)
if status != apis.kStatus_SSS_Success:
log.error("Refpem creation failed..!")
session_close(session)
return status
log.info("Successfully Created reference key.")
return status
def set_ecc_pair(session, keyid, client_key):
''' Set ECC Key pair to the Secure Module \n
keyid = 32bit Key ID. Should be in hex format. Example: 20E8A001 \n
key = Can be raw key (DER format) or in file.
For file, by default filename with extension .pem considered as PEM format and others as DER format.\n
'''
import sss.setkey as setkey
import sss.sss_api as apis
log.info("client_key file: %s" % (client_key,))
log.info("Injecting ECC key pair at key ID: 0x%x" % (keyid,))
set_obj = setkey.Set(session)
status = set_obj.do_set_ecc_key_pair(keyid, client_key, None)
if status != apis.kStatus_SSS_Success:
log.error("Injecting key pair failed..!")
session_close(session)
return status
log.info("Successfully Injected ECC key pair.")
return status
def set_rsa_pair(session, keyid, client_key):
''' Set RSA Key pair to the Secure Module \n
keyid = 32bit Key ID. Should be in hex format. Example: 20E8A001 \n
key = Can be raw key (DER format) or in file.
For file, by default filename with extension .pem considered as PEM format and others as DER format.\n
'''
import sss.setkey as setkey
import sss.sss_api as apis
log.info("client_key file: %s" % (client_key,))
log.info("Injecting RSA key pair at key ID: 0x%x" % (keyid,))
set_obj = setkey.Set(session)
status = set_obj.do_set_rsa_key_pair(keyid, client_key, None)
if status != apis.kStatus_SSS_Success:
log.error("Injecting key pair failed..!")
session_close(session)
return status
log.info("Successfully Injected RSA key pair.")
return status
def set_cert(session, keyid, cert):
''' Inject Certificate to the Secure Module
keyid = 32bit Key ID. Should be in hex format. Example: 20E8A001 \n
key = Can be raw certificate (DER format) or in file.
For file, by default filename with extension .pem and .cer considered as PEM format and others as DER format.\n
'''
import sss.setkey as setkey
import sss.sss_api as apis
log.info("certificate file: %s" % (cert,))
log.info("Injecting Certificate at key ID: 0x%x" % (keyid,))
set_obj = setkey.Set(session)
status = set_obj.do_set_cert(keyid, cert, None)
if status != apis.kStatus_SSS_Success:
log.error("Injecting certificate failed..!")
session_close(session)
return status
log.info("Successfully Injected Certificate.")
return status
def parse_in_args():
parser = argparse.ArgumentParser(description=__doc__,
epilog=example_text,
formatter_class=argparse.RawTextHelpFormatter)
required = parser.add_argument_group('required arguments')
optional = parser.add_argument_group('optional arguments')
required.add_argument('--key_type', default="",
help='Supported key types => ``ecc``, ``rsa``',
required=True)
optional.add_argument('--connection_data', default="none",
help='Parameter to connect to SE => eg. ``COM3``, ``127.0.0.1:8050``, ``none``. '
'Default: ``none``')
optional.add_argument('--connection_type', default="t1oi2c",
help='Supported connection types => ``%s``. Default: ``t1oi2c``' %
("``, ``".join(SUPPORTED_CONNECTION_TYPES)))
optional.add_argument('--subsystem', default="se05x",
help='Supported subsystem => ``se05x``, ``a71ch``. Default: ``se05x``')
optional.add_argument('--auth_type', default="None",
help='Supported subsystem => ``None``, ``PlatformSCP``, ``UserID``, ``ECKey``, ``AESKey``. Default: ``None``')
optional.add_argument('--scpkey', default="None",
help='')
optional.add_argument('--no_reset', action="store_true", help="do not reset contents of attached secure element")
if len(sys.argv) == 1:
parser.print_help(sys.stderr)
return None
args = parser.parse_args()
if args.key_type not in ["ecc", "rsa", ]:
parser.print_help(sys.stderr)
return None
if args.subsystem not in ["se05x", "a71ch", ]:
parser.print_help(sys.stderr)
return None
if args.connection_data.find(':') >= 0:
port_data = args.connection_data.split(':')
jrcp_host_name = port_data[0]
jrcp_port = port_data[1]
os.environ['JRCP_HOSTNAME'] = jrcp_host_name
os.environ['JRCP_PORT'] = jrcp_port
log.info("JRCP_HOSTNAME: %s" % jrcp_host_name)
log.info("JRCP_PORT: %s" % jrcp_port)
if args.connection_type == "t1oi2c":
args.connection_type = "jrcpv1"
elif args.connection_data.find('COM') >= 0:
if args.connection_type == "t1oi2c":
args.connection_type = "vcom"
elif args.connection_data.find('none') >= 0:
if args.subsystem == "a71ch":
args.connection_type = "sci2c"
else:
parser.print_help(sys.stderr)
return None
if args.connection_type not in SUPPORTED_CONNECTION_TYPES:
parser.print_help(sys.stderr)
return None
return args
def main():
cur_dir = os.path.abspath(os.path.dirname(__file__))
py_sss_dir = os.path.join(cur_dir, '..', '..', '..', '..', 'pycli', 'src')
sys.path.append(py_sss_dir)
import sss.sss_api as apis
# check for input arguments
args = parse_in_args()
if args is None:
return
# --------------------------------------------------------------
# Start of program - Ensure an SE is connected to your system.
# --------------------------------------------------------------
log.info("SE050 Key provisioning script (Rev.%s)." % SE050_PROVISIONING_SCRIPT)
log.info("Executing this script will insert keys in the attached SE050 secure element.")
if args.key_type == "ecc":
key_type = "prime256v1"
else:
key_type = "RSA"
keys_dir = os.path.join(cur_dir, '..', 'credentials', key_type) + os.sep
# ECC keys to be stored in SE
# ------------------------------
client_key = keys_dir + "tls_client_key.pem"
client_key_ref = keys_dir + "tls_client_key_ref.pem"
client_key_pub = keys_dir + "tls_client_key_pub.pem" # Contains public key only
# Client certificate
client_cer = keys_dir + "tls_client.cer"
# close old session
session_close(None)
# Open a new session
session = session_open(args.subsystem, args.connection_data, args.connection_type, args.auth_type, args.scpkey)
if session is None:
return
# NOTE: In case of A71CH always issue reset
# When SE050 is lacking storage / has conflicting storage issue a reset of the Secure Element
if not args.no_reset:
reset(session)
if key_type == "prime256v1":
# Inject ecc pair key to the Secure Element
status = set_ecc_pair(session, EC_KEYPAIR_INDEX_CLIENT_PRIVATE, client_key)
if status != apis.kStatus_SSS_Success:
return
# Generate Reference key from the key injected. Store it in file referred by "client_key_ref" variable.
status = refpem_ecc(session, EC_KEYPAIR_INDEX_CLIENT_PRIVATE, client_key_ref)
if status != apis.kStatus_SSS_Success:
return
key_id_cert = EC_CERTIFICATE_INDEX
else:
# Inject rsa key pair into the Secure Element
status = set_rsa_pair(session, RSA_KEYPAIR_INDEX_CLIENT_PRIVATE, client_key)
if status != apis.kStatus_SSS_Success:
return
# Generate Reference key from the key injected. Store it in file referred by "client_key_ref" variable.
status = refpem_rsa(session, RSA_KEYPAIR_INDEX_CLIENT_PRIVATE, client_key_ref)
if status != apis.kStatus_SSS_Success:
return
key_id_cert = RSA_CERTIFICATE_INDEX
# Inject certificate to the Secure Element
status = set_cert(session, key_id_cert, client_cer)
if status != apis.kStatus_SSS_Success:
return
session_close(session)
log.info("##############################################################")
log.info("# #")
log.info("# Program Completed Successfully #")
log.info("# #")
log.info("##############################################################")
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
main()