Secure Binary 2.1#

The Secure Binary (SB) image format is a command-based firmware update image. It has a long history, and has been used on multiple STMP, i.MX, and Kinetis devices.

The SB 2.0 and 2.1 file format also uses AES encryption for confidentiality and HMAC for extending trust from the signed part of the SB file to the command and data part of the SB file. These two keys (AES decrypt key and HMAC key) are wrapped in the RFC3394 key blob, for which the key wrapping key is the SBKEK key

Refer to the chapter “Secure Binary 2.1” in documentation for more info.

1. Prerequisites#

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

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#
# Copyright 2021-2024 NXP
#
# SPDX-License-Identifier: BSD-3-Clause

import os
from binascii import unhexlify

from spsdk.sbfile.sb2.images import BootImageV21

WORKSPACE = "workspace/"
DATA_DIR = "../_data/"
KEK_VALUE = unhexlify("AC701E99BD3492E419B756EADC0985B3D3D0BC0FDB6B057AA88252204C2DA732")

2. Secure Binary 2.1#

2.1 Parsing#

In this section, we will explore how to parse a Secure Boot (SB) version 2.1 using the SPSDK API. As an input we will use a simple SB file as well as OTFAD encrypted SB file.

# parse simple SB2.1 file
with open(os.path.join(DATA_DIR, "test_output_sb_2_1.sb2"), "rb") as f:
    sb_file = f.read()
img_obj21 = BootImageV21.parse(sb_file, kek=KEK_VALUE)
print("SB2.1 file\n", str(img_obj21))

# parse SB2.1 file with OTFAD
with open(os.path.join(DATA_DIR, "test_output_sb_2_1_otfad.sb2"), "rb") as f:
    sb_file = f.read()
img_obj21 = BootImageV21.parse(sb_file, kek=KEK_VALUE)
print("SB2.1 file with OTFAD\n", str(img_obj21))
SB2.1 file
 
:::::::::::::::::::::::::::::::::: IMAGE HEADER ::::::::::::::::::::::::::::::::::::::
 Version:              2.1
 Digest:               F4C1EE64A207665F0CB0A700701C7471
 Flag:                 0x8008 (Unsigned)
 Image Blocks:         238
 First Boot Tag Block: 106
 First Boot SectionID: 0
 Offset to Cert Block: 208
 Key Blob Block:       8
 Header Blocks:        6
 Sections MAC Count:   10
 Key Blob Block Count: 5
 Timestamp:            14:48:09 (14.02.2020)
 Product Version:      1.0.0
 Component Version:    1.0.0
 Build Number:         1
::::::::::::::::::::::::::::::: CERTIFICATES BLOCK ::::::::::::::::::::::::::::::::::::
 CB Version:           1.0
 CB Flags:             0
 CB Build Number:      1
 CB Image Length:      1440
 CB Cert. Count:       1
 CB Cert. Length:      1064
 Public Root Keys Hash e.g. RKH (SHA256):
  0) 49AD24EB3D2BDD52A8EF1BDFCA612D531061FC1376FFD4AC56457ED08380A627 <- Used
  1) 0000000000000000000000000000000000000000000000000000000000000000 
  2) 0000000000000000000000000000000000000000000000000000000000000000 
  3) 0000000000000000000000000000000000000000000000000000000000000000 
 RKTH (SHA256): DB31D46C717711A8231CBC38B1DE8A6E8657E1F733E04C2EE4B62FCEA59149FA
  - RKTH fuse [256:225]: 6CD431DB
  - RKTH fuse [224:193]: A8117771
  - RKTH fuse [192:161]: 38BC1C23
  - RKTH fuse [160:129]: 6E8ADEB1
  - RKTH fuse [128:097]: F7E15786
  - RKTH fuse [096:065]: 2E4CE033
  - RKTH fuse [064:033]: CE2FB6E4
  - RKTH fuse [032:001]: FA4991A5
 Root Certificate:
  Certification Authority:    NO
  Serial Number:              0x3cc30000babadeda
  Validity Range:             06.05.2019 (14:01:02) - 01.05.2039 (14:01:02)
  Signature Algorithm:        sha256
  Self Issued:                YES
::::::::::::::::::::::::::::::::::: BOOT SECTIONS ::::::::::::::::::::::::::::::::::::
[ SECTION: 0 | UID: 0x00000000 ]
 0) ERASE: Address=0x00000000, Length=10240, Flags=0x00000000, MemId=0x00000000
 1) LOAD: Address=0x00000000, DataLen=1696, Flags=0x00000000, MemId=0x00000000
 2) Command: tag=RESET, flags=0x0000, address=0x00000000, count=0x00000000, data=0x00000000


SB2.1 file with OTFAD
 
:::::::::::::::::::::::::::::::::: IMAGE HEADER ::::::::::::::::::::::::::::::::::::::
 Version:              2.1
 Digest:               642E843BBA7F7102C94A2334B0D0E37F
 Flag:                 0x8008 (Unsigned)
 Image Blocks:         259
 First Boot Tag Block: 106
 First Boot SectionID: 0
 Offset to Cert Block: 208
 Key Blob Block:       8
 Header Blocks:        6
 Sections MAC Count:   1
 Key Blob Block Count: 5
 Timestamp:            15:23:34 (25.02.2020)
 Product Version:      1.0.0
 Component Version:    1.0.0
 Build Number:         1
::::::::::::::::::::::::::::::: CERTIFICATES BLOCK ::::::::::::::::::::::::::::::::::::
 CB Version:           1.0
 CB Flags:             0
 CB Build Number:      1
 CB Image Length:      1440
 CB Cert. Count:       1
 CB Cert. Length:      1064
 Public Root Keys Hash e.g. RKH (SHA256):
  0) 49AD24EB3D2BDD52A8EF1BDFCA612D531061FC1376FFD4AC56457ED08380A627 <- Used
  1) 0000000000000000000000000000000000000000000000000000000000000000 
  2) 0000000000000000000000000000000000000000000000000000000000000000 
  3) 0000000000000000000000000000000000000000000000000000000000000000 
 RKTH (SHA256): DB31D46C717711A8231CBC38B1DE8A6E8657E1F733E04C2EE4B62FCEA59149FA
  - RKTH fuse [256:225]: 6CD431DB
  - RKTH fuse [224:193]: A8117771
  - RKTH fuse [192:161]: 38BC1C23
  - RKTH fuse [160:129]: 6E8ADEB1
  - RKTH fuse [128:097]: F7E15786
  - RKTH fuse [096:065]: 2E4CE033
  - RKTH fuse [064:033]: CE2FB6E4
  - RKTH fuse [032:001]: FA4991A5
 Root Certificate:
  Certification Authority:    NO
  Serial Number:              0x3cc30000babadeda
  Validity Range:             06.05.2019 (14:01:02) - 01.05.2039 (14:01:02)
  Signature Algorithm:        sha256
  Self Issued:                YES
::::::::::::::::::::::::::::::::::: BOOT SECTIONS ::::::::::::::::::::::::::::::::::::
[ SECTION: 0 | UID: 0x00000000 ]
 0) ERASE: Address=0x08001000, Length=57344, Flags=0x00000000, MemId=0x00000000
 1) LOAD: Address=0x08001000, DataLen=2048, Flags=0x00000000, MemId=0x00000000
 2) LOAD: Address=0x08000000, DataLen=256, Flags=0x00000000, MemId=0x00000000
 3) Command: tag=RESET, flags=0x0000, address=0x00000000, count=0x00000000, data=0x00000000

2.2 Generation#

In this chapter, we explore the process of generating Secure Boot (SB) files using the SPSDK API, focusing on two methods: one without encryption and the other utilizing OTFAD (On-the-Fly Encryption) for enhanced security.

2.2.1 Simple SB file#

from spsdk.crypto.certificate import Certificate
from spsdk.crypto.hash import get_hash
from spsdk.crypto.signature_provider import get_signature_provider
from spsdk.exceptions import SPSDKError
from spsdk.sbfile.sb2.commands import CmdErase, CmdLoad, CmdReset
from spsdk.sbfile.sb2.images import BootSectionV2, SBV2xAdvancedParams
from spsdk.utils.crypto.cert_blocks import CertBlockV1
from spsdk.utils.crypto.otfad import KeyBlob, Otfad
from spsdk.utils.misc import align_block

DEK_VALUE = b"\xA0" * 32  # it is recommended to use random value
MAC_VALUE = b"\x0B" * 32  # it is recommended to use random value

PRIVATE_KEY_PATH = os.path.join(DATA_DIR, "selfsign_privatekey_rsa2048.pem")
BOOT_IMG_PATH = os.path.join(DATA_DIR, "boot_image.bin")

########################################################################################################################
# Boot Section
########################################################################################################################


def gen_boot_section() -> BootSectionV2:
    """Generate a Boot Section without encryption."""
    with open(BOOT_IMG_PATH, "rb") as boot_image_file:
        boot_data = boot_image_file.read()

    boot_section = BootSectionV2(
        0,
        CmdErase(address=0, length=100000),
        CmdLoad(address=0, data=boot_data),
        CmdReset(),
        hmac_count=10,
    )

    return boot_section


########################################################################################################################
# Certificate Section
########################################################################################################################


def gen_cert_block() -> CertBlockV1:
    """Generate a Certification Block."""
    cert_obj = Certificate.load(os.path.join(DATA_DIR, "selfsign_v3.der.crt"))
    root_key = get_hash(cert_obj.get_public_key().export())

    cert_block = CertBlockV1()
    cert_block.set_root_key_hash(0, root_key)
    cert_block.add_certificate(cert_obj)
    return cert_block


########################################################################################################################
# Boot Image
########################################################################################################################


def gen_boot_image_21(boot_section: BootImageV21) -> BootImageV21:
    """Generate SB2.1 image with signature."""
    # advanced parameters
    adv_params = SBV2xAdvancedParams(dek=DEK_VALUE, mac=MAC_VALUE)
    # create boot image
    boot_image = BootImageV21(
        KEK_VALUE,
        boot_section,
        product_version="1.0.0",
        component_version="1.0.0",
        build_number=1,
        advanced_params=adv_params,
    )

    # add certificate block
    boot_image.cert_block = gen_cert_block()
    signature_provider = get_signature_provider(local_file_key=PRIVATE_KEY_PATH)
    boot_image.signature_provider = signature_provider
    # print image info
    return boot_image


# Generate SB21 raw image
boot_section = gen_boot_section()
sb21_signed = gen_boot_image_21(boot_section)

# Now, let's try to parse just the generated raw SB file from previous step.
img_obj21 = BootImageV21.parse(sb21_signed.export(), kek=KEK_VALUE)
print("Raw image\n", str(img_obj21))
Raw image
 
:::::::::::::::::::::::::::::::::: IMAGE HEADER ::::::::::::::::::::::::::::::::::::::
 Version:              2.1
 Digest:               3151D16DDFB3388DCE660B03004EACB2
 Flag:                 0x8008 (Unsigned)
 Image Blocks:         242
 First Boot Tag Block: 110
 First Boot SectionID: 0
 Offset to Cert Block: 208
 Key Blob Block:       8
 Header Blocks:        6
 Sections MAC Count:   10
 Key Blob Block Count: 5
 Timestamp:            14:07:01 (12.11.2024)
 Product Version:      1.0.0
 Component Version:    1.0.0
 Build Number:         1
::::::::::::::::::::::::::::::: CERTIFICATES BLOCK ::::::::::::::::::::::::::::::::::::
 CB Version:           1.0
 CB Flags:             0
 CB Build Number:      1
 CB Image Length:      1504
 CB Cert. Count:       1
 CB Cert. Length:      1124
 Public Root Keys Hash e.g. RKH (SHA256):
  0) 4CDDC64BFE422D67F6F689E5389E7CE08BDBC425849C76961D6BD6D05787F9AB <- Used
  1) 0000000000000000000000000000000000000000000000000000000000000000 
  2) 0000000000000000000000000000000000000000000000000000000000000000 
  3) 0000000000000000000000000000000000000000000000000000000000000000 
 RKTH (SHA256): EFABFFED4574ED9865D705123C5CF1933C1A441CD74BC08D931CF1F019586436
  - RKTH fuse [256:225]: EDFFABEF
  - RKTH fuse [224:193]: 98ED7445
  - RKTH fuse [192:161]: 1205D765
  - RKTH fuse [160:129]: 93F15C3C
  - RKTH fuse [128:097]: 1C441A3C
  - RKTH fuse [096:065]: 8DC04BD7
  - RKTH fuse [064:033]: F0F11C93
  - RKTH fuse [032:001]: 36645819
 Root Certificate:
  Certification Authority:    NO
  Serial Number:              0x3cc30000babadeda
  Validity Range:             07.02.2018 (08:30:04) - 07.02.2019 (08:30:04)
  Signature Algorithm:        sha256
  Self Issued:                YES
::::::::::::::::::::::::::::::::::: BOOT SECTIONS ::::::::::::::::::::::::::::::::::::
[ SECTION: 0 | UID: 0x00000000 ]
 0) ERASE: Address=0x00000000, Length=100000, Flags=0x00000000, MemId=0x00000000
 1) LOAD: Address=0x00000000, DataLen=1696, Flags=0x00000000, MemId=0x00000000
 2) Command: tag=RESET, flags=0x0000, address=0x00000000, count=0x00000000, data=0x00000000

2.2.2 OTFAD Encrypted SB file#

def gen_boot_section_otfad() -> BootSectionV2:
    """Generate a Boot Section with content encrypted by OTFAD.

    :raises SPSDKError: When length of key blobs is not 256
    """
    with open(BOOT_IMG_PATH, "rb") as boot_image_file:
        boot_data = boot_image_file.read()

    otfad = Otfad()
    key = bytes.fromhex("B1A0C56AF31E98CD6936A79D9E6F829D")
    counter = bytes.fromhex("5689fab8b4bfb264")
    otfad.add_key_blob(
        KeyBlob(
            0x08001000,
            0x0800F3FF,
            key,
            counter,
            zero_fill=bytes(4),
            crc=bytes(4),
        )
    )  # zero_fill and crc should be used only for testing !
    enc_image = otfad.encrypt_image(align_block(boot_data, 512), 0x08001000, True)
    key_blobs = otfad.encrypt_key_blobs(kek=bytes.fromhex("50F66BB4F23B855DCD8FEFC0DA59E963"))
    if len(key_blobs) != 256:
        raise SPSDKError("Length of key blobs is not 256")

    boot_section = BootSectionV2(
        0,
        CmdErase(address=0x08001000, length=0x0800F000 - 0x08001000),
        CmdLoad(address=0x08001000, data=enc_image),
        CmdLoad(address=0x08000000, data=key_blobs),
        CmdReset(),
        hmac_count=10,
    )
    return boot_section


# Generate SB21 OTFAD encrypted raw image
boot_section = gen_boot_section()
sb21_signed = gen_boot_image_21(boot_section)

# Now, let's try to parse just the generated raw SB file from previous step.
img_obj21 = BootImageV21.parse(sb21_signed.export(), kek=KEK_VALUE)
print("Raw image\n", str(img_obj21))
Raw image
 
:::::::::::::::::::::::::::::::::: IMAGE HEADER ::::::::::::::::::::::::::::::::::::::
 Version:              2.1
 Digest:               5A8F6F7EAA9D004CA333E14EBD518252
 Flag:                 0x8008 (Unsigned)
 Image Blocks:         242
 First Boot Tag Block: 110
 First Boot SectionID: 0
 Offset to Cert Block: 208
 Key Blob Block:       8
 Header Blocks:        6
 Sections MAC Count:   10
 Key Blob Block Count: 5
 Timestamp:            14:31:17 (11.11.2024)
 Product Version:      1.0.0
 Component Version:    1.0.0
 Build Number:         1
::::::::::::::::::::::::::::::: CERTIFICATES BLOCK ::::::::::::::::::::::::::::::::::::
 CB Version:           1.0
 CB Flags:             0
 CB Build Number:      1
 CB Image Length:      1504
 CB Cert. Count:       1
 CB Cert. Length:      1124
 Public Root Keys Hash e.g. RKH (SHA256):
  0) 4CDDC64BFE422D67F6F689E5389E7CE08BDBC425849C76961D6BD6D05787F9AB <- Used
  1) 0000000000000000000000000000000000000000000000000000000000000000 
  2) 0000000000000000000000000000000000000000000000000000000000000000 
  3) 0000000000000000000000000000000000000000000000000000000000000000 
 RKTH (SHA256): EFABFFED4574ED9865D705123C5CF1933C1A441CD74BC08D931CF1F019586436
  - RKTH fuse [256:225]: EDFFABEF
  - RKTH fuse [224:193]: 98ED7445
  - RKTH fuse [192:161]: 1205D765
  - RKTH fuse [160:129]: 93F15C3C
  - RKTH fuse [128:097]: 1C441A3C
  - RKTH fuse [096:065]: 8DC04BD7
  - RKTH fuse [064:033]: F0F11C93
  - RKTH fuse [032:001]: 36645819
 Root Certificate:
  Certification Authority:    NO
  Serial Number:              0x3cc30000babadeda
  Validity Range:             07.02.2018 (08:30:04) - 07.02.2019 (08:30:04)
  Signature Algorithm:        sha256
  Self Issued:                YES
::::::::::::::::::::::::::::::::::: BOOT SECTIONS ::::::::::::::::::::::::::::::::::::
[ SECTION: 0 | UID: 0x00000000 ]
 0) ERASE: Address=0x00000000, Length=100000, Flags=0x00000000, MemId=0x00000000
 1) LOAD: Address=0x00000000, DataLen=1696, Flags=0x00000000, MemId=0x00000000
 2) Command: tag=RESET, flags=0x0000, address=0x00000000, count=0x00000000, data=0x00000000