From 2df14cf6b9017a75ed001101c7a2bcdfb80eb19a Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 18:39:42 -0700 Subject: [PATCH 1/6] feat: add sbom target to Makefile Adds sbom target that calls gen-sbom to produce CycloneDX and SPDX output files. Extracts version from WOLFSENTRY_VERSION_MAJOR/MINOR/TINY macros in wolfsentry/wolfsentry.h. Sources enumerated from src/*.c. Requires WOLFSSL_DIR or GEN_SBOM pointing to gen-sbom. --- Makefile | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/Makefile b/Makefile index ff042fe..8a9011f 100644 --- a/Makefile +++ b/Makefile @@ -547,6 +547,58 @@ doc: doc-html $(BUILD_TOP)/doc/pdf/refman.pdf doc-clean: doc-html-clean doc-pdf-clean +# SBOM generation (CRA compliance) +SBOM_VERSION := $(shell awk '/^#define WOLFSENTRY_VERSION_MAJOR/{maj=$$3} /^#define WOLFSENTRY_VERSION_MINOR/{min=$$3} /^#define WOLFSENTRY_VERSION_TINY/{tiny=$$3} END{print maj"."min"."tiny}' \ + '$(SRC_TOP)/wolfsentry/wolfsentry.h' 2>/dev/null) +SBOM_CDX = wolfsentry-$(SBOM_VERSION).cdx.json +SBOM_SPDX = wolfsentry-$(SBOM_VERSION).spdx.json +SBOM_SPDX_TV = wolfsentry-$(SBOM_VERSION).spdx + +.PHONY: sbom + +sbom: + $(Q)if [ -z "$(SBOM_VERSION)" ] || [ "$(SBOM_VERSION)" = ".." ]; then \ + echo "ERROR: could not extract version from wolfsentry/wolfsentry.h" 1>&2; \ + exit 1; \ + fi + $(Q)if [ -n "$(GEN_SBOM)" ]; then \ + _gen_sbom="$(GEN_SBOM)"; \ + elif [ -n "$(WOLFSSL_DIR)" ]; then \ + _gen_sbom="$(WOLFSSL_DIR)/scripts/gen-sbom"; \ + else \ + echo "ERROR: set WOLFSSL_DIR (path to wolfssl repo) or GEN_SBOM (path to gen-sbom script)" 1>&2; \ + exit 1; \ + fi; \ + if [ ! -f "$$_gen_sbom" ]; then \ + echo "ERROR: gen-sbom not found: $$_gen_sbom" 1>&2; \ + exit 1; \ + fi; \ + if ! command -v python3 >/dev/null 2>&1; then \ + echo "ERROR: python3 not found in PATH" 1>&2; \ + exit 1; \ + fi; \ + _defines_h=$$(mktemp "$${TMPDIR:-/tmp}/wolfsentry-defines.XXXXXX"); \ + trap 'rm -f "$$_defines_h"' EXIT; \ + if ! $(CC) -dM -E -I'$(SRC_TOP)' -x c /dev/null >"$$_defines_h" 2>/dev/null; then \ + echo "ERROR: $(CC) -dM -E failed" 1>&2; \ + exit 1; \ + fi; \ + _srcs=""; \ + for _f in $(SRCS); do _srcs="$$_srcs $(SRC_TOP)/src/$$_f"; done; \ + python3 "$$_gen_sbom" \ + --name wolfsentry \ + --version "$(SBOM_VERSION)" \ + --supplier "wolfSSL Inc." \ + --license-file "$(SRC_TOP)/LICENSING" \ + --options-h "$$_defines_h" \ + --srcs $$_srcs \ + --cdx-out "$(BUILD_TOP)/$(SBOM_CDX)" \ + --spdx-out "$(BUILD_TOP)/$(SBOM_SPDX)" +ifndef VERY_QUIET + $(Q)echo "SBOM written: $(BUILD_TOP)/$(SBOM_CDX)" + $(Q)echo " $(BUILD_TOP)/$(SBOM_SPDX)" +endif + .PHONY: clean clean: $(Q)rm $(CLEAN_RM_ARGS) From e0e1e8d3b27b4feea269461ad3a71721dc455dd0 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Tue, 23 Jun 2026 17:42:08 -0700 Subject: [PATCH 2/6] docs: add SBOM/EU CRA Compliance section to README and build docs --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index 5fba952..ab00433 100644 --- a/README.md +++ b/README.md @@ -219,3 +219,25 @@ build with wolfSentry integration, and use `--with-wolfsentry=/the/install/path` if wolfSentry is installed in a nonstandard location. The wolfSSL test client/server can be loaded with user-supplied wolfSentry JSON configurations from the command line, using `--wolfsentry-config `. + +## SBOM / EU CRA Compliance + +wolfSentry 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). + +```sh +make sbom WOLFSSL_DIR=/path/to/wolfssl +``` + +Requires `python3` and `pyspdxtools` (`pip install spdx-tools`). `WOLFSSL_DIR` +must point to a wolfssl source tree containing `scripts/gen-sbom` (branch +`feat/sbom-embedded`, or `master` once wolfSSL/wolfssl#10343 merges). + +Output: `wolfsentry-.cdx.json`, `wolfsentry-.spdx.json`, `wolfsentry-.spdx` + +```sh +make install-sbom # installs to $(datadir)/doc/wolfsentry/ +make uninstall-sbom +``` + +For further CRA guidance see [wolfssl/doc/CRA.md](https://github.com/wolfSSL/wolfssl/blob/master/doc/CRA.md). From 352caaaf31675245f730675e6226347e29a91a00 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 6 Jul 2026 15:47:12 -0700 Subject: [PATCH 3/6] fix: single-line SBOM_VERSION shell for make 3.81 --- Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 8a9011f..061a177 100644 --- a/Makefile +++ b/Makefile @@ -548,8 +548,7 @@ doc: doc-html $(BUILD_TOP)/doc/pdf/refman.pdf doc-clean: doc-html-clean doc-pdf-clean # SBOM generation (CRA compliance) -SBOM_VERSION := $(shell awk '/^#define WOLFSENTRY_VERSION_MAJOR/{maj=$$3} /^#define WOLFSENTRY_VERSION_MINOR/{min=$$3} /^#define WOLFSENTRY_VERSION_TINY/{tiny=$$3} END{print maj"."min"."tiny}' \ - '$(SRC_TOP)/wolfsentry/wolfsentry.h' 2>/dev/null) +SBOM_VERSION := $(shell awk '/^#define WOLFSENTRY_VERSION_MAJOR/{maj=$$3} /^#define WOLFSENTRY_VERSION_MINOR/{min=$$3} /^#define WOLFSENTRY_VERSION_TINY/{tiny=$$3} END{print maj"."min"."tiny}' '$(SRC_TOP)/wolfsentry/wolfsentry.h' 2>/dev/null) SBOM_CDX = wolfsentry-$(SBOM_VERSION).cdx.json SBOM_SPDX = wolfsentry-$(SBOM_VERSION).spdx.json SBOM_SPDX_TV = wolfsentry-$(SBOM_VERSION).spdx From cfcc5da15400d8ba9f765b0a710ed75d23478bad Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 6 Jul 2026 15:53:12 -0700 Subject: [PATCH 4/6] fix: escape # in awk so make 3.81 parses shell make treats # as a comment; on GNU Make 3.81 (macOS runner) the /^#define/ awk patterns comment out the rest of the line incl the $(shell) closing paren -> 'unterminated call to function shell'. make 4.x tolerates it (Ubuntu passed). Escape as \# so all make versions see a literal #. Version still resolves (1.6.3). --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 061a177..eb4b8d9 100644 --- a/Makefile +++ b/Makefile @@ -548,7 +548,7 @@ doc: doc-html $(BUILD_TOP)/doc/pdf/refman.pdf doc-clean: doc-html-clean doc-pdf-clean # SBOM generation (CRA compliance) -SBOM_VERSION := $(shell awk '/^#define WOLFSENTRY_VERSION_MAJOR/{maj=$$3} /^#define WOLFSENTRY_VERSION_MINOR/{min=$$3} /^#define WOLFSENTRY_VERSION_TINY/{tiny=$$3} END{print maj"."min"."tiny}' '$(SRC_TOP)/wolfsentry/wolfsentry.h' 2>/dev/null) +SBOM_VERSION := $(shell awk '/^\#define WOLFSENTRY_VERSION_MAJOR/{maj=$$3} /^\#define WOLFSENTRY_VERSION_MINOR/{min=$$3} /^\#define WOLFSENTRY_VERSION_TINY/{tiny=$$3} END{print maj"."min"."tiny}' '$(SRC_TOP)/wolfsentry/wolfsentry.h' 2>/dev/null) SBOM_CDX = wolfsentry-$(SBOM_VERSION).cdx.json SBOM_SPDX = wolfsentry-$(SBOM_VERSION).spdx.json SBOM_SPDX_TV = wolfsentry-$(SBOM_VERSION).spdx From 409ed3f55dd86d2172472db32df4d32b5773ad6f Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Fri, 10 Jul 2026 12:55:31 -0700 Subject: [PATCH 5/6] fix: address SBOM review feedback --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index eb4b8d9..b7e11ae 100644 --- a/Makefile +++ b/Makefile @@ -551,13 +551,12 @@ doc-clean: doc-html-clean doc-pdf-clean SBOM_VERSION := $(shell awk '/^\#define WOLFSENTRY_VERSION_MAJOR/{maj=$$3} /^\#define WOLFSENTRY_VERSION_MINOR/{min=$$3} /^\#define WOLFSENTRY_VERSION_TINY/{tiny=$$3} END{print maj"."min"."tiny}' '$(SRC_TOP)/wolfsentry/wolfsentry.h' 2>/dev/null) SBOM_CDX = wolfsentry-$(SBOM_VERSION).cdx.json SBOM_SPDX = wolfsentry-$(SBOM_VERSION).spdx.json -SBOM_SPDX_TV = wolfsentry-$(SBOM_VERSION).spdx .PHONY: sbom sbom: - $(Q)if [ -z "$(SBOM_VERSION)" ] || [ "$(SBOM_VERSION)" = ".." ]; then \ - echo "ERROR: could not extract version from wolfsentry/wolfsentry.h" 1>&2; \ + $(Q)if ! printf '%s' "$(SBOM_VERSION)" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$$'; then \ + echo "ERROR: could not extract a valid version (got '$(SBOM_VERSION)') from wolfsentry/wolfsentry.h" 1>&2; \ exit 1; \ fi $(Q)if [ -n "$(GEN_SBOM)" ]; then \ @@ -578,12 +577,13 @@ sbom: fi; \ _defines_h=$$(mktemp "$${TMPDIR:-/tmp}/wolfsentry-defines.XXXXXX"); \ trap 'rm -f "$$_defines_h"' EXIT; \ - if ! $(CC) -dM -E -I'$(SRC_TOP)' -x c /dev/null >"$$_defines_h" 2>/dev/null; then \ + if ! $(CC) $(CPPFLAGS) $(CFLAGS) -dM -E -x c /dev/null >"$$_defines_h" 2>/dev/null; then \ echo "ERROR: $(CC) -dM -E failed" 1>&2; \ exit 1; \ fi; \ _srcs=""; \ for _f in $(SRCS); do _srcs="$$_srcs $(SRC_TOP)/src/$$_f"; done; \ + mkdir -p "$(BUILD_TOP)"; \ python3 "$$_gen_sbom" \ --name wolfsentry \ --version "$(SBOM_VERSION)" \ From d23ddd4cb71aa663d0895b6485b0d74bd9735f6e Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Tue, 14 Jul 2026 16:27:35 -0700 Subject: [PATCH 6/6] fix: sbom install targets, real options file --- Makefile | 37 ++++++++++++++++++++++++++++--------- README.md | 2 +- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index b7e11ae..432c0ca 100644 --- a/Makefile +++ b/Makefile @@ -548,13 +548,24 @@ doc: doc-html $(BUILD_TOP)/doc/pdf/refman.pdf doc-clean: doc-html-clean doc-pdf-clean # SBOM generation (CRA compliance) -SBOM_VERSION := $(shell awk '/^\#define WOLFSENTRY_VERSION_MAJOR/{maj=$$3} /^\#define WOLFSENTRY_VERSION_MINOR/{min=$$3} /^\#define WOLFSENTRY_VERSION_TINY/{tiny=$$3} END{print maj"."min"."tiny}' '$(SRC_TOP)/wolfsentry/wolfsentry.h' 2>/dev/null) +SBOM_VERSION := $(shell $(AWK) '/^\#define WOLFSENTRY_VERSION_MAJOR/{maj=$$3} /^\#define WOLFSENTRY_VERSION_MINOR/{min=$$3} /^\#define WOLFSENTRY_VERSION_TINY/{tiny=$$3} END{print maj"."min"."tiny}' '$(SRC_TOP)/wolfsentry/wolfsentry.h' 2>/dev/null) SBOM_CDX = wolfsentry-$(SBOM_VERSION).cdx.json SBOM_SPDX = wolfsentry-$(SBOM_VERSION).spdx.json .PHONY: sbom -sbom: +# The effective build configuration comes from $(OPTIONS_FILE): either the +# generated $(BUILD_TOP)/wolfsentry/wolfsentry_options.h (distilled from the +# real CFLAGS by build_wolfsentry_options_h.awk) or, under USER_SETTINGS_FILE, +# the user's own settings header, which gen-sbom parses with --user-settings +# since it is not a flat define dump. +ifdef USER_SETTINGS_FILE + SBOM_OPTIONS_ARG = --user-settings "$(OPTIONS_FILE)" +else + SBOM_OPTIONS_ARG = --options-h "$(OPTIONS_FILE)" +endif + +sbom: $(OPTIONS_FILE) $(Q)if ! printf '%s' "$(SBOM_VERSION)" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$$'; then \ echo "ERROR: could not extract a valid version (got '$(SBOM_VERSION)') from wolfsentry/wolfsentry.h" 1>&2; \ exit 1; \ @@ -575,12 +586,6 @@ sbom: echo "ERROR: python3 not found in PATH" 1>&2; \ exit 1; \ fi; \ - _defines_h=$$(mktemp "$${TMPDIR:-/tmp}/wolfsentry-defines.XXXXXX"); \ - trap 'rm -f "$$_defines_h"' EXIT; \ - if ! $(CC) $(CPPFLAGS) $(CFLAGS) -dM -E -x c /dev/null >"$$_defines_h" 2>/dev/null; then \ - echo "ERROR: $(CC) -dM -E failed" 1>&2; \ - exit 1; \ - fi; \ _srcs=""; \ for _f in $(SRCS); do _srcs="$$_srcs $(SRC_TOP)/src/$$_f"; done; \ mkdir -p "$(BUILD_TOP)"; \ @@ -589,7 +594,7 @@ sbom: --version "$(SBOM_VERSION)" \ --supplier "wolfSSL Inc." \ --license-file "$(SRC_TOP)/LICENSING" \ - --options-h "$$_defines_h" \ + $(SBOM_OPTIONS_ARG) \ --srcs $$_srcs \ --cdx-out "$(BUILD_TOP)/$(SBOM_CDX)" \ --spdx-out "$(BUILD_TOP)/$(SBOM_SPDX)" @@ -598,6 +603,20 @@ ifndef VERY_QUIET $(Q)echo " $(BUILD_TOP)/$(SBOM_SPDX)" endif +ifndef INSTALL_DOCDIR + INSTALL_DOCDIR := $(INSTALL_DIR)/share/doc/wolfsentry +endif + +.PHONY: install-sbom +install-sbom: sbom + $(Q)mkdir -p $(INSTALL_DOCDIR) + install -p -m 0644 $(BUILD_TOP)/$(SBOM_CDX) $(BUILD_TOP)/$(SBOM_SPDX) $(INSTALL_DOCDIR) + +.PHONY: uninstall-sbom +uninstall-sbom: + $(RM) $(INSTALL_DOCDIR)/$(SBOM_CDX) $(INSTALL_DOCDIR)/$(SBOM_SPDX) + @rmdir $(INSTALL_DOCDIR) 2>/dev/null || exit 0 + .PHONY: clean clean: $(Q)rm $(CLEAN_RM_ARGS) diff --git a/README.md b/README.md index ab00433..0700283 100644 --- a/README.md +++ b/README.md @@ -236,7 +236,7 @@ must point to a wolfssl source tree containing `scripts/gen-sbom` (branch Output: `wolfsentry-.cdx.json`, `wolfsentry-.spdx.json`, `wolfsentry-.spdx` ```sh -make install-sbom # installs to $(datadir)/doc/wolfsentry/ +make install-sbom # installs to $(INSTALL_DOCDIR), default /usr/local/share/doc/wolfsentry/ make uninstall-sbom ```