Masterboot image with Signature Provider for mcxn9xx

Masterboot image with Signature Provider for mcxn9xx#

This notebook describes how to use a custom remote signing service for generating masterboot image using nxpimage tool.

%run ../../init_notebook.ipynb

import pprint
import os

pp = pprint.PrettyPrinter(indent=4)

plugins_dir = '../common/plugins/'

SASP_PLUGIN = os.path.join(plugins_dir, '')
WORKSPACE = "workspace/mbimg/" # change this to path to your workspace
DATA_DIR = "data_mbimg/" # change this to path to your workspace
VERBOSITY = "-v" # verbosity of commands, might be -v or -vv for debug or blank for no additional info
Created `%!` as an alias for `%execute`.

Signature Provider Plugin#

First, we need to setup the Signature Provider plugin and start the custom HSM. In order to do that, open the signature provider notebook (signature_provider.ipynb) and follow the instructions there. Once you are done, come back and continue here.

Config File Setup#

The masterboot configuration file will be needed for successful generation of Masterboot image(MBI) using nxpimage application. * There are three types of MBI for mcxn9xx based on the authentication type: Plain, CRC and Signed.

In this example we will be interested only in the Signed image type.

Run the following code and see how the generated configuration template looks like.

import os
from spsdk.utils.misc import load_file
# choose family for the MCU
FAMILY = "mcxn9xx"

%! nxpimage $VERBOSITY mbi get-templates --family $FAMILY --output $WORKSPACE --force

MBI_CONFIG_PATH = os.path.join(WORKSPACE, "mcxn9xx_xip_signed.yaml")
# just for verification that the template was generated
assert os.path.exists(MBI_CONFIG_PATH)

config_content = load_file(MBI_CONFIG_PATH)
nxpimage -v mbi get-templates --family mcxn9xx --output workspace/mbimg/ --force 
Creating C:\spsdk\examples\jupyter_examples\signature_provider\mcxn9xx\workspace\mbimg\mcxn9xx_xip_plain.yaml template file.
Creating C:\spsdk\examples\jupyter_examples\signature_provider\mcxn9xx\workspace\mbimg\mcxn9xx_xip_crc.yaml template file.
Creating C:\spsdk\examples\jupyter_examples\signature_provider\mcxn9xx\workspace\mbimg\mcxn9xx_xip_signed.yaml template file.
Creating C:\spsdk\examples\jupyter_examples\signature_provider\mcxn9xx\workspace\mbimg\mcxn9xx_load_to_ram_plain.yaml template file.
Creating C:\spsdk\examples\jupyter_examples\signature_provider\mcxn9xx\workspace\mbimg\mcxn9xx_load_to_ram_crc.yaml template file.
Creating C:\spsdk\examples\jupyter_examples\signature_provider\mcxn9xx\workspace\mbimg\mcxn9xx_load_to_ram_signed.yaml template file.
# ==================  Master Boot Image Configuration template for mcxn9xx, Plain Signed XIP Image.  ===================

# ======================================================================================================================
#                                                  == Basic Settings ==                                                 
# ======================================================================================================================
# ------------------------------------------===== MCU family [Required] =====-------------------------------------------
# Description: MCU family name.
family: mcxn9xx
# --------------------------------------===== Application target [Required] =====---------------------------------------
# Description: Definition if application is Execute in Place(XiP) or loaded to RAM during reset sequence.
# Possible options: <xip, load-to-ram>
outputImageExecutionTarget: xip
# -------------------------------===== Type of boot image authentication [Required] =====-------------------------------
# Description: Specification of final master boot image authentication.
# Possible options: <plain, crc, signed, signed-encrypted, signed-nxp>
outputImageAuthenticationType: signed
# ------------------------------------===== Master Boot Image name [Required] =====-------------------------------------
# Description: The file for Master Boot Image result file.
masterBootOutputFile: my_mbi.bin
# ------------------------------------===== Plain application image [Required] =====------------------------------------
# Description: The input application image to by modified to Master Boot Image.
inputImageFile: my_application.bin
# --------------------------------===== Loading address of application [Required] =====---------------------------------
# Description: Application loading address in RAM if not XiP, otherwise address of load in XiP.
outputImageExecutionAddress: 0
# ---------------------------------------===== Firmware version. [Optional] =====---------------------------------------
# Description: Value compared with Secure_FW_Version monotonic counter value stored in PFR/IFR. If value is lower than
# value in PFR/IFR, then is image rejected (rollback protection)..
firmwareVersion: 0
# -----------------------------------------===== Image SubType [Optional] =====-----------------------------------------
# Description: Image subtype determine the image use in MCU. "main" for main application, "nbu" for Narrow Band Unit
# image and "recovery" for recovery image type
# Possible options: <main, nbu, recovery>
outputImageSubtype: main
# ----------------------------------------===== Image version. [Optional] =====-----------------------------------------
# Description: Image version is used for dual boot. The boot ROM decides which image to boot first based on the image
# version. It boots the one with the newer image version first, and in case of a failure, it boots the older one.
imageVersion: 0
# ======================================================================================================================
#                                              == Certificate Block V2.1 ==                                             
# ======================================================================================================================
# -----------------------------===== Certificate Block binary/config file [Required] =====------------------------------
# Description: Path to certificate block binary or config file.
certBlock: cert_block.yaml
# ======================================================================================================================
#                                              == Image Signing Settings ==                                             
# ======================================================================================================================
# --------------------------===== Main Certificate private key [Conditionally required] =====---------------------------
# Description: Main Certificate private key used to sign certificate. It can be replaced by signProvider key.
signPrivateKey: main_prv_key.pem
# -------------------------------===== Signature Provider [Conditionally required] =====--------------------------------
# Description: Signature provider configuration in format 'type=<sp_type>;<key1>=<value1>;<key2>=<value2>".
signProvider: type=file;file_path=my_prv_key.pem
# ======================================================================================================================
#                                               == Trust Zone Settings ==                                               
# ======================================================================================================================
# ------------------------------------===== TrustZone enable option [Optional] =====------------------------------------
# Description: If not specified, the Trust zone is disabled.
enableTrustZone: false
# ---------------------------------===== TrustZone Customization file [Optional] =====----------------------------------
# Description: If not specified, but TrustZone is enabled(enableTrustZone) the default values are used.
trustZonePresetFile: my_tz_custom.yaml

Masterboot image generation#

The generated configuration file contains all possible configuration settings.

Some of them are required (labeled with [Required] comment), some of them are conditionally required (labeled with [Conditionally required] comment) and some are optional (labeled with [Optional] comment).

Let’s now focus on two configuration settings: signPrivateKey and signProvider. These two configuration values are mutually exclusive, so only one can be chosen. In order to use Signature Provider, we will remove the line with signPrivateKey and update the one with signProvider.

Keep in mind that the signature provider configuration must meet following rules:

  • Configuration key

    • key names sign_provider or signProvider are allowed

  • Configuration value

    • format "type=<sp_type>;<key1>=<value1>;<key2>=<value2>;..."

    • the sp_type has to match the sp_type class attribute defined in the custom signature provider(plugins/

    • the remaining key-value pairs are passed to the __init__ method of the concrete Signature Provider

    • e.g.: "type=file;file_path=private_key.pem" will instantiate spsdk.crypto.PlainFileSP(file_path='private_key.pem')

For sake of simplification, the pre-generated master boot configuration file is used. It contains only settings related to this example.

import shutil

# Copy additional files needed for masterboot image creation
shutil.copytree(DATA_DIR, WORKSPACE)

CERT_BLOCK_CONFIG_PATH = os.path.join(WORKSPACE, "mcxn9xx_cert_block.yaml")
CMPA_CONFIG_PATH = os.path.join(WORKSPACE, "mcxn9xx_cmpa.yaml")
pp.pprint(f"All config files are ready in folder '{WORKSPACE}'")

%! nxpimage cert-block export --family $FAMILY --config $CERT_BLOCK_CONFIG_PATH
pp.pprint(f"Cert block binary has been generated '{CERT_BLOCK_CONFIG_PATH}'")

%! nxpimage $VERBOSITY mbi export --plugin $SASP_PLUGIN --config $MBI_CONFIG_PATH
pp.pprint(f"Mbi binary has been generated '{os.path.join(WORKSPACE, 'my_mbi.bin')}'")

SF0 = os.path.join(WORKSPACE, '')
SF1 = os.path.join(WORKSPACE, '')
CMPA_BIN = os.path.join(WORKSPACE, 'cmpa.bin')
%! pfr generate-binary -c $CMPA_CONFIG_PATH -sf $SF0 -sf $SF1 -o $CMPA_BIN
pp.pprint(f"CMPA has been generated in '{CMPA_BIN}'")
"All config files are ready in folder 'workspace/mbimg/'"
nxpimage cert-block export --family mcxn9xx --config workspace/mbimg/mcxn9xx_cert_block.yaml "Cert block binary has been generated 'workspace/mbimg/mcxn9xx_cert_block.yaml'"

RKTH: b7ed92485e2090838d9d1766513de63a655c626700a7241dd2d6d72160051873ac9ad63a0da3a73dbfe3b2ddc652d31f
Success. (Certificate Block: C:/spsdk/examples/jupyter_examples/signature_provider/mcxn9xx/workspace/mbimg/cert_block.bin created.)
nxpimage -v mbi export --plugin ../common/plugins/ --config workspace/mbimg/mcxn9xx_xip_signed.yaml 
RKTH: b7ed92485e2090838d9d1766513de63a655c626700a7241dd2d6d72160051873ac9ad63a0da3a73dbfe3b2ddc652d31f
Success. (Master Boot Image: C:/spsdk/examples/jupyter_examples/signature_provider/mcxn9xx/workspace/mbimg/my_mbi.bin created.)
"Mbi binary has been generated 'workspace/mbimg/my_mbi.bin'"
pfr generate-binary -c workspace/mbimg/mcxn9xx_cmpa.yaml -sf workspace/mbimg/ -sf workspace/mbimg/ -o workspace/mbimg/cmpa.bin 
Success. (PFR binary has been generated)
Result has been stored in: C:\spsdk\examples\jupyter_examples\signature_provider\mcxn9xx\workspace\mbimg\cmpa.bin
"CMPA has been generated in 'workspace/mbimg/cmpa.bin'"


At this point, we have everything we need for running the application. Connect the board, update the UART_CONNECTION variable depending on the used serial port and run the code. Keep in mind that the board must be in ISP mode. Once the code finishes, you can restart the board. Once the application is booted, green LED starts blinking.

UART_CONNECTION = "-p com19"
%! blhost $UART_CONNECTION -- fill-memory 0x20000000 4 0xc0000405 word
%! blhost $UART_CONNECTION -- configure-memory 9 0x20000000
%! blhost $UART_CONNECTION -- flash-erase-region 0x80000000 0x10000 
%! blhost $UART_CONNECTION -- fill-memory 0x20003000 4 0xF000000F word
%! blhost $UART_CONNECTION -- configure-memory 9 0x20003000
pp.pprint("Memory has been configured")

%! pfr erase-cmpa $UART_CONNECTION --family $FAMILY

CMPA_BIN = os.path.join(WORKSPACE, 'cmpa.bin')
%! pfr write $UART_CONNECTION --type cmpa --family $FAMILY --binary $CMPA_BIN
pp.pprint(f"CMPA has been written'")

MBI_BIN = os.path.join(WORKSPACE, 'my_mbi.bin')
%! blhost $UART_CONNECTION -- write-memory 0x80001000 $MBI_BIN
blhost -p com19 -- fill-memory 0x20000000 4 0xc0000405 word 
Response status = 0 (0x0) Success.
blhost -p com19 -- configure-memory 9 0x20000000 
Response status = 0 (0x0) Success.
blhost -p com19 -- flash-erase-region 0x80000000 0x10000 
Response status = 0 (0x0) Success.
blhost -p com19 -- fill-memory 0x20003000 4 0xF000000F word 
Response status = 0 (0x0) Success.
blhost -p com19 -- configure-memory 9 0x20003000 
Response status = 0 (0x0) Success.
'Memory has been configured'
pfr erase-cmpa -p com19 --family mcxn9xx 
CMPA page address on mcxn9xx is 0x1004000
CMPA page has been erased.
pfr write -p com19 --type cmpa --family mcxn9xx --binary workspace/mbimg/cmpa.bin 
CMPA page address on mcxn9xx is 0x1004000
CMPA data written to device.
"CMPA has been written'"
blhost -p com19 -- write-memory 0x80001000 workspace/mbimg/my_mbi.bin 
Writing memory
Response status = 0 (0x0) Success.
Response word 1 = 14284 (0x37cc)

HSM teardown#

Last step is to stop custom HSM. In order to do that, open again the HSM setup notebook (sahsm.ipynb) and stop the running jupyter notebook code cell.