Generating Master Boot Image with a Signature Provider for MCXN946#

This notebook provides a guide on utilizing a custom remote signing service to generate a Master Boot Image (MBI) using the nxpimage tool. We will explore the integration of the signature provider with the SPSDK framework and efficient signing of your images

1. Prerequisites#

  • SPSDK is needed with examples extension. pip install spsdk[examples] (Please refer to the installation documentation.)

  • Connect the mcxn946 board via your preferred interface

import os

DATA_DIR = "_data/"
WORKSPACE = "workspace/"  # change this to path to your workspace
PLUGINS_DIR = "../_common/plugins/"
SASP_PLUGIN = os.path.join(PLUGINS_DIR, "sasp.py")
FAMILY = "mcxn946"
VERBOSITY = (
    "-v"  # verbosity of commands, might be -v or -vv for debug or blank for no additional info
)

2. Signature Provider Plugin Setup#

First, we need to setup the Signature Provider plugin and start the custom HSM. In order to do that, go to Signature Provider notebook and follow the instructions there. Once you are done, come back and continue here.

3. Preparing the configuration file#

To successfully create a Master Boot Image, a configuration file is required for the nxpimage application. There are three types of MBIs for the mcxn9xx series, categorized by their authentication type: Plain, CRC, and Signed. In this example, we will focus exclusively on the Signed image type.

Let’s begin with creating a template configuration file running the nxpimage mbi get-templates command. This command generates a YAML template that can be customized so the custom Signature Provider is integrated. Below, we’ll compare the differences between the template and our customized example to highlight the additions we’ve made.

# Get difference of template and user YAML configuration
YamlDiffWidget("mcxn946_mbi.diffc").html
nxpimage mbi get-templates -f mcxn946 -o workspace/ --force 
Creating workspace/mcxn946_xip_plain.yaml template file.
Creating workspace/mcxn946_xip_crc.yaml template file.
Creating workspace/mcxn946_xip_signed.yaml template file.
Creating workspace/mcxn946_load_to_ram_plain.yaml template file.
Creating workspace/mcxn946_load_to_ram_crc.yaml template file.

Configuration Differences

3.1 Signature Provider configuration#

The signature provider configuration string must follow the format:

"type=<identifier>;<key1>=<value1>;<key2>=<value2>;..."
  • The <identifier> must match the identifier class attribute defined in the custom Signature Provider (in this example plugins/sasp.py).

  • The remaining key-value pairs will be passed to the __init__ method of the given Signature Provider.

For instance, the configuration string:

"type=file;file_path=private_key.pem"

will instantiate the following object:

spsdk.crypto.PlainFileSP(file_path='private_key.pem')

4. Master Boot Image Generation#

At this point, we have everything we need to run nxpimage application using remote HSM for image signing.

CONFIG_PATH = os.path.join(DATA_DIR, "mcxn946_xip_signed.yaml")
OUTPUT_PATH = os.path.join(WORKSPACE, "my_mbi.bin")
%! nxpimage $VERBOSITY mbi export --plugin $SASP_PLUGIN --config $CONFIG_PATH

# check if the signed image exists
assert os.path.exists(OUTPUT_PATH)
nxpimage -v mbi export --plugin ../_common/plugins/sasp.py --config _data/mcxn946_xip_signed.yaml 
RKTH: b7ed92485e2090838d9d1766513de63a655c626700a7241dd2d6d72160051873ac9ad63a0da3a73dbfe3b2ddc652d31f
INFO:spsdk.apps.nxpimage:
+==0x0000_0000= Application Block ======+
|       Size: 13.9 kiB; 14,284 B        |
|+==0x0000_0000= Application ==========+|
||      Size: 13.6 kiB; 13,956 B       ||
|+==0x0000_3683========================+|
|+==0x0000_3684= Certification Block ==+|
||             Size: 208 B             ||
|+==0x0000_3753========================+|
|+==0x0000_3754= Manifest =============+|
||             Size: 24 B              ||
|+==0x0000_376b========================+|
|+==0x0000_376c= ECC signature ========+|
||             Size: 96 B              ||
|+==0x0000_37cb========================+|
+==0x0000_37cb==========================+

Success. (Master Boot Image: workspace/my_mbi.bin created.)

5. Customer Manufacturing Programming Area (CMPA) generation#

You can generate your own CMPA template running the command pfr get-template and update it with your settings. Below, we’ll compare the differences between the template and our customized example to highlight the additions we’ve made. Only the non-default settings are kept in this config file.

Note: Pay special attention to the RoTK_USAGE register as it defines number of RoT keys in the certification block. In our case, we used 2 RoT keys, so the RoTK2_Usage and RoTK3_Usage are set to KEY_SLOT_NOT_USED.

# Get difference of template and user YAML configuration
YamlDiffWidget("mcxn946_cmpa.diffc").html
pfr get-template -f mcxn946 -t cmpa -o workspace/cmpa_template.yaml --force 
The PFR cmpa template for mcxn946 has been saved into workspace/cmpa_template.yaml YAML file

Configuration Differences

CMPA_CFG = os.path.join(DATA_DIR, "cmpa.yaml")
SF0 = os.path.join(DATA_DIR, "hsm_k0_secp384r1.pub")
SF1 = os.path.join(DATA_DIR, "hsm_k1_secp384r1.pub")
CMPA_BIN = os.path.join(WORKSPACE, "cmpa.bin")
%! pfr generate-binary -c $CMPA_CFG -sf $SF0 -sf $SF1 -o $CMPA_BIN

# check if the CMPA binary has been generated
assert os.path.exists(CMPA_BIN)
pfr generate-binary -c _data/cmpa.yaml -sf _data/hsm_k0_secp384r1.pub -sf _data/hsm_k1_secp384r1.pub -o workspace/cmpa.bin 
Success. (PFR binary has been generated)

6. Execution#

At this point, we have everything we need for running the application. Connect the board and run the code. When the code finishes, you can restart the board. Once the application is booted, the green LED starts blinking.

Note: Keep in mind that the board must be in ISP mode.

USB_CONNECTION = "-u 0x1fc9:0x014f"
CMPA_BIN = os.path.join(WORKSPACE, "cmpa.bin")
MBI_BIN = os.path.join(WORKSPACE, "my_mbi.bin")

%! blhost $USB_CONNECTION -- fill-memory 0x20000000 4 0xc0000405 word
%! blhost $USB_CONNECTION -- configure-memory 9 0x20000000
%! blhost $USB_CONNECTION -- flash-erase-region 0x80000000 0x10000
%! blhost $USB_CONNECTION -- fill-memory 0x20003000 4 0xF000000F word
%! blhost $USB_CONNECTION -- configure-memory 9 0x20003000
print("Memory has been configured")

%! pfr erase-cmpa $USB_CONNECTION --family $FAMILY
%! pfr write $USB_CONNECTION --type cmpa --family $FAMILY --binary $CMPA_BIN
print(f"CMPA has been written")

%! blhost $USB_CONNECTION -- write-memory 0x80001000 $MBI_BIN
print(f"Master boot image has been written")
blhost -u 0x1fc9:0x014f -- fill-memory 0x20000000 4 0xc0000405 word 
Response status = 0 (0x0) Success.
blhost -u 0x1fc9:0x014f -- configure-memory 9 0x20000000 
Response status = 0 (0x0) Success.
blhost -u 0x1fc9:0x014f -- flash-erase-region 0x80000000 0x10000 
Response status = 0 (0x0) Success.
blhost -u 0x1fc9:0x014f -- fill-memory 0x20003000 4 0xF000000F word 
Response status = 0 (0x0) Success.
blhost -u 0x1fc9:0x014f -- configure-memory 9 0x20003000 
Response status = 0 (0x0) Success.
Memory has been configured
pfr erase-cmpa -u 0x1fc9:0x014f --family mcxn946 
CMPA page address on mcxn946 is 0x1004000
CMPA page has been erased.
pfr write -u 0x1fc9:0x014f --type cmpa --family mcxn946 --binary workspace/cmpa.bin 
CMPA page address on mcxn946 is 0x1004000
CMPA data written to device.
CMPA has been written
blhost -u 0x1fc9:0x014f -- write-memory 0x80001000 workspace/my_mbi.bin 
Writing memory
Response status = 0 (0x0) Success.
Response word 1 = 14284 (0x37cc)
Master boot image has been written

7. HSM teardown#

Last step is to stop custom HSM. In order to do that, open again the HSM Setup notebook and stop the running jupyter notebook code cell.