diff --git a/README.md b/README.md index 75cc656..0ca2949 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ This layer currently provides recipes for the following wolfSSL products: - [wolfSSH lightweight SSH library](https://www.wolfssl.com/products/wolfssh/) - [wolfMQTT lightweight MQTT client library](https://www.wolfssl.com/products/wolfmqtt/) - [wolfTPM portable TPM 2.0 library](https://www.wolfssl.com/products/wolftpm/) +- [wolfHSM hardware security module framework](https://www.wolfssl.com/products/wolfhsm/) + (source staging only, see `recipes-wolfssl/wolfhsm/README.md`) - [wolfSSL-py A Python wrapper for the wolfSSL library](https://github.com/wolfSSL/wolfssl-py) - [wolfCrypt-py A Python Wrapper for the wolfCrypt API](https://github.com/wolfSSL/wolfcrypt-py) - [wolfPKCS11 A PKCS#11 implementation using wolfSSL](https://github.com/wolfSSL/wolfpkcs11) diff --git a/conf/layer.conf b/conf/layer.conf index 24ac926..80d35e8 100644 --- a/conf/layer.conf +++ b/conf/layer.conf @@ -33,6 +33,8 @@ BBFILES += "${LAYERDIR}/recipes-wolfssl/wolfssl/*.bb \ ${LAYERDIR}/recipes-wolfssl/wolfengine/wolfssl*.bbappend \ ${LAYERDIR}/recipes-wolfssl/wolfboot/*.bb \ ${LAYERDIR}/recipes-wolfssl/wolfboot/*.bbappend \ + ${LAYERDIR}/recipes-wolfssl/wolfhsm/*.bb \ + ${LAYERDIR}/recipes-wolfssl/wolfhsm/*.bbappend \ ${LAYERDIR}/recipes-examples/wolfcrypt/wolfcryptbenchmark/*.bb \ ${LAYERDIR}/recipes-examples/wolfcrypt/wolfcryptbenchmark/*.bbappend \ ${LAYERDIR}/recipes-examples/wolfcrypt/wolfcrypttest/*.bb \ @@ -110,6 +112,7 @@ PREFERRED_PROVIDER_wolfcrypt-py ??= "wolfcrypt-py" PREFERRED_PROVIDER_wolfprovider ??= "wolfprovider" PREFERRED_PROVIDER_wolfengine ??= "wolfengine" PREFERRED_PROVIDER_wolfboot ??= "wolfboot" +PREFERRED_PROVIDER_wolfhsm ??= "wolfhsm" BBFILES += "${@bb.utils.contains('WOLFSSL_TYPE', \ 'fips', \ diff --git a/recipes-wolfssl/wolfhsm/README.md b/recipes-wolfssl/wolfhsm/README.md new file mode 100644 index 0000000..21591db --- /dev/null +++ b/recipes-wolfssl/wolfhsm/README.md @@ -0,0 +1,94 @@ +# wolfHSM (Yocto/OE recipe) + +Stages [wolfHSM](https://github.com/wolfSSL/wolfHSM) sources and headers into +the sysroot so other recipes can compile them into their own binaries. + +## Recipes + +| Recipe | Purpose | +|---|---| +| `wolfhsm_git.bb` | Stages `wolfhsm/` (headers), `src/` and the selected `port/` directories to `${datadir}/wolfhsm`, plus a `wolfhsm.mk` build fragment. Also installs the headers at `${includedir}/wolfhsm`. | + +## Why this stages source instead of building a library + +wolfHSM is configured by the application that uses it. `wolfhsm/wh_settings.h` +does `#include "wolfhsm_cfg.h"` whenever `WOLFHSM_CFG` is defined, and that +header selects the transport, the NVM backend, buffer sizes, whether crypto is +compiled in at all, and much else besides. Two consumers with different +`wolfhsm_cfg.h` files do not share an ABI, so there is no single `libwolfhsm` +that would be correct to ship. + +wolfHSM also has no build system to drive: its top-level `Makefile` only +recurses into `test/`, `benchmark/`, `tools/` and `examples/`, each of which +brings its own `wolfhsm_cfg.h`. Upstream expects you to compile `src/*.c` and +one `port/*/` directory directly into your application. This recipe makes that +possible from a Yocto build without vendoring a checkout. + +## Consuming it from a recipe + +```bitbake +DEPENDS += "wolfhsm" + +do_compile() { + oe_runmake WOLFHSM_DIR="${STAGING_DATADIR}/wolfhsm" +} +``` + +Your Makefile then compiles `$(WOLFHSM_DIR)/src/*.c` and +`$(WOLFHSM_DIR)/port/posix/*.c` with `-I$(WOLFHSM_DIR) -DWOLFHSM_CFG` and an +include path pointing at your own `wolfhsm_cfg.h`. + +Alternatively, include the staged fragment and use the variables it defines: + +```make +include $(WOLFHSM_DIR)/wolfhsm.mk +CFLAGS += $(WOLFHSM_INC) -DWOLFHSM_CFG -I$(MY_CONFIG_DIR) +SRC += $(WOLFHSM_SRC) $(WOLFHSM_PORT_SRC) +``` + +`wolfhsm.mk` resolves `WOLFHSM_DIR` from its own location, so it works +unchanged from a recipe sysroot, an SDK sysroot, or a plain copy. + +## Selecting ports + +wolfHSM ships ports for `posix`, `skeleton`, `microchip`, `infineon`, +`stmicro`, `renesas` and `ti`. Only `posix` is staged by default; staging all +of them would put a lot of unrelated vendor code in every sysroot. Override in +`local.conf` or a bbappend: + +```bitbake +WOLFHSM_PORTS = "posix infineon" +``` + +Naming a port that does not exist in the source tree is a `bbfatal` rather +than a silent no-op. + +## Packaging + +Everything lands in `wolfhsm-dev`; `FILES:${PN}` is explicitly emptied so the +default `${datadir}/${BPN}` claim cannot pull the staging directory into a +runtime package. wolfHSM source is a build input, not a runtime artifact, and +should never appear in a target rootfs. `RDEPENDS:${PN}-dev` is cleared for the +same reason: bitbake's default would make `wolfhsm-dev` depend on a runtime +`wolfhsm` package that is deliberately never produced. + +To cross-compile a wolfHSM consumer from an SDK, have the consumer's own `-dev` +package pull the headers in: + +```bitbake +RDEPENDS:${PN}-dev += "wolfhsm-dev" +``` + +or, if there is no such consumer package, add it to the SDK directly: + +```bitbake +TOOLCHAIN_TARGET_TASK:append = " wolfhsm-dev" +``` + +## Pinning + +`SRCREV` is pinned in `wolfhsm.inc`. Override per-build with: + +```bitbake +SRCREV:pn-wolfhsm = "" +``` diff --git a/recipes-wolfssl/wolfhsm/files/wolfhsm.mk b/recipes-wolfssl/wolfhsm/files/wolfhsm.mk new file mode 100644 index 0000000..f221504 --- /dev/null +++ b/recipes-wolfssl/wolfhsm/files/wolfhsm.mk @@ -0,0 +1,25 @@ +## wolfhsm.mk - build fragment for consumers of the staged wolfHSM sources. +## +## wolfHSM has no build system of its own; it is compiled into the application +## that supplies wolfhsm_cfg.h. Include this fragment to get the source lists +## and include paths for doing that: +## +## include $(SDKTARGETSYSROOT)/usr/share/wolfhsm/wolfhsm.mk +## CFLAGS += $(WOLFHSM_INC) -DWOLFHSM_CFG -I$(MY_CONFIG_DIR) +## SRC += $(WOLFHSM_SRC) $(WOLFHSM_PORT_SRC) +## +## $(MY_CONFIG_DIR) must contain your wolfhsm_cfg.h. -DWOLFHSM_CFG is what +## makes wolfhsm/wh_settings.h include it. + +# Resolved from this fragment's own location, so it is correct whether it is +# read out of a recipe sysroot, an SDK sysroot, or a plain copy. +WOLFHSM_DIR ?= $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) + +# Which platform port to compile. Only ports listed in WOLFHSM_PORTS at +# recipe-build time are present here. +WOLFHSM_PORT ?= posix +WOLFHSM_PORT_DIR ?= $(WOLFHSM_DIR)/port/$(WOLFHSM_PORT) + +WOLFHSM_SRC := $(wildcard $(WOLFHSM_DIR)/src/*.c) +WOLFHSM_PORT_SRC := $(wildcard $(WOLFHSM_PORT_DIR)/*.c) +WOLFHSM_INC := -I$(WOLFHSM_DIR) -I$(WOLFHSM_PORT_DIR) diff --git a/recipes-wolfssl/wolfhsm/wolfhsm.inc b/recipes-wolfssl/wolfhsm/wolfhsm.inc new file mode 100644 index 0000000..bc83a9b --- /dev/null +++ b/recipes-wolfssl/wolfhsm/wolfhsm.inc @@ -0,0 +1,19 @@ +# Shared include for wolfHSM recipes. +# +# wolfHSM is a hardware security module framework: a client/server protocol +# plus a set of platform "ports" mapping transport, flash, time and lock +# primitives onto a given target. + +HOMEPAGE = "https://www.wolfssl.com/products/wolfhsm" +BUGTRACKER = "https://github.com/wolfSSL/wolfHSM/issues" +SECTION = "libs" +LICENSE = "GPL-3.0-only" +LIC_FILES_CHKSUM = "file://LICENSE;md5=1ebbd3e34237af26da5dc08a4e440464" + +# NOTE: pinned to a wolfSSL/wolfHSM main tip at the time of writing. Bump as +# upstream evolves. Downstream users can override via local.conf: +# SRCREV:pn-wolfhsm = "" +SRC_URI = "git://github.com/wolfSSL/wolfHSM.git;protocol=https;branch=main" +SRCREV ?= "4aeecb2c35686bd4daeb40b3537500d15a93aff9" + +S = "${WORKDIR}/git" diff --git a/recipes-wolfssl/wolfhsm/wolfhsm_git.bb b/recipes-wolfssl/wolfhsm/wolfhsm_git.bb new file mode 100644 index 0000000..b9f373b --- /dev/null +++ b/recipes-wolfssl/wolfhsm/wolfhsm_git.bb @@ -0,0 +1,88 @@ +SUMMARY = "wolfHSM hardware security module framework (source staging)" +DESCRIPTION = "wolfHSM provides a client/server protocol for offloading key \ +storage and cryptographic operations to a secure processor or enclave, plus \ +platform ports supplying the transport, flash, time and lock primitives. \ +This recipe stages the wolfHSM sources and headers for other recipes to \ +compile in-tree; it does not build a library. See the note below." + +# WHY THIS STAGES SOURCE INSTEAD OF BUILDING A LIBRARY +# +# wolfHSM is configured by the application that uses it: wolfhsm/wh_settings.h +# does `#include "wolfhsm_cfg.h"` whenever WOLFHSM_CFG is defined, and that +# header selects the transport, the NVM backend, buffer sizes, whether crypto +# is compiled in at all, and much else. Two consumers with different +# wolfhsm_cfg.h files do not share an ABI, so there is no single libwolfhsm +# that would be correct to ship. +# +# wolfHSM also has no build system to drive: its top-level Makefile only +# recurses into test/, benchmark/, tools/ and examples/, each of which brings +# its own wolfhsm_cfg.h. Upstream expects you to compile src/*.c and one +# port/*/ directory directly into your application, which is what this recipe +# makes possible from a Yocto build. +# +# Consumers therefore DEPEND on wolfhsm and point their build at +# ${STAGING_DATADIR}/wolfhsm, supplying their own wolfhsm_cfg.h. See README.md. + +require wolfhsm.inc + +SRC_URI += "file://wolfhsm.mk" + +# Which port/ directories to stage. wolfHSM ships ports for posix, skeleton, +# microchip, infineon, stmicro, renesas and ti; staging all of them would put +# a lot of unrelated vendor code in every sysroot. Override in local.conf or a +# bbappend, e.g. WOLFHSM_PORTS = "posix infineon". +WOLFHSM_PORTS ?= "posix" + +PV = "1.4.0+git" + +# Nothing to build. See the note above. +do_configure[noexec] = "1" +do_compile[noexec] = "1" + +do_install() { + install -d ${D}${datadir}/wolfhsm + + # Headers and core sources. Consumers add -I${STAGING_DATADIR}/wolfhsm so + # that #include "wolfhsm/wh_client.h" resolves, which is why the wolfhsm/ + # directory is preserved rather than flattened. + cp -R --no-dereference --preserve=mode,timestamps \ + ${S}/wolfhsm ${S}/src ${D}${datadir}/wolfhsm/ + + install -d ${D}${datadir}/wolfhsm/port + for port in ${WOLFHSM_PORTS}; do + if [ ! -d ${S}/port/$port ]; then + bbfatal "WOLFHSM_PORTS names '$port' but ${S}/port/$port does not exist." + fi + cp -R --no-dereference --preserve=mode,timestamps \ + ${S}/port/$port ${D}${datadir}/wolfhsm/port/ + done + + # Also expose the headers at the conventional include path, for consumers + # that only need to call the client API against a library someone else + # already compiled (e.g. an application linking a vendor's libwolfhsm). + install -d ${D}${includedir}/wolfhsm + install -m 0644 ${S}/wolfhsm/*.h ${D}${includedir}/wolfhsm/ + + # ${WORKDIR}, not ${UNPACKDIR}: the layer still supports pre-styhead + # releases (LAYERSERIES_COMPAT reaches back to sumo) where file:// SRC_URI + # entries unpack straight into ${WORKDIR}. + install -m 0644 ${WORKDIR}/wolfhsm.mk ${D}${datadir}/wolfhsm/wolfhsm.mk +} + +# ${datadir} is already part of the default SYSROOT_DIRS; named explicitly so +# the staging behaviour this recipe depends on is visible at a glance. +SYSROOT_DIRS += "${datadir}/wolfhsm" + +# Everything lands in -dev. wolfHSM source has no business in a target rootfs: +# it is a build input, not a runtime artifact. FILES:${PN} is emptied because +# the default value claims ${datadir}/${BPN}, which is exactly our staging dir. +FILES:${PN} = "" +FILES:${PN}-dev = "${datadir}/wolfhsm ${includedir}/wolfhsm" + +# bitbake.conf defaults RDEPENDS:${PN}-dev to "${PN} (= ${EXTENDPKGV})". With +# no files in ${PN} that package is never produced, which would leave +# wolfhsm-dev with an unsatisfiable runtime dependency at rootfs/SDK install +# time. There is nothing at runtime to depend on, so clear it. +RDEPENDS:${PN}-dev = "" + +BBCLASSEXTEND = "native nativesdk"