Firmware SBoMs for open source projects

You might be surprised to hear that closed source firmware typically contains open source dependencies. In the case of EDK II (probably the BIOS of your x64 machine you’re using now) it’s about 20 different projects, and in the case of coreboot (hopefully the firmware of the machine you’ll own in the future) it’s about another 10 — some overlapping with EDK II. Examples here would be things like libjpeg (for the OEM splash image) or libssl (for crypto, but only the good kind).

It makes no sense for each person building firmware to write the same SBOM for the OSS code. Moving the SBOM upstream means it can be kept up to date by the same team writing the open source code. It’s very similar to what we encouraged desktop application developers to do with AppStream metadata a decade or so ago. That was wildly successful, so maybe we can do the same trick again here.

My proposal would to submit a bom.json to each upstream project in CycloneDX format, stored in a location amenable to the project — e.g. in ./contrib, ./data/sbom or even in the root project folder. The location isn’t important, only the file basename needs to be predictable.

Notice the CycloneDX word there not SPDX — the latter is great for open source license compliance, but I was only able to encode 43% of our “example firmware SBOM” into SPDX format, even with a lot of ugly hacks. I spent a long time trying to jam a round peg in a square hole and came to the conclusion it’s not going to work very well. SPDX works great as an export format to ensure license compliance (and the uswid CLI can already do that now…) but SPDX doesn’t work very well as a data source. CycloneDX is just a better designed format for a SBoM, sorry ISO.

Let’s assume we check in a new file to ~30 projects. With my upstream-maintainer hat on, nobody likes to manually edit yet-another-file when tagging releases, so I’m encouraging projects shipping a CycloneDX bom.json to use some of the auto-substituted tokens, e.g.

  • @VCS_TAG@git describe --tags --abbrev=0 e.g. 1.2.3
  • @VCS_VERSION@git describe --tags e.g. 1.2.3-250-gfa2371946
  • @VCS_BRANCH@git rev-parse --abbrev-ref HEAD e.g. staging
  • @VCS_COMMIT@git rev-parse HEAD e.g. 3090e61ee3452c0478860747de057c0269bfb7b6
  • @VCS_SBOM_AUTHORS@git shortlog -n -s -- bom.json e.g. Example User, Another User
  • @VCS_SBOM_AUTHOR@@VCS_SBOM_AUTHORS@[0] e.g. Example User

Using git in this way during the built process allows us to also “fixup” SBOM files with either missing details, or when the downstream ODM patches the project to do something upstream wouldn’t be happy with shipping upstream.

For fwupd (which I’m using as a cute example, it’s not built into firmware…) the bom.json file would be something like this:

{
  "bomFormat": "CycloneDX",
  "specVersion": "1.6",
  "version": 1,
  "components": [
    {
      "type": "library",
      "bom-ref": "pkg:github/fwupd/fwupd@@VCS_TAG@",
      "cpe": "cpe:2.3:a:fwupd:fwupd:@VCS_TAG@:*:*:*:*:*:*:*",
      "name": "fwupd",
      "version": "@VCS_VERSION@",
      "description": "Firmware update daemon",
      "authors": [
        {
          "name": "@VCS_SBOM_AUTHORS@"
        }
      ],
      "supplier": {
        "name": "fwupd developers",
        "url": [
          "https://github.com/fwupd/fwupd/blob/main/MAINTAINERS"
        ]
      },
      "licenses": [
        {
          "license": {
            "id": "LGPL-2.1-or-later"
          }
        }
      ],
      "externalReferences": [
        {
          "type": "website",
          "url": "https://fwupd.org/"
        },
        {
          "type": "vcs",
          "url": "https://github.com/fwupd/fwupd"
        }
      ]
    }
  ]
}

Putting it all together means we can do some pretty clever things assuming we have a recursive git checkout using either git modules, sub-modules or sub-projects:

$ uswid --find ~/Code/fwupd --fixup --save bom.json --verbose
Found:
 - ~/Code/fwupd/contrib/bom.json
 - ~/Code/fwupd/venv/build/contrib/bom.json
 - ~/Code/fwupd/subprojects/libjcat/contrib/spdx.json
Substitution required in ~/Code/fwupd/contrib/bom.json:
 - @VCS_TAG@ → 2.0.1
 - @VCS_VERSION@ → 2.0.1-253-gd27804fbb
Fixup required in ~/Code/fwupd/subprojects/libjcat/spdx.json:
 - Add VCS commit → db8822a01af89aa65a8d29c7110cc86d78a5d2b3
Additional dependencies added:
 - pkg:github/hughsie/libjcat@0.2.1 → pkg:github/hughsie/libxmlb@0.2.1
 - pkg:github/fwupd/fwupd@2.0.1 → pkg:github/hughsie/libjcat@0.2.1
~/Code/fwupd/venv/build/contrib/bom.json was merged into existing component pkg:github/fwupd/fwupd@2.0.1

And then we have a bom.json that we can use as an input file used for building the firmware blob. If we can convince EDK2 to merge the additional bom.json for each built module then it all works like magic, and we can build the 100% accurate external SBoM into the firmware binary itself with no additional work. Comments most welcome.

Published by

hughsie

Richard has over 10 years of experience developing open source software. He is the maintainer of GNOME Software, PackageKit, GNOME Packagekit, GNOME Power Manager, GNOME Color Manager, colord, and UPower and also contributes to many other projects and opensource standards. Richard has three main areas of interest on the free desktop, color management, package management, and power management. Richard graduated a few years ago from the University of Surrey with a Masters in Electronics Engineering. He now works for Red Hat in the desktop group, and also manages a company selling open source calibration equipment. Richard's outside interests include taking photos and eating good food.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.