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