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