Secure Binary 2.1 Example

Secure Binary 2.1 Example#

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.

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

"""This example shows methods how to create Secure binary (SB) images.

- The SB file version 2.1 is used
- Boot image with and without a signature
"""

import os
from binascii import unhexlify

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 BootImageV21, 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

DATA_DIR = os.path.join("..", "_data", "sbfile")

########################################################################################################################
# Certificate Section
########################################################################################################################
def gen_cert_block() -> CertBlockV1:
    """Generate a Certification Block."""
    cert_obj = Certificate.load(f"{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 Section
########################################################################################################################
def gen_boot_section() -> BootSectionV2:
    """Generate a Boot Section without encryption."""
    with open(f"{DATA_DIR}/boot_image.bin", "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


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(f"{DATA_DIR}/boot_image.bin", "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


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

# Input values
KEK_VALUE = unhexlify("AC701E99BD3492E419B756EADC0985B3D3D0BC0FDB6B057AA88252204C2DA732")
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 = f"{DATA_DIR}/selfsign_privatekey_rsa2048.pem"


def gen_boot_image_21() -> bytes:
    """Generate SB2.1 image with signature."""
    # create boot section
    boot_section = gen_boot_section()
    # 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
    print(str(boot_image))

    return boot_image.export()
# parse simple SB2.1 file generated by elftosb.exe
with open(f"{DATA_DIR}/test_output_sb_2_1_from_elftosb.sb2", "rb") as f:
    sb_file = f.read()
img_obj21 = BootImageV21.parse(sb_file, kek=KEK_VALUE)
print("SB2.1 file generated by elftosb\n", str(img_obj21))
SB2.1 file generated by elftosb
 
:::::::::::::::::::::::::::::::::: 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
# parse SB2.1 file with OTFAD
with open(f"{DATA_DIR}/otfad/test_output_sb_2_1_from_elftosb_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 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
# Generate SB21 raw image
raw_data_sb21_signed = gen_boot_image_21()
:::::::::::::::::::::::::::::::::: IMAGE HEADER ::::::::::::::::::::::::::::::::::::::
 Version:              2.1
 Digest:               2981A9A767D82F88CB7BEB88295AF4A9
 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:            10:44:34 (02.01.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
 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
# Parse raw image
img_obj21 = BootImageV21.parse(raw_data_sb21_signed, kek=KEK_VALUE)
print("Raw image\n", str(img_obj21))
Raw image
 
:::::::::::::::::::::::::::::::::: IMAGE HEADER ::::::::::::::::::::::::::::::::::::::
 Version:              2.1
 Digest:               2981A9A767D82F88CB7BEB88295AF4A9
 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:            10:44:34 (02.01.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