Vom CMake-Build zur SBOM: Trusted Firmware-M auf STM32H5

| Ritesh Noronha

Code to Image SBOM

Wir haben kürzlich mit lynkctl CycloneDX-SBOMs für einen echten Trusted-Firmware-M-Build auf einem STM32H5-Board erzeugt. Der Build macht ein häufiges Problem von Embedded-Firmware greifbar: Die meisten SBOMs beschreiben, was in einem Repository vorhanden ist, nicht, was tatsächlich im Image ausgeliefert wird.

Gebaut ist nicht ausgeliefert

In einem Embedded-Projekt kann der Quellbaum Vendor-SDKs, Board-Support-Packages, RTOS-Ports, HAL-Treiber, Krypto-Bibliotheken, Netzwerk-Stacks, Test-Utilities, generierte Dateien, Beispielanwendungen und alten Kompatibilitätscode enthalten. Ein CMake-Target referenziert nur einen Ausschnitt davon. Der Compiler baut weniger, als der Baum enthält. Der Linker behält weniger, als der Compiler erzeugt hat.

Für ein CMake-basiertes Embedded-Projekt denke ich in vier Schichten:

  1. Code, der im Repository vorhanden ist.

  2. Code, der von der gewählten CMake-Konfiguration referenziert wird.

  3. Code, der zu Objekten und Archiven kompiliert wird.

  4. Code, der tatsächlich zum finalen Firmware-Artefakt beiträgt.

Die meisten False Positives entstehen, wenn man diese Schichten zu einer einzigen Antwort zusammenfasst.

Wenn ein Schwachstellen-Scanner ein Produkt markiert, weil eine verwundbare Komponente in einem gemeinsam genutzten third_party-Ordner existiert, hat das Security-Team nun Arbeit vor sich. Jemand muss nachweisen, ob dieser Code für dieses Gerät erreichbar, kompiliert, gelinkt oder gänzlich ungenutzt ist. Im Flottenmaßstab werden diese False Positives teuer.

Der umgekehrte Fall tritt ebenfalls auf. Ein Build kann ein statisches Archiv, eine Toolchain-Runtime, eine generierte Quelldatei oder ein vom Hersteller geliefertes Objekt einbinden, das in keinem Paket-Manifest auftaucht. Wenn die SBOM nur Manifeste oder offensichtliche Verzeichnisse scannt, kann sie Dinge übersehen, die tatsächlich ausgeliefert werden.

Für Embedded-Arbeit bedeutet Genauigkeit nicht einfach mehr Komponenten. Genauigkeit bedeutet zu wissen, welche Evidenz jede Komponente stützt.

Warum Quellcode-Scanning für einen CMake-Build nicht ausreicht

Das Scannen des Quellbaums ist der übliche Ausgangspunkt. Werkzeuge durchlaufen das Repository, finden Paket-Manifeste und mitgelieferten Quellcode und geben CycloneDX oder SPDX aus. Die Schwäche liegt nicht darin, dass Scanning schlecht wäre. Scanning erkennt Vorhandensein, nicht Auslieferung. Wenn third_party/mbedtls im Baum existiert, ist das eine nützliche Information. Es bedeutet nicht, dass mbedTLS für dieses Board kompiliert, in diese Firmware gelinkt oder nach der Dead-Code-Elimination beibehalten wurde.

Für ein CMake-Projekt weiß der Build bereits mehr als das Dateisystem: die gewählte Konfiguration, den Target-Graphen, Compile-Flags, Include-Pfade, Link-Eingaben und Artefakte. Das Open-Source-Projekt cmake-sbom nimmt das ernst. Es integriert sich in CMake und erzeugt SPDX aus Install-Artefakten und expliziten sbom_add-Deklarationen. Der Kompromiss ist, dass es davon abhängt, was das Projekt explizit deklariert – und TF-M deklariert sich nicht auf diese Weise. Es erbt Vendor-CMake, STMicro-Board-Skripte, generierte Dateien und statische Archive, die in den Build-Baum geladen werden – nichts davon für die SBOM-Erzeugung konzipiert.

Genau für diesen Fall ist lynkctl gebaut.

Der Build: Trusted Firmware-M auf STM32H573I-DK

Trusted Firmware-M, meist als TF-M geschrieben, ist die Referenzimplementierung der Secure Processing Environment für Arm-Cortex-M-Geräte. Es baut auf dem Armv8-M-Sicherheitsmodell auf, einschließlich TrustZone, sofern die Hardware dies unterstützt.

TF-M bietet:

  • Secure Boot zur Authentifizierung von Firmware-Images.

  • Einen sicheren Runtime-Kern, der Isolation und Kommunikation verwaltet.

  • Sichere Dienste wie Krypto, Internal Trusted Storage, Protected Storage, Firmware-Update und Initial Attestation.

  • PSA-Functional-API-Schnittstellen, über die nicht-sichere Anwendungen sichere Dienste aufrufen können.

Es ist keine einzelne flache Anwendung. Es ist eine konfigurierte Firmware-Plattform mit Bootloader-Code, sicheren Partitionen, Vendor-Plattform-Support, generierten Dateien, Drittanbieter-Bibliotheken und Linker-Ausgaben.

Das Target für diesen Durchlauf war das Entwicklungskit STM32H573I-DK:

TFM_PLATFORM=stm/stm32h573i_dk
TFM_PLATFORM=stm/stm32h573i_dk
TFM_PLATFORM=stm/stm32h573i_dk

In den Plattform-Metadaten von TF-M ist es konfiguriert als:

TFM_SYSTEM_PROCESSOR=cortex-m33
TFM_SYSTEM_ARCHITECTURE=armv8-m.main
STM32H573xx
CRYPTO_HW_ACCELERATOR_TYPE=stm
TFM_SYSTEM_PROCESSOR=cortex-m33
TFM_SYSTEM_ARCHITECTURE=armv8-m.main
STM32H573xx
CRYPTO_HW_ACCELERATOR_TYPE=stm
TFM_SYSTEM_PROCESSOR=cortex-m33
TFM_SYSTEM_ARCHITECTURE=armv8-m.main
STM32H573xx
CRYPTO_HW_ACCELERATOR_TYPE=stm

Die Plattform aktiviert TrustZone und verwendet MCUboot als Bootloader-Pfad. Sie aktiviert außerdem die sicheren Dienste, die für ein repräsentatives Image relevant sind: Crypto, Internal Trusted Storage, Protected Storage, Initial Attestation, die Platform-Partition und Firmware Update.

Der Build erzeugt mehr als ein Firmware-Artefakt: ein Bootloader-Image und ein sicheres TF-M-Image, jeweils mit eigener Linker-Map:

build/stm32h5-gnu-brew-newlib/bin/bl2.axf
build/stm32h5-gnu-brew-newlib/bin/tfm_s.axf
build/stm32h5-gnu-brew-newlib/bin/bl2.map
build/stm32h5-gnu-brew-newlib/bin/tfm_s.map
build/stm32h5-gnu-brew-newlib/bin/bl2.axf
build/stm32h5-gnu-brew-newlib/bin/tfm_s.axf
build/stm32h5-gnu-brew-newlib/bin/bl2.map
build/stm32h5-gnu-brew-newlib/bin/tfm_s.map
build/stm32h5-gnu-brew-newlib/bin/bl2.axf
build/stm32h5-gnu-brew-newlib/bin/tfm_s.axf
build/stm32h5-gnu-brew-newlib/bin/bl2.map
build/stm32h5-gnu-brew-newlib/bin/tfm_s.map

Diese Unterscheidung ist für die SBOM-Erzeugung entscheidend. Eine Map-Datei ist image-spezifisch. Wenn wir Dead-Code-Abgleich und Genauigkeit auf Ebene des gelinkten Images wollen, sind Bootloader und sichere Firmware zwei SBOM-Subjekte, nicht eines.

Erst die Firmware bauen, dann erzeugen

Der SBOM-Durchlauf erfolgt nach dem Konfigurieren und Bauen der Firmware. Das gibt lynkctl echte Build-Metadaten, statt ihn zu zwingen, allein aus dem Quellcode zu schließen.

Wir haben die GNU-Arm-Embedded-Toolchain verwendet:

TFM_TOOLCHAIN_FILE=toolchain_GNUARM.cmake
CROSS_COMPILE=arm-none-eabi
TFM_TOOLCHAIN_FILE=toolchain_GNUARM.cmake
CROSS_COMPILE=arm-none-eabi
TFM_TOOLCHAIN_FILE=toolchain_GNUARM.cmake
CROSS_COMPILE=arm-none-eabi
cmake -S . -B build/stm32h5-gnu-brew-newlib -GNinja \
  -DTFM_PLATFORM=stm/stm32h573i_dk \
  -DTFM_TOOLCHAIN_FILE=toolchain_GNUARM.cmake \
  -DCROSS_COMPILE=arm-none-eabi

ninja -C
cmake -S . -B build/stm32h5-gnu-brew-newlib -GNinja \
  -DTFM_PLATFORM=stm/stm32h573i_dk \
  -DTFM_TOOLCHAIN_FILE=toolchain_GNUARM.cmake \
  -DCROSS_COMPILE=arm-none-eabi

ninja -C
cmake -S . -B build/stm32h5-gnu-brew-newlib -GNinja \
  -DTFM_PLATFORM=stm/stm32h573i_dk \
  -DTFM_TOOLCHAIN_FILE=toolchain_GNUARM.cmake \
  -DCROSS_COMPILE=arm-none-eabi

ninja -C


Zwei SBOMs erzeugen

Jedes gelinkte Image hat seine eigene Map-Datei, daher haben wir zwei SBOMs erzeugt:

build/sboms/stm32h5-bl2.cdx.json
build/sboms/stm32h5-tfm_s.cdx.json
build/sboms/stm32h5-bl2.cdx.json
build/sboms/stm32h5-tfm_s.cdx.json
build/sboms/stm32h5-bl2.cdx.json
build/sboms/stm32h5-tfm_s.cdx.json

Damit bleibt das SBOM-Subjekt am Binär-Subjekt ausgerichtet. Bootloader und sichere Runtime sind nicht identisch: Sie teilen sich einen Teil der Build-Umgebung und der Abhängigkeits-Evidenz, aber jedes Image hat seinen eigenen Target-Graphen, gelinkte Inhalte und seine eigene Runtime-Rolle. Sie in einer einzigen map-gesteuerten SBOM zusammenzufassen würde diese Grenze verwischen.

Diese Befehle wurden aus dem TF-M-Quell-Root ausgeführt, wobei --project-root gesetzt war, um die Quellpfade am TF-M-Root zu verankern. Wir haben den CMake-Provider explizit verwendet, das konfigurierte Build-Verzeichnis übergeben und explizite --vendored-root-Werte übergeben. Das ist hier wichtig, weil mehrere Abhängigkeiten in den Build-Baum unter build/stm32h5-gnu-brew-newlib/lib/ext/*-src geladen werden. Diese Roots sind die Paketgrenzen für MCUboot, TF-PSA-Crypto, CMSIS_6, QCBOR und t_cose.

Für den Bootloader:

lynkctl generate . \
  --provider cmake \
  --cmake-build-dir build/stm32h5-gnu-brew-newlib \
  --cmake-target bl2 \
  --map-file build/stm32h5-gnu-brew-newlib/bin/bl2.map \
  --project-root /src/trusted-firmware-m \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/mcuboot-src \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/tf-psa-crypto-src \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/cmsis-src \
  --evidence \
  -o build/sboms/stm32h5-bl2.cdx.json \
  -v
lynkctl generate . \
  --provider cmake \
  --cmake-build-dir build/stm32h5-gnu-brew-newlib \
  --cmake-target bl2 \
  --map-file build/stm32h5-gnu-brew-newlib/bin/bl2.map \
  --project-root /src/trusted-firmware-m \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/mcuboot-src \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/tf-psa-crypto-src \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/cmsis-src \
  --evidence \
  -o build/sboms/stm32h5-bl2.cdx.json \
  -v
lynkctl generate . \
  --provider cmake \
  --cmake-build-dir build/stm32h5-gnu-brew-newlib \
  --cmake-target bl2 \
  --map-file build/stm32h5-gnu-brew-newlib/bin/bl2.map \
  --project-root /src/trusted-firmware-m \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/mcuboot-src \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/tf-psa-crypto-src \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/cmsis-src \
  --evidence \
  -o build/sboms/stm32h5-bl2.cdx.json \
  -v

Für das sichere Firmware-Image:

lynkctl generate . \
  --provider cmake \
  --cmake-build-dir build/stm32h5-gnu-brew-newlib \
  --cmake-target tfm_s \
  --map-file build/stm32h5-gnu-brew-newlib/bin/tfm_s.map \
  --project-root /src/trusted-firmware-m \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/mcuboot-src \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/tf-psa-crypto-src \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/qcbor-src \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/t_cose-src \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/cmsis-src \
  --evidence \
  -o build/sboms/stm32h5-tfm_s.cdx.json \
  -v
lynkctl generate . \
  --provider cmake \
  --cmake-build-dir build/stm32h5-gnu-brew-newlib \
  --cmake-target tfm_s \
  --map-file build/stm32h5-gnu-brew-newlib/bin/tfm_s.map \
  --project-root /src/trusted-firmware-m \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/mcuboot-src \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/tf-psa-crypto-src \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/qcbor-src \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/t_cose-src \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/cmsis-src \
  --evidence \
  -o build/sboms/stm32h5-tfm_s.cdx.json \
  -v
lynkctl generate . \
  --provider cmake \
  --cmake-build-dir build/stm32h5-gnu-brew-newlib \
  --cmake-target tfm_s \
  --map-file build/stm32h5-gnu-brew-newlib/bin/tfm_s.map \
  --project-root /src/trusted-firmware-m \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/mcuboot-src \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/tf-psa-crypto-src \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/qcbor-src \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/t_cose-src \
  --vendored-root build/stm32h5-gnu-brew-newlib/lib/ext/cmsis-src \
  --evidence \
  -o build/sboms/stm32h5-tfm_s.cdx.json \
  -v

Ergebnisse

Die Bootloader-SBOM wurde mit 10 Komponenten abgeschlossen:

SBOM generated: 10 components, 5 warnings, 0 errors
selected CMake configuration "MinSizeRel"
selected CMake target "bl2"
SBOM generated: 10 components, 5 warnings, 0 errors
selected CMake configuration "MinSizeRel"
selected CMake target "bl2"
SBOM generated: 10 components, 5 warnings, 0 errors
selected CMake configuration "MinSizeRel"
selected CMake target "bl2"

Die SBOM der sicheren Firmware wurde mit 20 Komponenten abgeschlossen:

SBOM generated: 20 components, 5 warnings, 0 errors
selected CMake configuration "MinSizeRel"
selected CMake target "tfm_s"
SBOM generated: 20 components, 5 warnings, 0 errors
selected CMake configuration "MinSizeRel"
selected CMake target "tfm_s"
SBOM generated: 20 components, 5 warnings, 0 errors
selected CMake configuration "MinSizeRel"
selected CMake target "tfm_s"

Beide sind CycloneDX-1.6-JSON-Dokumente. Bei den Warnungen handelt es sich um OSS-Index-Grenzwarnungen für Content-Range- oder Alias-Übereinstimmungen wie tinycrypt, mbedtls, tf-psa-crypto, mbedtls-framework und cddl_gen. Die Komponenten-Identitäten wurden dennoch durch die expliziten Vendored-Roots und die Anreicherung aus dem Git-Checkout verbessert. Das ist ein Bereich zur diagnostischen Bereinigung, kein Blocker für den SBOM-Inhalt.

Das Komponenteninventar

Nach der Erzeugung der SBOMs haben wir die Komponentenliste aus jedem CycloneDX-Dokument geprüft. Die vollständigen SBOMs enthalten das komplette Inventar und die Evidenz-Annotationen; für den Artikel sind die wichtigen Einträge die Open-Source- und Runtime-Komponenten, die Review und Triage beeinflussen.

Komponente

Gesehen in

Warum sie wichtig ist

TF-PSA-Crypto

bl2, tfm_s

Krypto-Implementierung, die von Bootloader und sicherem Image verwendet wird.

MCUboot

bl2, tfm_s

Bootloader-Code und Firmware-Update-Pfad.

CMSIS_6

bl2, tfm_s

Arm-Cortex-M-Support-Schicht, die vom Plattform-Build verwendet wird.

STM32H5 HAL driver

bl2, tfm_s

STMicro-Plattform-Treibercode für die gewählte Board-Familie.

CMSIS Device H5

bl2, tfm_s

STM32H5-Geräte-Support hinter den Plattform-Targets.

QCBOR

tfm_s

CBOR-Kodierung, die von Attestation-bezogenem Code der sicheren Firmware verwendet wird.

t_cose

tfm_s

COSE-Support, der von Attestation-bezogenem Code der sicheren Firmware verwendet wird.

GCC-Runtime-Support (libgcc.a)

bl2, tfm_s

Runtime-Archiv, das in das Firmware-Image gelinkt wird, verschieden von der ausführbaren Compiler-Datei.

Die vollständigen Inventare enthalten außerdem First-Party-TF-M-Targets wie tfm_spm, tfm_sprt und die sicheren Partitionen. Diese Zeilen sind in der SBOM nützlich, weil sie Build-Evidenz auf Target-Ebene bewahren, müssen hier aber nicht vollständig wiedergegeben werden. Build-Werkzeuge wie cmake, ninja und die Compiler-Frontends sind als excluded markiert, statt als gelinkter Runtime-Firmware-Inhalt gezählt zu werden.

Was uns dieser Build sagt

Der Bootloader und die sichere Runtime sind unterschiedliche Subjekte. bl2 linkt MCUboots bootutil, einen Krypto-Ausschnitt als bl2_crypto und die STM32H5-Plattform als platform_bl2. tfm_s linkt einen größeren Satz: die sicheren Partitionen (tfm_psa_rot_partition_*), die SPM- und SPRT-Runtime, QCBOR und t_cose für die Attestation-Kodierung sowie ein anderes Krypto-Service-Target. Eine einzelne zusammengeführte SBOM hätte diesen Unterschied verborgen.

Build-Werkzeuge sind als excluded gekennzeichnet. cmake, ninja und die beiden arm-none-eabi-Compiler-Frontends wurden erkannt, aber sie sind kein gelinkter Firmware-Inhalt und werden daher als außerhalb des Geltungsbereichs markiert, statt als ausgeliefert gezählt zu werden. Der eine Toolchain-Eintrag, der tatsächlich ausgeliefert wird – gcc als required –, ist dort, weil libgcc.a in das Image gelinkt wird. Das ist die Unterscheidung, die bei der Triage zählt: Ein Scanner, der „Compiler gefunden" genauso behandelt wie „Compiler ausgeliefert", erzeugt Rauschen.

Die Vendored-Roots lieferten echte Paket-Identität. TF-PSA-Crypto, MCUboot, CMSIS_6, QCBOR und t_cose wurden zu GitHub-PURLs mit Commit-genauen Versionen aufgelöst, weil wir der Enrichment-Pipeline gesagt haben, wo ihre Paketgrenzen im geladenen Build-Baum liegen. Ohne das taucht derselbe Code als anonymer mitgelieferter Quellcode auf.

Die STMicro-Plattform taucht als echter Inhalt auf. Dieses Board setzt CRYPTO_HW_ACCELERATOR_TYPE=stm, und diese Wahl ist in tfm_s als das Target crypto_service_crypto_hw sichtbar: Das sichere Image ist mit der Hardware-Krypto des STM32H5 verdrahtet, nicht mit einem reinen Software-Pfad. Der Rest der STMicro-Schicht löst sich zu versionierten STMicro-Repos auf – dem stm32h5xx_hal_driver und dem cmsis_device_h5-Geräte-Support hinter platform_bl2 und platform_s. Bei einem STM32-Build ist das genau der Code, nach dem ein Kunde oder Auditor fragen wird, daher sollte er eine echte Identität tragen, statt in einem unbenannten Board-Verzeichnis zu liegen.

Generierter Code ist schwierig. Vendor-Blobs sind schwierig. Gepatchte mitgelieferte Bäume sind schwierig. Proprietäre Toolchain-Bibliotheken sind schwierig. LTO kann Grenzen verschleiern. Veraltete Build-Verzeichnisse können lügen. Linker-Maps unterscheiden sich zwischen Toolchains.

Deshalb ist Diagnostik wichtig. Ein nützlicher SBOM-Generator sollte einem mitteilen, wenn Evidenz fehlt oder schwach ist, und unterscheiden, was er bestätigt hat, von dem, was er abgeleitet hat. Die OSS-Index-Warnungen in diesem Durchlauf sind ein Beispiel für diese Ehrlichkeit: Das Werkzeug hat gekennzeichnet, wo eine Content-Übereinstimmung unscharf war, statt eine saubere Identität zu behaupten, die es nicht hatte. Eine stille, übermäßig selbstsichere SBOM ist schlechter als eine laute, aber ehrliche.

Das Ziel ist belastbare Evidenz. Für CMake-basierte Embedded-Firmware beginnt die beste SBOM mit dem Build, wird durch Linker-Evidenz korrigiert und erst danach angereichert, wenn dieses Fundament solide ist.

Möchten Sie die tatsächlichen SBOMs?

Die beiden CycloneDX-1.6-Dokumente aus diesem Durchlauf, stm32h5-bl2.cdx.json und stm32h5-tfm_s.cdx.json, sind auf Anfrage verfügbar. Um die echten Komponentenlisten, Evidenz-Annotationen und Scope-Entscheidungen einzusehen, fordern Sie die SBOMs hier an oder schreiben Sie uns an hello@interlynk.io.

Vertraut von Sicherheits- und Compliance-Teams in 100+ regulierten Unternehmen

Sehen Sie sich Ihr richtig erstelltes SBOM an

Interlynk automatisiert SBOMs, verwaltet Open-Source-Risiken, überwacht Lieferanten und bereitet Sie auf die Post-Quanten-Ära vor – alles auf einer vertrauenswürdigen Plattform.

Vertraut von Sicherheits- und Compliance-Teams in 100+ regulierten Unternehmen

Interlynk automatisiert SBOMs, verwaltet Open-Source-Risiken, überwacht Lieferanten und bereitet Sie auf das Post-Quanten-Zeitalter vor – alles auf einer vertrauenswürdigen Plattform.

Audit-bereite SBOM. Bei jedem Build.

Vertraut von Sicherheits- und Compliance-Teams in 100+ regulierten Unternehmen

Interlynk automatisiert SBOMs, verwaltet Open-Source-Risiken, überwacht Lieferanten und bereitet Sie auf das Post-Quanten-Zeitalter vor – alles auf einer vertrauenswürdigen Plattform.

Audit-bereite SBOM. Bei jedem Build.