18 December 2025·11 min read

Generating a CRA-Compliant SBOM for Embedded Firmware

Generate a CRA-compliant SBOM for embedded firmware: handle binary blobs, vendor SDKs, and RTOS forks that break standard SBOM tools.


If you ask a firmware team about SBOMs, you'll get one of two responses: either they've been generating them via Yocto's create-spdx class for years and treat this as solved, or they have almost no idea where to start because half their firmware is binary blobs from chipset vendors.

The reality for most embedded product companies is somewhere in the middle—some components are tracked, others aren't, and the tracking that exists isn't machine-readable in any standard format.

The CRA is going to force resolution of this ambiguity. Annex I Part II, point 1 requires manufacturers to:

Identify and document vulnerabilities and components contained in the product with digital elements, including by drawing up a software bill of materials in a commonly used and machine-readable format covering at the very least the top-level dependencies of the products.

This post breaks down what the regulation actually requires, why firmware SBOM is harder than application SBOM, and how to implement it for Yocto-based and non-Yocto firmware builds.

What the Regulation Actually Requires

The CRA text is more permissive than many summaries suggest. Key points:

1. Machine-readable format, but no mandatory standard

The regulation says "commonly used and machine-readable format." It does not mandate SPDX or CycloneDX specifically. Both are acceptable; either is compliant. The format decision is yours to make based on your tooling ecosystem.

2. "At the very least top-level dependencies"

The SBOM must cover top-level dependencies as a minimum. However, ENISA guidance (2025) interprets "top-level dependencies" broadly for firmware: all libraries, packages, and components that are linked into or bundled with your firmware image count as top-level dependencies, regardless of whether they're direct or transitive.

The practical implication: you can't get away with listing only the packages in your CMakeLists.txt or Makefile and ignoring transitive dependencies. If a library you link pulls in three others and those are compiled into your binary, they need to be in the SBOM.

3. The SBOM is part of your technical documentation

Article 31 and Annex VII require the SBOM to be kept in the technical documentation file. It doesn't need to be published publicly, but it must be available to market surveillance authorities upon request. Many manufacturers are choosing to publish SBOMs alongside their products anyway (for supply chain transparency), but this isn't a CRA mandate.

4. NTIA minimum elements apply as a baseline

ENISA has cross-referenced the NTIA minimum SBOM elements (2021) as a baseline for what constitutes an adequate SBOM. These are:

  • Supplier name
  • Component name
  • Version of the component
  • Other unique identifiers (CPE, PURL)
  • Dependency relationships
  • Author of SBOM data
  • Timestamp

Your SBOM needs to cover at least these fields per component.

Why Firmware SBOM Is Harder Than Application SBOM

For web application or backend service teams, generating an SBOM is relatively straightforward: parse your package manager lock file (package-lock.json, Pipfile.lock, Cargo.lock, go.sum), generate an SPDX or CycloneDX document, done.

Firmware is different for four structural reasons:

1. Binary Blobs and Closed-Source Components

The most common hard problem: your Wi-Fi chipset vendor provides a pre-compiled binary library. You link it into your firmware. You don't have source code. You have a version number and a filename.

What can you include in the SBOM?

  • Component name: yes
  • Supplier: yes
  • Version: yes (if the vendor documents it, which isn't always the case)
  • Hash/checksum: yes (SHA-256 of the binary blob)
  • PURL: sometimes (if the vendor has a published CPE or package registry entry)
  • License: sometimes (if the vendor's distribution terms are documented)
  • Transitive dependencies of the blob: generally no

For binary blobs, you create an SBOM entry with what you know and mark the license and dependency information as NOASSERTION in SPDX or equivalent in CycloneDX. This is incomplete but defensible if you've made reasonable efforts to gather information.

The better long-term answer: contractual requirements on your chipset and SDK vendors to provide SBOMs for their binary deliverables. This is becoming a procurement requirement in the embedded industry.

2. Vendor SDKs with Unreleased Patches

Many embedded product teams use a vendor SDK that's built on top of an open-source project (FreeRTOS, lwIP, mbedTLS, etc.) but with proprietary patches applied. The vendor ships a versioned SDK, but that version doesn't correspond to any public release of the underlying component.

This creates an SBOM problem: your SBOM should reference the vendor SDK version (since that's what's actually in your firmware), but you also want to capture the underlying component versions for vulnerability matching.

The SPDX approach: include both entries—the vendor SDK package and the upstream component it's derived from, with a CONTAINS or DYNAMIC_LINK relationship. Many scanners won't generate this automatically; you'll need to add it manually or via tooling customization.

3. RTOS Forks and Custom Modifications

If your team has forked FreeRTOS, Zephyr, or another open-source RTOS and applied custom patches, your firmware contains a component that doesn't match any public release. This is common for embedded teams that need to backport security fixes or add hardware-specific support. For RTOS-specific SBOM guidance, see our Zephyr CRA compliance guide (which covers west manifest parsing) and FreeRTOS CRA compliance guide (which covers the vendor SDK dependency challenge).

For SBOM purposes:

  • List the upstream project as your component (e.g., FreeRTOS-Kernel)
  • Record your fork's base version (e.g., v11.0.1)
  • Add the patch commit hash or your internal version identifier
  • Mark it as a modified version

In SPDX: use the PackageComment field or the ExternalRef field to document the modification. In CycloneDX: use the pedigree element with patches to describe modifications.

4. Bootloaders and Secure Boot Chains

The full firmware image often includes components that aren't part of the main application firmware build: bootloaders (U-Boot, MCUboot, proprietary), secure boot verification code, trust anchor certificates, and provisioning tools. These need to be in your SBOM too. For details on which bootloader components to document and how secure boot chains work, see our secure boot and firmware signing guide.

For Yocto-based builds, the create-spdx class captures most of this if configured correctly. For custom build systems, you may need to add these components manually.

SPDX vs. CycloneDX: Which to Choose

Both formats are CRA-compliant. Choose based on your tooling ecosystem:

FeatureSPDX (ISO/IEC 5962:2021)CycloneDX (OWASP, v1.7+)
StandardisationISO/IEC international standardOWASP community standard
FormatsTag-value, JSON, RDF, YAML, XMLJSON, XML, Protobuf
Tooling ecosystemStrong in Linux/Yocto worldStrong in DevSecOps/cloud world
License expressionRich SPDX license syntaxSPDX license identifiers supported
VEX supportSPDX VEX (OpenVEX)Native VEX via vulnerabilities component
Dependency relationshipsDESCRIBES, CONTAINS, DEPENDS_ON, etc.dependsOn, provides, runtimeDependency
Yocto native supportYes (create-spdx class)Via external tools
Vulnerability matchingVia PURL, CPEVia PURL, CPE

Recommendation for embedded teams:

  • If you're using Yocto: use SPDX (native support, lower effort)
  • If you need rich VEX integration for vulnerability triage: consider CycloneDX
  • If you need to consume SBOMs from your chipset vendors: match their format

You can generate both—SPDX as your authoritative SBOM for the technical documentation file, CycloneDX for integration with vulnerability scanning tools.

Yocto: Setting Up create-spdx

The Yocto Project's create-spdx class (available since Kirkstone) generates SPDX 2.3 or SPDX 3.0 documents for your image. Here's a minimal configuration:

# In your local.conf or distro.conf

# Enable SPDX generation
INHERIT += "create-spdx"

# Output format (spdx-json, spdx-tag-value, or spdx-rdf)
SPDX_PRETTY = "1"

# Include security metadata (CVE checkpoints)
SPDX_INCLUDE_SOURCES = "1"

# Namespace for your SBOM (use your company domain)
SPDX_NAMESPACE_PREFIX = "https://sbom.yourcompany.com"

# Optional: generate per-package SBOMs in addition to image-level
SPDX_INCLUDE_PACKAGED = "1"

After a full image build, Yocto outputs SPDX documents to:

tmp/deploy/spdx/<machine>/<image>/

The top-level SPDX document describes the full image and includes DESCRIBES relationships to all packages. Each package document includes license information, source URIs, and checksums.

What create-spdx doesn't handle automatically:

  • Binary blob components (you'll need to add these manually or via a custom bbclass)
  • Components fetched via custom do_fetch scripts outside of Yocto's standard fetchers
  • Bootloader components if they're built outside the Yocto image (e.g., a separate U-Boot build)

For binary blobs, write a custom bbclass that adds them as SPDX packages with NOASSERTION for license fields:

# In your custom bbclass (e.g., wifi-blob-spdx.bbclass)
python do_spdx_wifi_blob() {
    import json
    spdx_pkg = {
        "SPDXID": "SPDXRef-wifi-firmware",
        "name": d.getVar("WIFI_BLOB_NAME"),
        "version": d.getVar("WIFI_BLOB_VERSION"),
        "supplier": "Organization: " + d.getVar("WIFI_BLOB_VENDOR"),
        "downloadLocation": "NOASSERTION",
        "filesAnalyzed": False,
        "checksums": [{"algorithm": "SHA256", "checksumValue": d.getVar("WIFI_BLOB_SHA256")}],
        "licenseConcluded": "NOASSERTION",
        "licenseDeclared": "NOASSERTION",
        "copyrightText": "NOASSERTION",
    }
    # Append to image SPDX document
    # (implementation depends on your Yocto version and SPDX class structure)
}

VEX: Reducing Triage Noise

An SBOM listing all your firmware components will generate a large number of CVE matches — many of which aren't actually exploitable in your firmware configuration. Research on SBOM-based vulnerability scanning suggests that the vast majority of CVE matches against firmware SBOMs are false positives — studies have shown baseline false-positive rates as high as 95%+ for downstream vulnerability scanners. The most common false-positive categories are: CVEs affecting a feature you don't use, a code path you've compiled out, or a library version your vendor has backported a fix into.

VEX (Vulnerability Exploitability eXchange) is a specification for expressing whether a known CVE is exploitable in a specific product configuration. A VEX document says, for each CVE matched against your SBOM:

  • Not Affected: The CVE affects this component, but not in the way your firmware uses it
  • Affected: The CVE is exploitable in your firmware
  • Fixed: The CVE was exploitable but has been addressed in the current version
  • Under Investigation: Not yet determined

For CRA compliance, VEX serves two functions:

  1. Internal triage: Filter your CVE matches to the ones that actually require action (Article 14 obligations only apply to actually-exploitable vulnerabilities)
  2. Customer communication: Share VEX documents with your customers so they can make accurate risk assessments without relying on raw CVE matches against your SBOM

VEX formats:

  • OpenVEX: Simple JSON-LD format, good for standalone VEX documents
  • SPDX VEX: Part of the SPDX 3.0 specification
  • CycloneDX VEX: Embedded in CycloneDX documents via the vulnerabilities element

For embedded product companies, the minimum viable VEX process:

  1. Run vulnerability scanning against your SBOM (using tools like Grype, OSV-Scanner, or ENISA's planned tooling)
  2. For each CVE match with CVSS ≥ 7.0: triage whether it's actually exploitable in your firmware
  3. Document the triage decision in a VEX document with justification
  4. Update the VEX document with each new firmware release

The VEX process naturally feeds into your Article 14 obligation: if your triage shows a CVE is actively exploited and affects your firmware, the 24-hour notification clock starts.

Pre-Deadline Checklist

Use this checklist to assess your current SBOM posture:

Foundation

  • Complete inventory of all components in your firmware image (open source, commercial, binary blobs)
  • Version numbers recorded for all components including binary blobs (where available)
  • Hash/checksum recorded for all binary blobs
  • Supplier information recorded for all components

Generation tooling

  • Automated SBOM generation integrated into your build pipeline (not a one-time manual exercise)
  • SBOM updated with every firmware release (not just when you remember to)
  • SBOM format is SPDX or CycloneDX (not a custom spreadsheet or proprietary format)
  • NTIA minimum elements present for all components

Hard cases addressed

  • Binary blobs documented with NOASSERTION for unknown fields (not omitted)
  • Vendor SDKs with upstream components documented at both levels
  • RTOS forks documented with base version and modification indicator
  • Bootloader and secure boot components included

VEX integration

  • CVE monitoring against SBOM running continuously
  • VEX triage process defined and documented
  • VEX documents generated and versioned alongside firmware releases
  • VEX documents available to customers on request

Compliance documentation

  • SBOM included in technical documentation file (Annex VII)
  • SBOM generation process documented as part of your security processes
  • Retention process in place (10 years from product placement on market)

If you're working through Yocto's create-spdx class and are dealing with binary blob gaps, the Stack Canary assessment tool will help identify which Annex I requirements remain open and prioritise your remediation work.


Based on Regulation EU 2024/2847 Annex I Part II, NTIA Minimum Elements for a Software Bill of Materials (2021), Yocto Project documentation, SPDX ISO/IEC 5962:2021, and OWASP CycloneDX specification v1.7. ENISA SBOM guidance for the CRA (2025). This does not constitute legal advice.

Sources


Check your CRA compliance status

Answer 7 questions about your embedded product and get a personalized gap analysis — with your CRA classification, key deadlines, and specific action items.

Start free assessment →