This plan follows ASD-STE100 Simplified Technical English: short sentences, active voice, one idea per sentence, imperative for steps, vertical lists, and one term for one thing.
This plan describes the wolfGlass project. wolfGlass is one toolkit that makes software transparency artifacts for all wolfSSL products. The artifacts are the SBOM, the security advisory, the VEX, and the build provenance.
Use these terms with one meaning only.
| Term | Meaning |
|---|---|
| toolkit | The wolfGlass repository and its tools. |
| the generator | The gen-sbom script that makes an SBOM. |
| the fragment | A shared build recipe (sbom.am, sbom.cmake, or sbom.mk). |
| product repository | A repository that ships a wolfSSL product. |
| to vendor | To copy a controlled toolkit file into a product repository. |
| the pin | The VERSION value that records the toolkit release in use. |
| central | Made one time in one place, not in each product repository. |
| out-of-tree | Run against a checkout, but write no file into that checkout. |
| SBOM | Software Bill of Materials (CycloneDX 1.6 and SPDX 2.3). |
| CSAF | Common Security Advisory Framework 2.0 advisory. |
| VEX | Vulnerability Exploitability eXchange statement. |
| bomsh | The OmniBOR build-trace tool that adds provenance. |
The EU Cyber Resilience Act starts full obligations on 11 December 2027. The Act
requires a machine-readable SBOM for each product with digital elements. wolfSSL
ships more than 30 products. The products use many build systems: autotools,
CMake, plain Make, user_settings.h, and language package managers.
Today the SBOM tools live inside the wolfSSL repository. Each product repository copies the fragment by hand. There is no version pin and no drift check. This causes three problems:
- The copied fragments drift apart.
- The CMake products and the plain-Make products duplicate about 120 to 150 lines each.
- The hard cases have no clean home: air-gapped builds, embedded builds, and FIPS-certified bundles.
Create wolfGlass as one standalone repository. Make wolfGlass the single source of truth for the SBOM tools. Keep the generator unchanged, because it is already product-agnostic. Add two design elements:
- One vendorable set of files that each product copies with a pin.
- One out-of-tree mode for bundles that you must not modify.
Give each build system one shared fragment:
sbom.amfor autotools.sbom.cmakewithsbom_run.cmakefor CMake.sbom.mkfor plain Make.
Keep the advisory tool and the provenance tool central. Do not vendor them into each product.
- Fix a defect one time. Then all products get the fix.
- The output stays reproducible, because the pin and
SOURCE_DATE_EPOCHcontrol it. - The SBOM maps upstream CVEs onto each downstream product.
- The tools run on air-gapped Linux, embedded hosts, and Windows.
- The out-of-tree mode keeps FIPS-certified bundles bit-identical.
- A shared GPLv3 license matches the current wolfSSL repositories and removes a license mismatch when code moves between wolfGlass, wolfSSL, and wolfBoot.
Each product belongs to one tier. The tier shows how the product uses wolfSSL. The tier sets the SBOM method.
| Tier | Name | Build system | wolfSSL link | SBOM method |
|---|---|---|---|---|
| R | Root / generator | autotools, CMake, user_settings | Root | Hash libwolfssl. Capture options.h. |
| L | Linker | autotools, some CMake | Yes (--dep-wolfssl) |
Hash own library or binary. |
| E | Embedder | plain Make | No (wolfcrypt inside) | Hash a source merkle (--srcs-file). |
| S | Standalone | Make, autotools, CMake | Only if linked | Hash own library. |
| B | Language binding | Maven, pip, cargo, go, .NET | Declares wolfSSL | Hash the wrapper package. |
| Feature | Part of wolfSSL | via wolfSSL | Inside wolfSSL | Covered by the wolfSSL SBOM. |
| External | External consumer | autotools, CMake | Declares wolfSSL | Out-of-tree. |
| Kit | Customer demo | CMake, user_settings | References wolfSSL | Two-level composition. |
The tier is not the whole story. One product can use many build systems, and the
SBOM method has two independent axes: the config (what the build enabled) and the
composition (what artifact the build produced). One constraint sits across both:
a FIPS canister and a kernel module must be hashed as-built and must not use a
substituted source list. docs/TIERS.md holds the full product-by-front-end
matrix, the two axes, the as-built constraint, and the dictionary. Treat
docs/TIERS.md as the authoritative roster.
Tier R (root):
- wolfSSL (autotools and CMake).
- wolfCrypt (same source tree as wolfSSL).
Tier L (links libwolfssl):
- wolfSSH.
- wolfProvider (autotools only; also OpenSSL).
- wolfEngine (autotools only; also OpenSSL).
- wolfCLU (hashes the wolfssl binary).
- wolfSCEP (autotools only; GPLv2-or-later).
- wolfTPM (autotools and CMake).
- wolfMQTT (autotools and CMake).
- wolfPKCS11 (not in this workspace; verify at the source).
The wolfSSL kernel modules (linuxkm and bsdkm) and wolfGuard are the as-built
LIB* case. Hash the module as built. Do not substitute a source list. wolfGuard
links linuxkm; its tier is still unset. See docs/TIERS.md.
Tier E (embeds wolfcrypt sources):
- wolfBoot.
- wolfHSM (tests and examples).
Tier S (standalone library):
- wolfSentry (plain Make only; CMake in one example).
- wolfHSM (core library; vendor ports are restricted stubs).
- wolfIP.
Tier B (language binding):
- wolfSSL JNI/JSSE.
- wolfCrypt JNI/JCE.
- wolfSSL Python.
- wolfCrypt Python.
- wolfCrypt rustls provider.
- wolfSSL Go.
- wolfSSL C# (in-tree wrapper).
Feature (part of wolfSSL):
- wolfCrypt.
- SSL/TLS Sniffer.
- wolfEntropy.
- wolfFE.
External:
- curl (candidate; confirm scope before you attest).
- tinycurl (candidate; confirm scope before you attest).
Kit:
- CRA Kit (base).
- CRA Kit (CMake and embedded).
Use this structure for the wolfGlass repository.
wolfglass/
├── README.md
├── LICENSE (GPLv3; aligned with wolfSSL repos)
├── VERSION (toolkit release; products pin to this)
├── share/ (the ONLY vendorable set) [DONE]
│ ├── sbom-driver.py (product-neutral Python engine)
│ ├── sbom-driver (thin shell wrapper)
│ ├── validate_sbom.py (structural validator; --name-prefix)
│ ├── frontends/
│ │ ├── compdb_sbom.py (compile_commands.json extractor)
│ │ ├── iar_sbom.py (IAR .ewp extractor)
│ │ └── zephyr_sbom.py (Zephyr module extractor)
│ ├── build/
│ │ ├── sbom.mk (plain-Make fragment)
│ │ └── sbom.cmake (CMake: wolfglass_add_sbom())
│ ├── gen-sbom (populated after the engine-home sign-off) [PENDING]
│ └── sbom.am (autotools fragment; lifted from wolfSSL) [PENDING]
├── central/ (never vendored; runs for wolfSSL only) [PENDING]
│ ├── gen-advisory
│ ├── advisory-vex-overlay.schema.json
│ └── advisory-vex-overlay.example.json
├── provenance/ (opt-in; Linux CI; all products on demand)[PENDING]
│ ├── wolfglass-bomsh
│ └── bomsh_verify.py
├── schemas/ (CDX 1.6 and SPDX 2.3, for offline validation)[PENDING]
├── tools/
│ ├── wolfglass-sync (copies share/ into a product; supports --src) [DONE]
│ └── wolfglass (out-of-tree driver) [PENDING]
├── .github/workflows/
│ ├── selftest.yml (wolfGlass CI: unit + integration) [DONE]
│ └── sbom-reusable.yml (reusable workflow for products) [DONE]
├── tests/
│ └── test_sbom.py (unit + integration self-test) [DONE]
└── docs/
Vendor only the share/ files. Do not put a share/ folder into a product. The
synced files go into the product tools/sbom/ folder. The driver, the
extractors, and the fragments keep their relative layout, so a product needs no
path configuration.
Use the name wolfGlass. The word "glass" means transparency, which is the purpose of an SBOM. The name matches the wolfSSL family style. The name has few search-engine conflicts. Do not use wolfLiquidGlass, because it conflicts with the Apple design language. Do not use wolfCrystal, because it conflicts with the Crystal programming language.
Use the copy-sync method as the standard method for products that wolfSSL owns.
- Run
wolfglass-syncto copy the needed files. - Write the pin next to the files (
VERSIONand.wolfglass-rev). - Add
make sbom-selfcheckto detect drift.
Use the out-of-tree mode for bundles that you must not change.
- Run
wolfglass --src <checkout> --out <sibling>. - Use this mode for FIPS bundles, external consumers, and the CRA Kit.
Do not use a git submodule as the standard method. A submodule breaks source tarballs. A submodule needs network access at clone time. A submodule adds files to a frozen tree.
Do not pull the latest generator at build time. A network pull breaks reproducibility. A network pull fails on air-gapped hosts. A network pull adds supply-chain risk.
Vendor the generator into the release tarballs. Then an air-gapped customer has a
self-contained build. Keep the WOLFSSL_DIR reference for day-to-day work.
Do not change how the generator makes an SBOM. The generator is already generic.
It accepts --name, --version, --supplier, and the dependency flags. It
accepts --options-h, --user-settings, --lib, --srcs-file, and
--no-artifact-hash.
Add one required behavior to the shared driver: scrub absolute host paths from
the captured macros. A path-valued define, for example
-DPICO_SDK_PATH=$(PICO_SDK_PATH) from arch.mk, otherwise makes the SBOM
machine-specific and leaks a local path into a shipped artifact. wolfBoot already
implements this scrub in tools/scripts/wolfboot-sbom.sh. Promote that scrub to
the shared driver so every product gets it. Keep a --no-scrub flag for debug
only.
Decide these two items one time:
- Keep wolfGlass under GPLv3 to match wolfSSL and wolfBoot. This removes license friction when code moves between the repositories.
- Choose whether to rename the producer identity. A rename changes every SBOM one time. If you rename it, also raise the generator version in the same commit.
Give the two build systems that lack a shared fragment their own fragment.
sbom.cmakereplaces about 150 duplicated lines in the CMake products.sbom.mkgives the plain-Make products one common recipe.
Make the CMake fragment a function, not a per-product target. Today three CMake
copies exist and drift: wolfMQTT (inline in CMakeLists.txt), wolfTPM (inline in
CMakeLists.txt), and wolfBoot (cmake/sbom.cmake). Each copy re-parses the
version header, re-checks the wolfSSL path, and re-detects python. Provide one
wolfglass_add_sbom() function in sbom.cmake. Each product calls it with its
name, its version header, its targets, and its define variables. Do not bless
wolfBoot's cmake/sbom.cmake as canonical, because that makes a fourth copy.
Converge the three copies onto the one function.
Reduce the per-product wiring to a manifest. For autotools this is already true:
a product sets a few SBOM_* variables and includes sbom.am. Match that model
for Make and CMake. Keep only the true product knowledge in the product: the
route-through script, the module extractor, and the HAL source selector.
Centralize version detection and generator discovery in the shared layer. The manifest passes two strings (the version header path and the version macro name). The manifest does not re-implement the regex or the search path.
Note: user_settings.h already solves the config capture. The gap is the recipe,
not the config. Standardize the options mode on "direct" to match sbom.am. Then
the CMake SBOM stays byte-comparable with the autotools SBOM.
Keep the advisory tool central. The gen-advisory tool is already multi-product.
It builds the PURL and the CPE per product, the same as the generator.
- Author the CSAF advisory one time per CVE. wolfSSL Inc. is one manufacturer.
- Make one VEX per product. The VEX answer depends on the build of each product.
- Do not add a build step to each product for advisories. The security team makes the advisory from CVE records.
Publish the per-product VEX next to the per-product SBOM if a customer needs it. This is a publish choice, not a tool change. The customer makes the VEX for the customer end product.
Host bomsh in wolfGlass under provenance/. bomsh is reusable, because it traces
build syscalls. The trace works for any build system on Linux.
- Depend on the external
bomtrace3and bomsh scripts. Pin their version. Do not vendor them. - Run bomsh on Linux only. Use Linux CI, WSL2, or a container for other hosts.
- Enable bomsh per product on demand. Most products do not need it.
- Run bomsh at release time or nightly. Do not run bomsh on each pull request, because it needs a full traced rebuild.
Keep the CI logic central in wolfGlass as reusable workflows. Let each product
repository call the workflows. This removes the current duplicate sbom.yml
files.
| Job | Runs in | Trigger |
|---|---|---|
| Tool unit tests and reproducibility | wolfGlass | each pull request to wolfGlass |
| Integration canary (one product per tier) | wolfGlass | each pull request to wolfGlass and nightly |
Drift check (sbom-selfcheck) |
each product repository | each pull request |
| SBOM build and validate | each product repository | each pull request, filtered by path |
| SBOM release artifact | each product repository | on a tag or release |
| bomsh provenance | product or wolfGlass | on release, nightly, or on demand |
| Version-bump propagation | automation | on a wolfGlass release |
Generate the SBOM in the product repository, because the build lives there. Validate on each pull request. Attach the real SBOM at release time. Do not commit generated SBOMs on each pull request, because they cause churn.
- Pin each product to one wolfGlass
VERSION. - Release wolfGlass with a version number and a changelog.
- Open a bot pull request on each release. The pull request re-syncs
share/and raises the pin. - Let each product CI validate the bump.
- Never auto-update at build time.
Protect against a bad change with two controls:
- Run the canary in wolfGlass before a release.
- Move a product only after its own CI passes.
- Keep the generator tests and the advisory tests in wolfGlass.
- Run the unit tests on each wolfGlass pull request.
- Run the reproducibility test to confirm byte-equal output.
- Validate sample output against the pinned schemas.
- Never vendor the tests into a product.
The tools must meet these constraints.
- Run with pure Python 3 and no required third-party package.
- Use no network at generation time.
- Validate offline with the pinned schemas.
- Support the no-library and no-
options.hcases with--srcs-fileand--no-artifact-hash. - Write no file into a FIPS-certified bundle.
Migrate in the order of lowest risk. Decide first. Prove the hard path once with wolfBoot. Then move the easy products in a batch. Then do the hard tail.
wolfBoot already ships the reference implementation (commit that adds
tools/scripts/wolfboot-sbom.sh, the IDE extractors, cmake/sbom.cmake, the
validator, and a CI canary). The tools are written to be product-neutral. So the
work is mostly relocation and de-branding, plus one real refactor of the CMake
layer.
Phase 0 — Decide (unblocks everything).
- Choose the driver language. Prefer Python for Windows and air-gap parity. Keep a thin shell wrapper.
- Choose the vendored path name. Use one name in every product.
- Confirm the generator home and get a named owner's sign-off. See §21.
Phase 1 — Scaffold wolfGlass share/.
- Define the manifest contract (the
SBOM_*variables for Make and thewolfglass_add_sbom()arguments for CMake). - Lift the driver and the extractors into
share/. De-brand the wolfBoot defaults (name, version header, generator path, name prefix). - Promote the path scrub to the shared driver.
Phase 2 — wolfBoot (the reference migration).
- Refactor the CMake target into the shared
wolfglass_add_sbom()function. - Keep the product knowledge in wolfBoot: the route-through script, the Zephyr extractor, and the HAL selector.
- Re-vendor
share/back into wolfBoot. Prove the round trip.
Phase 3 — wolfSSL (the canonical source).
- Make wolfGlass canonical for the generator and
sbom.am. - Make wolfSSL a
wolfglass-synctarget that keeps its own vendored copy. - Add no build-time dependency on wolfGlass.
Phase 4 — Batch the easy products.
- Re-point the autotools products to the vendored
sbom.am(wolfSSH, wolfMQTT, wolfEngine, wolfCLU, wolfSCEP). This is near-free. - Collapse the CMake products (wolfMQTT, wolfTPM) onto the shared function. This deletes about 150 duplicated lines each.
Phase 5 — Add the shared tools and CI.
- Write
wolfglass-syncand stamp the pin into each vendored copy. The pin isVERSIONplus.wolfglass-rev. - Write the
wolfglassout-of-tree driver. - Write
wolfglass-bomshfrom the current bomsh recipe. - Write the reusable workflows. Add the canary matrix. Add the drift check that validates the call site, not only the file hashes.
Phase 6 — The hard tail.
- wolfHSM: add per-port source-list extractors; mark the restricted stubs.
- wolfSSL kernel (linuxkm and bsdkm) and wolfGuard: add the Kbuild extractor and
the as-built
LIB*rule. - Assign the wolfGuard tier and owner.
Phase 7 — Propagate and pin.
- Release wolfGlass v1.0.
- Open the bump pull requests.
- Confirm each product CI passes.
| Risk | Mitigation |
|---|---|
| A toolkit change breaks a product SBOM. | Run the canary before each release. |
| A vendored fragment drifts. | Run the drift check on each pull request. |
| A rename rotates every SBOM. | Rename one time and raise the generator version. |
| An air-gapped build has no generator. | Vendor the generator into the release tarball. |
| The CMake and autotools output differ. | Standardize the options mode on "direct". |
Decide these before Phase 1. They block the code work or set its shape.
- Choose the driver language. Prefer Python for Windows and air-gap parity. Keep a thin shell wrapper. Decide this before the lift, because it is cheapest then.
- Choose the vendored path name. Use one name in every product, for example
tools/sbom/. The sync tool and the drift check depend on one convention. - Confirm the generator home. wolfGlass is canonical. wolfSSL keeps a vendored copy and gains no build-time dependency. This touches licensing and the FIPS release process, so it needs a named owner's sign-off.
Decide these before Phase 7. They do not block the code work.
- License choice is set: GPLv3 to match wolfSSL repositories.
- Choose the location of the CRA Kit. Keep it in the examples repository and reference wolfGlass.
- Choose whether to rename the producer identity now or later.
- Assign the wolfGuard tier and owner.