mirror of https://github.com/wolfSSL/wolfTPM.git
SBOM: adopt shared sbom.am fragment; bring CMake target to parity
Autotools: replace the inline make sbom recipe with the shared
scripts/sbom.am fragment (declare wolftpm identity, LICENSE, wolfSSL
dependency, and wolftpm/options.h as the feature-macro source). Add GIT
discovery + AC_SUBST for reproducible SOURCE_DATE_EPOCH, and revert the
WOLFTPM_LIBRARY_VERSION_{FIRST,SECOND,THIRD} split that only fed the old
hardcoded --lib path (the fragment discovers the artifact by glob).
CMake: pin the SBOM licence to GPL-3.0-or-later (header-accurate; matches
autotools SBOM_LICENSE_OVERRIDE) and record wolfSSL as a dependency via
--dep-wolfssl, capability-gated on gen-sbom --help so older gen-sbom
versions still produce a valid (dependency-less) SBOM.
Pin the license default to GPL-3.0-or-later to match the source headers
("or (at your option) any later version"), fixing the SBOM licence field.
Add a CI workflow that builds wolfSSL + wolfTPM and asserts SBOM identity,
licence, options capture, reproducibility, and the wolfSSL dependency.
Signed-off-by: Sameeh Jubran <sameeh@wolfssl.com>
pull/536/head
parent
6f86a048dd
commit
fc511714d3
|
|
@ -0,0 +1,175 @@
|
||||||
|
name: SBOM Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ 'master', 'main', 'release/**' ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ '*' ]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
wolfssl_ref:
|
||||||
|
description: 'wolfssl git ref that provides scripts/gen-sbom'
|
||||||
|
# TODO: switch back to 'master' once wolfSSL/wolfssl#10343 merges.
|
||||||
|
default: 'refs/pull/10343/head'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
# This workflow only reads the repo and uploads artefacts; no API writes.
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sbom:
|
||||||
|
name: wolfTPM SBOM generation (linux)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 20
|
||||||
|
steps:
|
||||||
|
- name: Checkout wolftpm
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
path: wolftpm
|
||||||
|
|
||||||
|
# wolfTPM links wolfSSL/wolfCrypt (RNG, parameter encryption, PK
|
||||||
|
# callbacks), so its SBOM records wolfSSL as a dependency. wolfSSL is
|
||||||
|
# built + installed here so wolfTPM has a library to link, and the same
|
||||||
|
# source tree (scripts/gen-sbom + wolfssl/version.h) is passed to
|
||||||
|
# `make sbom` via WOLFSSL_DIR -- so the recorded wolfSSL dependency
|
||||||
|
# version matches the linked one. gen-sbom is not yet on wolfssl master,
|
||||||
|
# so default to the open PR head that carries it (wolfSSL/wolfssl#10343)
|
||||||
|
# so CI actually exercises `make sbom` instead of silently skipping.
|
||||||
|
# TODO: switch the fallback back to 'master' once #10343 merges.
|
||||||
|
- name: Checkout wolfssl (gen-sbom + library source)
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: wolfSSL/wolfssl
|
||||||
|
ref: ${{ github.event.inputs.wolfssl_ref || 'refs/pull/10343/head' }}
|
||||||
|
path: wolfssl
|
||||||
|
|
||||||
|
- name: Install build tooling and SBOM validator (pyspdxtools)
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y build-essential autoconf automake libtool \
|
||||||
|
pkg-config
|
||||||
|
python3 -m pip install --user 'spdx-tools==0.8.*'
|
||||||
|
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
||||||
|
|
||||||
|
- name: Build and install wolfssl
|
||||||
|
working-directory: wolfssl
|
||||||
|
run: |
|
||||||
|
autoreconf -ivf
|
||||||
|
./configure --enable-wolftpm --enable-pkcallbacks \
|
||||||
|
--prefix="$GITHUB_WORKSPACE/wolfssl-install"
|
||||||
|
make -j"$(nproc)"
|
||||||
|
make install
|
||||||
|
|
||||||
|
# gen-sbom lives in wolfssl and may not be on the checked-out ref yet (the
|
||||||
|
# wolfSSL SBOM change can land separately). Gate on its presence and on
|
||||||
|
# --dep-wolfssl so this workflow is safe against a gen-sbom that predates
|
||||||
|
# it.
|
||||||
|
- name: Detect gen-sbom availability and capabilities
|
||||||
|
id: gate
|
||||||
|
run: |
|
||||||
|
GS="$GITHUB_WORKSPACE/wolfssl/scripts/gen-sbom"
|
||||||
|
if [ ! -f "$GS" ]; then
|
||||||
|
echo "have=no" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "::notice::wolfssl scripts/gen-sbom not present on this ref; skipping SBOM generation."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "have=yes" >> "$GITHUB_OUTPUT"
|
||||||
|
if python3 "$GS" --help 2>/dev/null | grep -q -- '--dep-wolfssl'; then
|
||||||
|
echo "dep_wolfssl=yes" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "dep_wolfssl=no" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "::notice::gen-sbom on this ref has no --dep-wolfssl; the wolfssl dependency + name-derived identity assertions will be skipped."
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Configure and build wolftpm
|
||||||
|
if: steps.gate.outputs.have == 'yes'
|
||||||
|
working-directory: wolftpm
|
||||||
|
run: |
|
||||||
|
autoreconf -ivf
|
||||||
|
./configure --enable-swtpm --disable-fwtpm --disable-examples \
|
||||||
|
--with-wolfcrypt="$GITHUB_WORKSPACE/wolfssl-install"
|
||||||
|
make -j"$(nproc)"
|
||||||
|
|
||||||
|
- name: Generate SBOM
|
||||||
|
if: steps.gate.outputs.have == 'yes'
|
||||||
|
working-directory: wolftpm
|
||||||
|
run: make sbom WOLFSSL_DIR="$GITHUB_WORKSPACE/wolfssl"
|
||||||
|
|
||||||
|
- name: Outputs exist and SPDX validates
|
||||||
|
if: steps.gate.outputs.have == 'yes'
|
||||||
|
working-directory: wolftpm
|
||||||
|
run: |
|
||||||
|
ls wolftpm-*.cdx.json wolftpm-*.spdx.json wolftpm-*.spdx
|
||||||
|
pyspdxtools --infile wolftpm-*.spdx.json
|
||||||
|
|
||||||
|
- name: CycloneDX identity, licence, and captured options
|
||||||
|
if: steps.gate.outputs.have == 'yes'
|
||||||
|
working-directory: wolftpm
|
||||||
|
run: |
|
||||||
|
python3 - <<'PY'
|
||||||
|
import glob, json
|
||||||
|
cdx = json.load(open(glob.glob('wolftpm-*.cdx.json')[0]))
|
||||||
|
assert cdx['bomFormat'] == 'CycloneDX', cdx.get('bomFormat')
|
||||||
|
assert cdx['specVersion'] == '1.6', cdx.get('specVersion')
|
||||||
|
m = cdx['metadata']['component']
|
||||||
|
assert m['name'] == 'wolftpm', m['name']
|
||||||
|
assert m['purl'].startswith('pkg:github/wolfSSL/wolftpm@'), m['purl']
|
||||||
|
# Default override must land as GPL-3.0-or-later (matches source headers).
|
||||||
|
ids = [l.get('license', {}).get('id') for l in m.get('licenses', [])]
|
||||||
|
assert 'GPL-3.0-or-later' in ids, ids
|
||||||
|
# Identity is the hashed library artifact.
|
||||||
|
assert {h['alg'] for h in m.get('hashes', [])}, 'no component hash'
|
||||||
|
# SBOM_OPTIONS_H must have been parsed: wolfTPM records its feature
|
||||||
|
# macros in wolftpm/options.h, so the SBOM's build properties must be
|
||||||
|
# populated.
|
||||||
|
props = [p for p in m.get('properties', [])
|
||||||
|
if p.get('name', '').startswith('wolfssl:build:')]
|
||||||
|
assert props, 'no wolfssl:build:* properties captured from options.h'
|
||||||
|
print('CDX ok:', m['name'], m['purl'], ids, f'{len(props)} build props')
|
||||||
|
PY
|
||||||
|
|
||||||
|
- name: Reproducible across two runs (SOURCE_DATE_EPOCH)
|
||||||
|
if: steps.gate.outputs.have == 'yes'
|
||||||
|
working-directory: wolftpm
|
||||||
|
run: |
|
||||||
|
rm -f wolftpm-*.cdx.json wolftpm-*.spdx.json wolftpm-*.spdx
|
||||||
|
SOURCE_DATE_EPOCH=1700000000 make sbom \
|
||||||
|
WOLFSSL_DIR="$GITHUB_WORKSPACE/wolfssl"
|
||||||
|
sha256sum wolftpm-*.cdx.json wolftpm-*.spdx.json > /tmp/a.sums
|
||||||
|
rm -f wolftpm-*.cdx.json wolftpm-*.spdx.json wolftpm-*.spdx
|
||||||
|
SOURCE_DATE_EPOCH=1700000000 make sbom \
|
||||||
|
WOLFSSL_DIR="$GITHUB_WORKSPACE/wolfssl"
|
||||||
|
sha256sum wolftpm-*.cdx.json wolftpm-*.spdx.json > /tmp/b.sums
|
||||||
|
diff /tmp/a.sums /tmp/b.sums
|
||||||
|
|
||||||
|
- name: wolfssl recorded as a dependency
|
||||||
|
if: steps.gate.outputs.have == 'yes' && steps.gate.outputs.dep_wolfssl == 'yes'
|
||||||
|
working-directory: wolftpm
|
||||||
|
run: |
|
||||||
|
python3 - <<'PY'
|
||||||
|
import glob, json
|
||||||
|
d = json.load(open(glob.glob('wolftpm-*.spdx.json')[0]))
|
||||||
|
assert 'wolfssl' in {p['name'] for p in d['packages']}, \
|
||||||
|
[p['name'] for p in d['packages']]
|
||||||
|
rels = [(r['spdxElementId'], r['relationshipType'],
|
||||||
|
r['relatedSpdxElement']) for r in d['relationships']]
|
||||||
|
assert ('SPDXRef-Package-wolftpm', 'DEPENDS_ON',
|
||||||
|
'SPDXRef-Package-wolfssl') in rels, rels
|
||||||
|
print('wolfssl dependency ok')
|
||||||
|
PY
|
||||||
|
|
||||||
|
- name: Upload SBOM artefacts
|
||||||
|
if: always() && steps.gate.outputs.have == 'yes'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: wolftpm-sbom-${{ github.sha }}
|
||||||
|
path: |
|
||||||
|
wolftpm/wolftpm-*.cdx.json
|
||||||
|
wolftpm/wolftpm-*.spdx.json
|
||||||
|
wolftpm/wolftpm-*.spdx
|
||||||
|
if-no-files-found: warn
|
||||||
|
retention-days: 90
|
||||||
|
|
@ -793,6 +793,15 @@ if(BUILD_WOLFTPM_LIB)
|
||||||
set(WOLFSSL_DIR "" CACHE PATH
|
set(WOLFSSL_DIR "" CACHE PATH
|
||||||
"Path to wolfssl source tree with scripts/gen-sbom")
|
"Path to wolfssl source tree with scripts/gen-sbom")
|
||||||
|
|
||||||
|
# wolfTPM is GPLv3-or-later (per the per-file source headers: "either
|
||||||
|
# version 3 of the License, or (at your option) any later version") or
|
||||||
|
# commercial. Pin the header-accurate SPDX id so the SBOM is correct
|
||||||
|
# regardless of the gen-sbom version's licence detection; commercial
|
||||||
|
# licensees can override it (e.g. LicenseRef-wolfSSL-Commercial). This
|
||||||
|
# mirrors SBOM_LICENSE_OVERRIDE in the autotools build.
|
||||||
|
set(SBOM_LICENSE_OVERRIDE "GPL-3.0-or-later" CACHE STRING
|
||||||
|
"SPDX licence expression recorded in the SBOM")
|
||||||
|
|
||||||
# Derive the version from wolftpm/version.h, NOT from PROJECT_VERSION.
|
# Derive the version from wolftpm/version.h, NOT from PROJECT_VERSION.
|
||||||
# autotools `make sbom` uses PACKAGE_VERSION, which is sourced from
|
# autotools `make sbom` uses PACKAGE_VERSION, which is sourced from
|
||||||
# version.h. Reading the same header here keeps the cmake and autotools
|
# version.h. Reading the same header here keeps the cmake and autotools
|
||||||
|
|
@ -863,6 +872,25 @@ if(BUILD_WOLFTPM_LIB)
|
||||||
set(SBOM_LIB
|
set(SBOM_LIB
|
||||||
"${SBOM_STAGING}/lib/libwolftpm${CMAKE_SHARED_LIBRARY_SUFFIX}")
|
"${SBOM_STAGING}/lib/libwolftpm${CMAKE_SHARED_LIBRARY_SUFFIX}")
|
||||||
|
|
||||||
|
# wolfTPM links wolfSSL/wolfCrypt, so record wolfSSL as a dependency in the
|
||||||
|
# SBOM (matches the autotools SBOM_DEP_WOLFSSL=yes). Recording it needs the
|
||||||
|
# gen-sbom from wolfSSL/wolfssl#10343; older gen-sbom rejects unknown flags,
|
||||||
|
# so probe --help and only pass --dep-wolfssl when supported. Against an
|
||||||
|
# older gen-sbom the SBOM is still valid but omits the wolfSSL entry.
|
||||||
|
set(_sbom_dep_wolfssl "")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${PYTHON3_CMD} ${WOLFSSL_DIR}/scripts/gen-sbom --help
|
||||||
|
OUTPUT_VARIABLE _sbom_help
|
||||||
|
ERROR_QUIET)
|
||||||
|
if(_sbom_help MATCHES "--dep-wolfssl")
|
||||||
|
set(_sbom_dep_wolfssl --dep-wolfssl yes)
|
||||||
|
else()
|
||||||
|
message(WARNING
|
||||||
|
"sbom: ${WOLFSSL_DIR}/scripts/gen-sbom has no --dep-wolfssl; "
|
||||||
|
"the SBOM will omit the wolfSSL dependency entry. Use the gen-sbom "
|
||||||
|
"from wolfSSL/wolfssl#10343 (or master once merged) to record it.")
|
||||||
|
endif()
|
||||||
|
|
||||||
# ${OPTION_FILE} is the generated wolftpm/options.h, the same compile-time
|
# ${OPTION_FILE} is the generated wolftpm/options.h, the same compile-time
|
||||||
# option fingerprint autotools `make sbom` feeds to gen-sbom via
|
# option fingerprint autotools `make sbom` feeds to gen-sbom via
|
||||||
# --options-h. Using it (rather than a raw `cc -dM` dump, which would only
|
# --options-h. Using it (rather than a raw `cc -dM` dump, which would only
|
||||||
|
|
@ -881,6 +909,8 @@ if(BUILD_WOLFTPM_LIB)
|
||||||
--license-file ${CMAKE_SOURCE_DIR}/LICENSE
|
--license-file ${CMAKE_SOURCE_DIR}/LICENSE
|
||||||
--options-h ${OPTION_FILE}
|
--options-h ${OPTION_FILE}
|
||||||
--lib ${SBOM_LIB}
|
--lib ${SBOM_LIB}
|
||||||
|
--license-override ${SBOM_LICENSE_OVERRIDE}
|
||||||
|
${_sbom_dep_wolfssl}
|
||||||
--cdx-out ${SBOM_CDX}
|
--cdx-out ${SBOM_CDX}
|
||||||
--spdx-out ${SBOM_SPDX}
|
--spdx-out ${SBOM_SPDX}
|
||||||
# Validate the SPDX JSON and emit the tag-value rendering as a side
|
# Validate the SPDX JSON and emit the tag-value rendering as a side
|
||||||
|
|
|
||||||
80
Makefile.am
80
Makefile.am
|
|
@ -133,69 +133,25 @@ cppcheck:
|
||||||
--error-exitcode=89 --std=c89 \
|
--error-exitcode=89 --std=c89 \
|
||||||
-I wolftpm src/ hal/ examples
|
-I wolftpm src/ hal/ examples
|
||||||
|
|
||||||
# SBOM generation (CRA compliance)
|
# SBOM generation (CRA compliance). The recipe is shared across the wolfSSL
|
||||||
SBOM_CDX = wolftpm-$(PACKAGE_VERSION).cdx.json
|
# stack's autotools products in scripts/sbom.am; wolfTPM just declares what it
|
||||||
SBOM_SPDX = wolftpm-$(PACKAGE_VERSION).spdx.json
|
# is (a libwolftpm library that links wolfSSL/wolfCrypt) and includes it.
|
||||||
SBOM_SPDX_TV = wolftpm-$(PACKAGE_VERSION).spdx
|
# WOLFSSL_DIR must point to a wolfssl source tree containing scripts/gen-sbom.
|
||||||
sbomdir = $(datadir)/doc/$(PACKAGE)
|
SBOM_PKGNAME = wolftpm
|
||||||
|
SBOM_LICENSE_FILE = $(srcdir)/LICENSE
|
||||||
|
SBOM_DEP_WOLFSSL = yes
|
||||||
|
|
||||||
.PHONY: sbom install-sbom uninstall-sbom
|
# wolfTPM records its feature macros in its own generated options header, so
|
||||||
|
# point gen-sbom at that header rather than at the compiler-derived defaults.
|
||||||
|
SBOM_OPTIONS_H = $(abs_builddir)/wolftpm/options.h
|
||||||
|
|
||||||
sbom:
|
# wolfTPM is GPLv3-or-later (per the per-file source headers: "either version 3
|
||||||
@if test -z "$(PYTHON3)"; then \
|
# of the License, or (at your option) any later version") or commercial. Pin
|
||||||
echo ""; \
|
# the header-accurate SPDX id here so the SBOM is correct regardless of the
|
||||||
echo "ERROR: 'python3' not found in PATH. Cannot generate SBOM."; \
|
# gen-sbom version's licence detection; commercial licensees can override it
|
||||||
echo ""; \
|
# (e.g. LicenseRef-wolfSSL-Commercial).
|
||||||
exit 1; \
|
SBOM_LICENSE_OVERRIDE ?= GPL-3.0-or-later
|
||||||
fi
|
|
||||||
@if test -z "$(PYSPDXTOOLS)"; then \
|
|
||||||
echo ""; \
|
|
||||||
echo "ERROR: 'pyspdxtools' not found in PATH. Cannot validate SBOM."; \
|
|
||||||
echo " Install: pip install spdx-tools"; \
|
|
||||||
echo ""; \
|
|
||||||
exit 1; \
|
|
||||||
fi
|
|
||||||
@if test -z "$(WOLFSSL_DIR)"; then \
|
|
||||||
echo ""; \
|
|
||||||
echo "ERROR: WOLFSSL_DIR is not set. Cannot locate gen-sbom."; \
|
|
||||||
echo " Set WOLFSSL_DIR to your wolfSSL source tree, e.g.:"; \
|
|
||||||
echo " make sbom WOLFSSL_DIR=/path/to/wolfssl"; \
|
|
||||||
echo ""; \
|
|
||||||
exit 1; \
|
|
||||||
fi
|
|
||||||
@if test ! -f "$(WOLFSSL_DIR)/scripts/gen-sbom"; then \
|
|
||||||
echo ""; \
|
|
||||||
echo "ERROR: $(WOLFSSL_DIR)/scripts/gen-sbom not found."; \
|
|
||||||
echo " Check that WOLFSSL_DIR points to a wolfSSL tree with SBOM support."; \
|
|
||||||
echo ""; \
|
|
||||||
exit 1; \
|
|
||||||
fi
|
|
||||||
rm -rf $(abs_builddir)/_sbom_staging
|
|
||||||
$(MAKE) install DESTDIR=$(abs_builddir)/_sbom_staging
|
|
||||||
$(PYTHON3) $(WOLFSSL_DIR)/scripts/gen-sbom \
|
|
||||||
--name wolftpm \
|
|
||||||
--version $(PACKAGE_VERSION) \
|
|
||||||
--supplier "wolfSSL Inc." \
|
|
||||||
--license-file $(srcdir)/LICENSE \
|
|
||||||
--options-h $(abs_builddir)/wolftpm/options.h \
|
|
||||||
--lib $(abs_builddir)/_sbom_staging$(libdir)/libwolftpm.so.@WOLFTPM_LIBRARY_VERSION_FIRST@.@WOLFTPM_LIBRARY_VERSION_SECOND@.@WOLFTPM_LIBRARY_VERSION_THIRD@ \
|
|
||||||
$(if $(SBOM_LICENSE_OVERRIDE),--license-override $(SBOM_LICENSE_OVERRIDE)) \
|
|
||||||
$(if $(SBOM_LICENSE_TEXT),--license-text $(SBOM_LICENSE_TEXT)) \
|
|
||||||
--cdx-out $(abs_builddir)/$(SBOM_CDX) \
|
|
||||||
--spdx-out $(abs_builddir)/$(SBOM_SPDX)
|
|
||||||
rm -rf $(abs_builddir)/_sbom_staging
|
|
||||||
$(PYSPDXTOOLS) --infile $(abs_builddir)/$(SBOM_SPDX) \
|
|
||||||
--outfile $(abs_builddir)/$(SBOM_SPDX_TV)
|
|
||||||
|
|
||||||
install-sbom: sbom
|
EXTRA_DIST += scripts/sbom.am
|
||||||
$(MKDIR_P) $(DESTDIR)$(sbomdir)
|
|
||||||
$(INSTALL_DATA) $(SBOM_CDX) $(DESTDIR)$(sbomdir)/
|
|
||||||
$(INSTALL_DATA) $(SBOM_SPDX) $(DESTDIR)$(sbomdir)/
|
|
||||||
$(INSTALL_DATA) $(SBOM_SPDX_TV) $(DESTDIR)$(sbomdir)/
|
|
||||||
|
|
||||||
uninstall-sbom:
|
include scripts/sbom.am
|
||||||
-rm -f $(DESTDIR)$(sbomdir)/$(SBOM_CDX)
|
|
||||||
-rm -f $(DESTDIR)$(sbomdir)/$(SBOM_SPDX)
|
|
||||||
-rm -f $(DESTDIR)$(sbomdir)/$(SBOM_SPDX_TV)
|
|
||||||
|
|
||||||
CLEANFILES += $(SBOM_CDX) $(SBOM_SPDX) $(SBOM_SPDX_TV)
|
|
||||||
|
|
|
||||||
28
README.md
28
README.md
|
|
@ -1126,6 +1126,12 @@ See `./examples/endorsement/get_ek_certs`.
|
||||||
|
|
||||||
wolfTPM generates a Software Bill of Materials (SBOM) in CycloneDX 1.6 and
|
wolfTPM generates a Software Bill of Materials (SBOM) in CycloneDX 1.6 and
|
||||||
SPDX 2.3 formats to support compliance with the EU Cyber Resilience Act (CRA).
|
SPDX 2.3 formats to support compliance with the EU Cyber Resilience Act (CRA).
|
||||||
|
The SBOM records the configured build options (from `wolftpm/options.h`),
|
||||||
|
hashes the built `libwolftpm` library artifact (shared or static; ELF, Mach-O,
|
||||||
|
or PE), and (with a sufficiently new `gen-sbom`) lists wolfSSL as a dependency
|
||||||
|
so vulnerability scanners can associate wolfSSL advisories with a wolfTPM
|
||||||
|
deployment. Output is reproducible: set `SOURCE_DATE_EPOCH` (or build from a git
|
||||||
|
checkout, which uses the last commit time) and repeated runs are byte-identical.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
make sbom WOLFSSL_DIR=/path/to/wolfssl
|
make sbom WOLFSSL_DIR=/path/to/wolfssl
|
||||||
|
|
@ -1135,19 +1141,29 @@ Requires `python3` and `pyspdxtools` (`pip install spdx-tools`). `WOLFSSL_DIR`
|
||||||
must point to a wolfssl source tree containing `scripts/gen-sbom` (branch
|
must point to a wolfssl source tree containing `scripts/gen-sbom` (branch
|
||||||
`feat/sbom-embedded`, or `master` once wolfSSL/wolfssl#10343 merges).
|
`feat/sbom-embedded`, or `master` once wolfSSL/wolfssl#10343 merges).
|
||||||
|
|
||||||
Output files in the build directory:
|
Output: `wolftpm-<version>.cdx.json`, `wolftpm-<version>.spdx.json`, `wolftpm-<version>.spdx`
|
||||||
|
|
||||||
| File | Format |
|
Optional overrides:
|
||||||
|------|--------|
|
|
||||||
| `wolftpm-<version>.cdx.json` | CycloneDX 1.6 |
|
- `SBOM_LICENSE_OVERRIDE` - SPDX expression to use instead of the licence
|
||||||
| `wolftpm-<version>.spdx.json` | SPDX 2.3 JSON |
|
parsed from `LICENSE` (e.g. `LicenseRef-wolfSSL-Commercial` for commercial
|
||||||
| `wolftpm-<version>.spdx` | SPDX 2.3 tag-value |
|
licensees). Defaults to `GPL-3.0-or-later` (the per-file header licence).
|
||||||
|
- `SBOM_LICENSE_TEXT` - path to the licence text for any `LicenseRef-*` used in
|
||||||
|
`SBOM_LICENSE_OVERRIDE` (required by SPDX 2.3).
|
||||||
|
- `SBOM_WOLFSSL_VERSION` - version recorded for the wolfSSL dependency;
|
||||||
|
auto-detected from `WOLFSSL_DIR/wolfssl/version.h` (or wolfSSL's `pkg-config`
|
||||||
|
entry) when unset.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
make install-sbom # installs to $(datadir)/doc/wolftpm/
|
make install-sbom # installs to $(datadir)/doc/wolftpm/
|
||||||
make uninstall-sbom
|
make uninstall-sbom
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note: recording wolfSSL as a dependency and emitting wolfTPM-specific project
|
||||||
|
URLs require the `gen-sbom` from wolfSSL/wolfssl#10343. Against an older
|
||||||
|
`gen-sbom`, `make sbom` still succeeds and produces a valid SBOM, but omits the
|
||||||
|
wolfSSL dependency entry and inherits wolfSSL's project URLs.
|
||||||
|
|
||||||
For further CRA guidance see [wolfssl/doc/CRA.md](https://github.com/wolfSSL/wolfssl/blob/master/doc/CRA.md).
|
For further CRA guidance see [wolfssl/doc/CRA.md](https://github.com/wolfSSL/wolfssl/blob/master/doc/CRA.md).
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
|
||||||
29
configure.ac
29
configure.ac
|
|
@ -29,16 +29,17 @@ AC_ARG_PROGRAM
|
||||||
|
|
||||||
AC_CONFIG_HEADERS([src/config.h])
|
AC_CONFIG_HEADERS([src/config.h])
|
||||||
|
|
||||||
WOLFTPM_LIBRARY_VERSION_FIRST=17
|
WOLFTPM_LIBRARY_VERSION=17:0:0
|
||||||
# ^--- current (installed so name: current-age)
|
# | | |
|
||||||
WOLFTPM_LIBRARY_VERSION_SECOND=0
|
# +------+ | +---+
|
||||||
# ^-- revision
|
# | | |
|
||||||
WOLFTPM_LIBRARY_VERSION_THIRD=0
|
# current:revision:age
|
||||||
# ^--- age
|
# | | |
|
||||||
WOLFTPM_LIBRARY_VERSION=${WOLFTPM_LIBRARY_VERSION_FIRST}:${WOLFTPM_LIBRARY_VERSION_SECOND}:${WOLFTPM_LIBRARY_VERSION_THIRD}
|
# | | +- increment if source code has changed
|
||||||
AC_SUBST([WOLFTPM_LIBRARY_VERSION_FIRST])
|
# | | set to zero if [current] or [revision] is incremented
|
||||||
AC_SUBST([WOLFTPM_LIBRARY_VERSION_SECOND])
|
# | +- increment if interfaces have been added
|
||||||
AC_SUBST([WOLFTPM_LIBRARY_VERSION_THIRD])
|
# | set to zero if [current] is incremented
|
||||||
|
# +- increment if interfaces have been removed or changed
|
||||||
AC_SUBST([WOLFTPM_LIBRARY_VERSION])
|
AC_SUBST([WOLFTPM_LIBRARY_VERSION])
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -990,9 +991,15 @@ AC_SUBST([AM_CFLAGS])
|
||||||
AC_SUBST([AM_LDFLAGS])
|
AC_SUBST([AM_LDFLAGS])
|
||||||
AC_SUBST([CPPCHECK])
|
AC_SUBST([CPPCHECK])
|
||||||
|
|
||||||
# SBOM generation
|
# Tools used by the SBOM targets (see scripts/sbom.am `make sbom`). GIT is used
|
||||||
|
# only to derive SOURCE_DATE_EPOCH for reproducible SBOM output; all three are
|
||||||
|
# optional and the target reports a clear error when a required one is missing.
|
||||||
AC_PATH_PROG([PYTHON3], [python3])
|
AC_PATH_PROG([PYTHON3], [python3])
|
||||||
AC_PATH_PROG([PYSPDXTOOLS], [pyspdxtools])
|
AC_PATH_PROG([PYSPDXTOOLS], [pyspdxtools])
|
||||||
|
AC_PATH_PROG([GIT], [git])
|
||||||
|
AC_SUBST([PYTHON3])
|
||||||
|
AC_SUBST([PYSPDXTOOLS])
|
||||||
|
AC_SUBST([GIT])
|
||||||
|
|
||||||
# FINAL
|
# FINAL
|
||||||
AC_CONFIG_FILES([Makefile])
|
AC_CONFIG_FILES([Makefile])
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,206 @@
|
||||||
|
# scripts/sbom.am - shared Automake recipe for CRA-compliant SBOM generation.
|
||||||
|
#
|
||||||
|
# One generator (gen-sbom) does the work; each product just describes itself and
|
||||||
|
# includes this fragment. It is deliberately product-agnostic: a Makefile.am
|
||||||
|
# sets a few variables (below) and does `include scripts/sbom.am` to get the
|
||||||
|
# `sbom`, `install-sbom` and `uninstall-sbom` targets.
|
||||||
|
#
|
||||||
|
# The canonical copy lives in the wolfSSL repository (scripts/sbom.am); keep
|
||||||
|
# product copies in sync. gen-sbom is taken from a vendored scripts/gen-sbom
|
||||||
|
# if a product ships one (used automatically), otherwise from a wolfSSL source
|
||||||
|
# tree via WOLFSSL_DIR. wolfSSH uses the WOLFSSL_DIR route; vendoring gen-sbom
|
||||||
|
# for fully offline tarball builds can be added later with no change here.
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# The including Makefile.am MUST set, before `include scripts/sbom.am`:
|
||||||
|
# SBOM_PKGNAME Product name recorded in the SBOM (e.g. wolfssh). Drives
|
||||||
|
# the output filenames and gen-sbom --name.
|
||||||
|
# SBOM_LICENSE_FILE Path to the product's LICENSING file
|
||||||
|
# (e.g. $(srcdir)/LICENSING).
|
||||||
|
#
|
||||||
|
# Optional (defaults shown):
|
||||||
|
# SBOM_OPTIONS_H Path to a product-generated options header (e.g.
|
||||||
|
# wolfMQTT's $(builddir)/wolfmqtt/options.h) that records
|
||||||
|
# the enabled build macros. Set this for products whose
|
||||||
|
# feature flags are NOT in config.h (no AC_DEFINE); when
|
||||||
|
# unset the recipe derives the macros from the compiler +
|
||||||
|
# config.h. Default: unset.
|
||||||
|
# SBOM_ARTIFACT lib | bin - which build output to hash. Default: lib.
|
||||||
|
# SBOM_LIB_STEM Library basename w/o extension. Default: lib$(SBOM_PKGNAME).
|
||||||
|
# SBOM_BIN_NAME Program name when SBOM_ARTIFACT = bin. Default: $(SBOM_PKGNAME).
|
||||||
|
# SBOM_DEP_WOLFSSL yes | no - record wolfSSL as a dependency. Default: no.
|
||||||
|
# SBOM_DEP_OPENSSL yes | no - record OpenSSL as a dependency (wolfProvider /
|
||||||
|
# wolfEngine). Default: no.
|
||||||
|
# SBOM_LICENSE_OVERRIDE SPDX expression to record instead of the licence
|
||||||
|
# detected from SBOM_LICENSE_FILE.
|
||||||
|
# SBOM_LICENSE_TEXT Path to licence text for any LicenseRef-* used in
|
||||||
|
# SBOM_LICENSE_OVERRIDE (required by SPDX 2.3).
|
||||||
|
# SBOM_WOLFSSL_VERSION Version recorded for the wolfSSL dependency;
|
||||||
|
# auto-detected from WOLFSSL_DIR/wolfssl/version.h when unset.
|
||||||
|
# SBOM_OPENSSL_VERSION Version recorded for the OpenSSL dependency;
|
||||||
|
# gen-sbom resolves it via pkg-config when unset.
|
||||||
|
#
|
||||||
|
# The wolfSSL/OpenSSL dependency flags are feature-detected against gen-sbom
|
||||||
|
# --help, so a product wired for them still produces a valid SBOM (with a NOTE)
|
||||||
|
# against a gen-sbom that predates the flag.
|
||||||
|
#
|
||||||
|
# gen-sbom is located at $(srcdir)/scripts/gen-sbom if vendored, else at
|
||||||
|
# $(WOLFSSL_DIR)/scripts/gen-sbom. python3, pyspdxtools and git come from
|
||||||
|
# configure (AC_PATH_PROG); git is used only to derive SOURCE_DATE_EPOCH.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
SBOM_ARTIFACT ?= lib
|
||||||
|
SBOM_LIB_STEM ?= lib$(SBOM_PKGNAME)
|
||||||
|
SBOM_BIN_NAME ?= $(SBOM_PKGNAME)
|
||||||
|
SBOM_DEP_WOLFSSL ?= no
|
||||||
|
SBOM_DEP_OPENSSL ?= no
|
||||||
|
|
||||||
|
SBOM_CDX = $(SBOM_PKGNAME)-$(PACKAGE_VERSION).cdx.json
|
||||||
|
SBOM_SPDX = $(SBOM_PKGNAME)-$(PACKAGE_VERSION).spdx.json
|
||||||
|
SBOM_SPDX_TV = $(SBOM_PKGNAME)-$(PACKAGE_VERSION).spdx
|
||||||
|
sbomdir = $(datadir)/doc/$(PACKAGE)
|
||||||
|
|
||||||
|
# Prefer a vendored gen-sbom; fall back to an external wolfSSL source tree.
|
||||||
|
# The fallback is $(wildcard)-guarded and only consulted when WOLFSSL_DIR is
|
||||||
|
# set, so an unset WOLFSSL_DIR leaves SBOM_GEN empty (and the sbom recipe's
|
||||||
|
# `test -f` prints the "set WOLFSSL_DIR" error) rather than resolving to an
|
||||||
|
# absolute /scripts/gen-sbom that could run an unrelated host script.
|
||||||
|
SBOM_GEN = $(firstword $(wildcard $(srcdir)/scripts/gen-sbom) \
|
||||||
|
$(if $(WOLFSSL_DIR),$(wildcard $(WOLFSSL_DIR)/scripts/gen-sbom)))
|
||||||
|
|
||||||
|
# Library artifact search order (versioned first) covering ELF, Mach-O and PE.
|
||||||
|
# Windows import libs (.lib) come with and without the "lib" prefix.
|
||||||
|
SBOM_LIB_GLOBS = \
|
||||||
|
$(SBOM_LIB_STEM).so.[0-9]* \
|
||||||
|
$(SBOM_LIB_STEM).so \
|
||||||
|
$(SBOM_LIB_STEM).[0-9]*.dylib \
|
||||||
|
$(SBOM_LIB_STEM).dylib \
|
||||||
|
$(SBOM_LIB_STEM).dll \
|
||||||
|
$(SBOM_LIB_STEM).dll.a \
|
||||||
|
$(SBOM_LIB_STEM).lib \
|
||||||
|
$(SBOM_PKGNAME).lib \
|
||||||
|
$(SBOM_LIB_STEM).a
|
||||||
|
|
||||||
|
# Automake requires CLEANFILES to be initialised with `=` before `+=`; the
|
||||||
|
# including Makefile.am must declare `CLEANFILES =` (typically in its primaries
|
||||||
|
# init block) before `include scripts/sbom.am`.
|
||||||
|
CLEANFILES += $(SBOM_CDX) $(SBOM_SPDX) $(SBOM_SPDX_TV)
|
||||||
|
|
||||||
|
.PHONY: sbom install-sbom uninstall-sbom
|
||||||
|
|
||||||
|
# Stage a `make install` into a private tree, discover the installed artifact
|
||||||
|
# (shared/static library or program; ELF/Mach-O/PE), hash it, capture the
|
||||||
|
# configured build macros (AM_CPPFLAGS + config.h), generate SPDX+CDX, validate
|
||||||
|
# the SPDX, then convert to tag-value. The staging tree and temp defines file
|
||||||
|
# are removed unconditionally via `trap`, even on failure. SOURCE_DATE_EPOCH is
|
||||||
|
# honoured for reproducible output (defaults to the last git commit time).
|
||||||
|
sbom:
|
||||||
|
@test -n "$(PYTHON3)" || { \
|
||||||
|
echo "ERROR: 'python3' not found in PATH. Cannot generate SBOM."; \
|
||||||
|
exit 1; }
|
||||||
|
@test -n "$(PYSPDXTOOLS)" || { \
|
||||||
|
echo "ERROR: 'pyspdxtools' not found (pip install spdx-tools)."; \
|
||||||
|
exit 1; }
|
||||||
|
@test -f "$(SBOM_GEN)" || { \
|
||||||
|
echo "ERROR: gen-sbom not found. Vendor scripts/gen-sbom, or re-run:"; \
|
||||||
|
echo " make sbom WOLFSSL_DIR=/path/to/wolfssl"; \
|
||||||
|
exit 1; }
|
||||||
|
@rm -rf $(abs_builddir)/_sbom_staging
|
||||||
|
@set -e; \
|
||||||
|
_defines=`mktemp $(abs_builddir)/_sbom_defines.XXXXXX`; \
|
||||||
|
trap 'rm -rf $(abs_builddir)/_sbom_staging "$$_defines"' EXIT INT TERM HUP; \
|
||||||
|
$(MAKE) install DESTDIR=$(abs_builddir)/_sbom_staging; \
|
||||||
|
sbom_art=""; \
|
||||||
|
if test "$(SBOM_ARTIFACT)" = bin; then \
|
||||||
|
for art in \
|
||||||
|
"$(abs_builddir)/_sbom_staging$(bindir)/$(SBOM_BIN_NAME)" \
|
||||||
|
"$(abs_builddir)/_sbom_staging$(bindir)/$(SBOM_BIN_NAME)".exe; do \
|
||||||
|
if test -f "$$art"; then sbom_art="$$art"; break; fi; \
|
||||||
|
done; \
|
||||||
|
else \
|
||||||
|
for art in \
|
||||||
|
$(addprefix "$(abs_builddir)/_sbom_staging$(libdir)"/,$(SBOM_LIB_GLOBS)) \
|
||||||
|
$(addprefix "$(abs_builddir)/_sbom_staging$(bindir)"/,$(SBOM_LIB_STEM).dll $(SBOM_PKGNAME).dll); do \
|
||||||
|
if test -f "$$art"; then sbom_art="$$art"; break; fi; \
|
||||||
|
done; \
|
||||||
|
fi; \
|
||||||
|
if test -z "$$sbom_art"; then \
|
||||||
|
echo ""; \
|
||||||
|
echo "ERROR: no installed $(SBOM_PKGNAME) artifact found for SBOM."; \
|
||||||
|
echo " (configure with --enable-shared or --enable-static)"; \
|
||||||
|
echo ""; \
|
||||||
|
exit 1; \
|
||||||
|
fi; \
|
||||||
|
echo "SBOM: hashing $$sbom_art"; \
|
||||||
|
opts_h="$(SBOM_OPTIONS_H)"; \
|
||||||
|
if test -z "$$opts_h"; then \
|
||||||
|
opts_h="$$_defines"; \
|
||||||
|
$(CC) -dM -E $(DEFAULT_INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
|
||||||
|
$(if $(wildcard $(abs_builddir)/config.h),-include $(abs_builddir)/config.h) \
|
||||||
|
-x c /dev/null > "$$_defines"; \
|
||||||
|
fi; \
|
||||||
|
if test -z "$${SOURCE_DATE_EPOCH:-}" && test -n "$(GIT)" && \
|
||||||
|
$(GIT) -C "$(srcdir)" rev-parse --git-dir >/dev/null 2>&1; then \
|
||||||
|
sde=`$(GIT) -C "$(srcdir)" log -1 --format=%ct 2>/dev/null`; \
|
||||||
|
if test -n "$$sde"; then SOURCE_DATE_EPOCH="$$sde"; export SOURCE_DATE_EPOCH; fi; \
|
||||||
|
fi; \
|
||||||
|
dep_args=""; \
|
||||||
|
if test "$(SBOM_DEP_WOLFSSL)" = yes; then \
|
||||||
|
if $(PYTHON3) "$(SBOM_GEN)" --help 2>/dev/null \
|
||||||
|
| $(GREP) -q -- '--dep-wolfssl'; then \
|
||||||
|
dep_args="$$dep_args --dep-wolfssl yes"; \
|
||||||
|
wv="$(SBOM_WOLFSSL_VERSION)"; \
|
||||||
|
if test -z "$$wv" && test -f "$(WOLFSSL_DIR)/wolfssl/version.h"; then \
|
||||||
|
wv=`sed -n 's/.*LIBWOLFSSL_VERSION_STRING[ \t]*"\([^"]*\)".*/\1/p' \
|
||||||
|
"$(WOLFSSL_DIR)/wolfssl/version.h"`; \
|
||||||
|
fi; \
|
||||||
|
if test -n "$$wv"; then \
|
||||||
|
dep_args="$$dep_args --dep-version wolfssl=$$wv"; \
|
||||||
|
fi; \
|
||||||
|
else \
|
||||||
|
echo "NOTE: this gen-sbom has no --dep-wolfssl support, so the SBOM"; \
|
||||||
|
echo " will not list wolfssl as a dependency component. That"; \
|
||||||
|
echo " support is added by wolfSSL/wolfssl#10343; until it merges"; \
|
||||||
|
echo " to wolfssl master, point WOLFSSL_DIR at that PR's branch"; \
|
||||||
|
echo " to enable it. The generated SBOM is valid either way."; \
|
||||||
|
fi; \
|
||||||
|
fi; \
|
||||||
|
if test "$(SBOM_DEP_OPENSSL)" = yes; then \
|
||||||
|
if $(PYTHON3) "$(SBOM_GEN)" --help 2>/dev/null \
|
||||||
|
| $(GREP) -q -- '--dep-openssl'; then \
|
||||||
|
dep_args="$$dep_args --dep-openssl yes"; \
|
||||||
|
if test -n "$(SBOM_OPENSSL_VERSION)"; then \
|
||||||
|
dep_args="$$dep_args --dep-version openssl=$(SBOM_OPENSSL_VERSION)"; \
|
||||||
|
fi; \
|
||||||
|
else \
|
||||||
|
echo "NOTE: this gen-sbom has no --dep-openssl support; openssl will"; \
|
||||||
|
echo " not be listed as a dependency component."; \
|
||||||
|
fi; \
|
||||||
|
fi; \
|
||||||
|
$(PYTHON3) "$(SBOM_GEN)" \
|
||||||
|
--name $(SBOM_PKGNAME) \
|
||||||
|
--version $(PACKAGE_VERSION) \
|
||||||
|
--supplier "wolfSSL Inc." \
|
||||||
|
--license-file $(SBOM_LICENSE_FILE) \
|
||||||
|
--options-h "$$opts_h" \
|
||||||
|
--lib "$$sbom_art" \
|
||||||
|
$$dep_args \
|
||||||
|
$(if $(SBOM_LICENSE_OVERRIDE),--license-override '$(SBOM_LICENSE_OVERRIDE)') \
|
||||||
|
$(if $(SBOM_LICENSE_TEXT),--license-text '$(SBOM_LICENSE_TEXT)') \
|
||||||
|
--cdx-out $(abs_builddir)/$(SBOM_CDX) \
|
||||||
|
--spdx-out $(abs_builddir)/$(SBOM_SPDX); \
|
||||||
|
$(PYSPDXTOOLS) --infile $(abs_builddir)/$(SBOM_SPDX) \
|
||||||
|
--outfile $(abs_builddir)/$(SBOM_SPDX_TV)
|
||||||
|
|
||||||
|
install-sbom: sbom
|
||||||
|
$(MKDIR_P) $(DESTDIR)$(sbomdir)
|
||||||
|
$(INSTALL_DATA) $(SBOM_CDX) $(DESTDIR)$(sbomdir)/
|
||||||
|
$(INSTALL_DATA) $(SBOM_SPDX) $(DESTDIR)$(sbomdir)/
|
||||||
|
$(INSTALL_DATA) $(SBOM_SPDX_TV) $(DESTDIR)$(sbomdir)/
|
||||||
|
|
||||||
|
uninstall-sbom:
|
||||||
|
-rm -f $(DESTDIR)$(sbomdir)/$(SBOM_CDX)
|
||||||
|
-rm -f $(DESTDIR)$(sbomdir)/$(SBOM_SPDX)
|
||||||
|
-rm -f $(DESTDIR)$(sbomdir)/$(SBOM_SPDX_TV)
|
||||||
|
|
||||||
|
uninstall-hook: uninstall-sbom
|
||||||
Loading…
Reference in New Issue