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/classes/wolfssl-commercial.bbclass b/classes/wolfssl-commercial.bbclass index b72ae10..eb8db9f 100644 --- a/classes/wolfssl-commercial.bbclass +++ b/classes/wolfssl-commercial.bbclass @@ -28,8 +28,14 @@ # # Optional format variables: # COMMERCIAL_BUNDLE_FILE - Bundle filename including extension (defaults to .7z) +# COMMERCIAL_BUNDLE_URL - URL bitbake fetches the bundle from, e.g. an https:// +# link to a publicly downloadable GPLv3 FIPS Ready release. Avoids having to +# stage the archive on every build host. # COMMERCIAL_BUNDLE_GCS_URI - gs:// path to the protected bundle # COMMERCIAL_BUNDLE_SRC_DIR - Direct path to already-extracted source directory (skips fetch/extract) +# +# Resolution order in get_commercial_src_uri: SRC_DIR (no fetch), then URL, then +# GCS_URI, then a local file under COMMERCIAL_BUNDLE_DIR. # Commercial bundles already ship generated configure scripts, so skip autoreconf AUTOTOOLS_AUTORECONF = "no" @@ -67,11 +73,17 @@ def get_commercial_src_uri(d): gcs_uri = d.getVar('COMMERCIAL_BUNDLE_GCS_URI') placeholder = d.getVar('COMMERCIAL_BUNDLE_PLACEHOLDER') or '' - if gcs_uri and bundle_archive: + # A plain download URL and a gs:// path are handled identically here: both + # are just a URI bitbake's fetcher understands. COMMERCIAL_BUNDLE_URL takes + # precedence so a recipe can offer the public download as its default while + # still allowing a GCS override. + remote_uri = d.getVar('COMMERCIAL_BUNDLE_URL') or gcs_uri + + if remote_uri and bundle_archive: unpack_flag = ';unpack=false' if bundle_archive.endswith('.7z') else '' sha_flag = f';sha256sum={bundle_sha}' if bundle_sha else '' filename_flag = f';downloadfilename={bundle_archive}' - return f'{gcs_uri}{filename_flag}{unpack_flag}{sha_flag}' + return f'{remote_uri}{filename_flag}{unpack_flag}{sha_flag}' bundle_dir = d.getVar('COMMERCIAL_BUNDLE_DIR') @@ -126,6 +138,7 @@ COMMERCIAL_BUNDLE_SHA ?= "" COMMERCIAL_BUNDLE_TARGET ?= "${WORKDIR}" COMMERCIAL_BUNDLE_PLACEHOLDER ?= "${WOLFSSL_LAYERDIR}/recipes-wolfssl/wolfssl/commercial/files/README.md" COMMERCIAL_BUNDLE_GCS_URI ?= "" +COMMERCIAL_BUNDLE_URL ?= "" COMMERCIAL_BUNDLE_SRC_DIR ?= "" COMMERCIAL_BUNDLE_ARCHIVE = "${@get_commercial_bundle_archive(d)}" 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/conf/wolfssl-fips-ready.conf.sample b/conf/wolfssl-fips-ready.conf.sample index 3b178de..2b37fad 100644 --- a/conf/wolfssl-fips-ready.conf.sample +++ b/conf/wolfssl-fips-ready.conf.sample @@ -6,9 +6,20 @@ # # Instructions: # 1. Set the variables below with your FIPS Ready bundle details -# 2. Set WOLFSSL_SRC_DIR to the directory containing your .zip bundle +# 2. Say where the bundle comes from: WOLFSSL_SRC_URL to have bitbake download +# it, or WOLFSSL_SRC_DIR if the .zip is already staged on the build host # 3. Build once to get the FIPS_HASH value (if using manual mode) # 4. Set FIPS_HASH and rebuild (if using manual mode) +# +# The GPLv3 FIPS Ready bundles are published openly, so the URL route needs no +# credentials and nothing staged by hand. A worked example: +# +# WOLFSSL_VERSION = "5.9.2" +# WOLFSSL_SRC = "wolfssl-5.9.2-gplv3-fips-ready" +# WOLFSSL_BUNDLE_FILE = "wolfssl-5.9.2-gplv3-fips-ready.zip" +# WOLFSSL_SRC_URL = "https://www.wolfssl.com/${WOLFSSL_BUNDLE_FILE}" +# WOLFSSL_SRC_SHA = "e3941a3dd38f054ec041b8146c737bde6fa34cda0300708c8207ab3c4e0ab1ad" +# WOLFSSL_LICENSE_MD5 = "d32239bcb673463ab874e80d47fae504" # Use wolfSSL FIPS Ready as the wolfSSL provider PREFERRED_PROVIDER_virtual/wolfssl = "wolfssl-fips-ready" @@ -33,7 +44,13 @@ WOLFSSL_BUNDLE_FILE = "" # Bundle SHA256 checksum WOLFSSL_SRC_SHA = "" -# Directory containing the .zip bundle (REQUIRED - must be set) +# Where the bundle comes from. Set exactly one of these. +# +# URL bitbake downloads the archive from (no manual staging required). +WOLFSSL_SRC_URL = "" +# +# Directory on the build host already holding the .zip, for hosts with no +# outbound network access. WOLFSSL_SRC_DIR = "" # MD5 of the COPYING (GPLv3) license file in the bundle diff --git a/recipes-wolfssl/wolfboot/README.md b/recipes-wolfssl/wolfboot/README.md index 8a489ee..c750df0 100644 --- a/recipes-wolfssl/wolfboot/README.md +++ b/recipes-wolfssl/wolfboot/README.md @@ -83,6 +83,47 @@ Artifacts deployed to `tmp/deploy/images//`: Note: the private signing key is **not** deployed — it stays on the workstation / secrets store you pointed `WOLFBOOT_SIGNING_KEY` at. +## Using an existing wolfSSL source tree + +By default `wolfboot.inc` fetches a pinned wolfSSL (`SRCREV_wolfssl`) into +`lib/wolfssl` alongside wolfBoot. To build wolfCrypt from a tree you already +have instead, set an absolute path in `local.conf`: + +``` +WOLFBOOT_WOLFSSL_SRC = "/path/to/wolfssl" +``` + +This drops the wolfSSL entry from `SRC_URI` entirely — nothing is downloaded +and `SRCREV_wolfssl` is unused. Both `wolfboot_git.bb` and +`wolfboot-keytools-native_git.bb` pick it up, so the bootloader and the +signing tools stay on one wolfCrypt version. + +Things worth knowing: + +- **It is a source tree, not a library.** wolfBoot compiles the wolfCrypt + sources into a `-nostdlib` bare-metal image; it cannot link the target + `libwolfssl.so` that the `wolfssl` recipe builds. Point this at wolfSSL + *sources*. +- **The tree is copied into `${WORKDIR}` before use**, by the + `do_stage_external_wolfssl` task. wolfBoot writes its object files next to + the wolfCrypt sources, so an in-place build would dirty your tree. The + original may therefore be read-only or shared, but the copy is a full one — + prefer a clean source tree over a working directory with a large `.git` and + build output in it. +- **Prebuilt objects are excluded** from that copy (`*.o`, `*.a`, `*.lo`, + `*.la`, `*.so*`, `.libs`, `.git`). Without this, a natively configured tree + (`./configure && make`) would hand the cross build host x86 objects that + `make` considers newer than their sources. +- **Changes to the tree do trigger a rebuild.** The recipe fingerprints every + file's path, size and mtime into the task hashes, since BitBake would + otherwise hash only the `WOLFBOOT_WOLFSSL_SRC` string and happily reuse a + stale `wolfboot.elf`. +- The path must be absolute. wolfBoot's `Makefile` abspaths + `WOLFBOOT_LIB_WOLFSSL` only for its own default; a command-line override — + how the recipe passes it — reaches every sub-makefile verbatim. The recipe + rejects relative paths rather than let them resolve against whichever + directory each sub-make runs in. + ## SD card layout (wolfBoot A/B scheme) | Partition | Size | Type | Contents | diff --git a/recipes-wolfssl/wolfboot/wolfboot-keytools-native_git.bb b/recipes-wolfssl/wolfboot/wolfboot-keytools-native_git.bb index 12032f0..31e63c7 100644 --- a/recipes-wolfssl/wolfboot/wolfboot-keytools-native_git.bb +++ b/recipes-wolfssl/wolfboot/wolfboot-keytools-native_git.bb @@ -1,8 +1,8 @@ SUMMARY = "wolfBoot signing and key generation tools (native)" DESCRIPTION = "Host-side keygen and sign utilities for wolfBoot secure-boot \ image signing. Builds RSA4096 signing keys and signs firmware images with \ -SHA3-384 hashes. Uses wolfBoot's bundled wolfCrypt (under lib/wolfssl) -- \ -no external wolfSSL dependency." +SHA3-384 hashes. Uses wolfBoot's bundled wolfCrypt (under lib/wolfssl) by \ +default, or the tree named by WOLFBOOT_WOLFSSL_SRC when that is set." require wolfboot.inc @@ -12,11 +12,23 @@ do_configure[noexec] = "1" do_compile() { # Build the keytools (host-side signing/keygen utilities). + # + # Track wolfboot_git.bb's choice of wolfCrypt: the keytools produce the + # keystore and the image signatures that wolfboot.elf then verifies, so + # building the two halves from different wolfSSL versions risks a format + # mismatch that only shows up as a failed verification on the target. + # tools/keytools/Makefile also emits its objects under + # $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src, hence the staged copy here too. + WOLFSSL_DIR="${S}/lib/wolfssl" + if [ -n "${WOLFBOOT_WOLFSSL_SRC}" ]; then + WOLFSSL_DIR="${WOLFBOOT_WOLFSSL_STAGED_SRC}" + fi + oe_runmake -C tools/keytools \ CC="${CC}" \ LD="${CC}" \ WOLFBOOTDIR=${S} \ - WOLFBOOT_LIB_WOLFSSL=${S}/lib/wolfssl \ + WOLFBOOT_LIB_WOLFSSL="$WOLFSSL_DIR" \ V=1 } diff --git a/recipes-wolfssl/wolfboot/wolfboot.inc b/recipes-wolfssl/wolfboot/wolfboot.inc index b3970e2..462b3fd 100644 --- a/recipes-wolfssl/wolfboot/wolfboot.inc +++ b/recipes-wolfssl/wolfboot/wolfboot.inc @@ -3,7 +3,8 @@ # Pulls the wolfBoot source tree and the wolfSSL submodule source side-by-side. # wolfBoot bundles wolfSSL under lib/wolfssl, so we stage wolfSSL there instead # of fetching it from the wolfBoot submodule pointer (keeps the two SRCREVs -# explicit and greppable). +# explicit and greppable). Set WOLFBOOT_WOLFSSL_SRC to build against an +# existing wolfSSL source tree instead, see the block below. HOMEPAGE = "https://github.com/wolfssl/wolfBoot" BUGTRACKER = "https://github.com/wolfssl/wolfBoot/issues" @@ -11,6 +12,26 @@ SECTION = "bootloaders" LICENSE = "GPL-3.0-only" LIC_FILES_CHKSUM = "file://LICENSE;md5=1ebbd3e34237af26da5dc08a4e440464" +# --- Optional: build against an existing wolfSSL source tree ---------------- +# +# Set WOLFBOOT_WOLFSSL_SRC to the absolute path of a wolfSSL source tree to +# build wolfCrypt from it instead of the pinned copy fetched below. When it is +# set the wolfSSL entry drops out of SRC_URI entirely and nothing is downloaded +# making SRCREV_wolfssl unused. +# +# NOTE: this reuses a source *tree*, not a built library. wolfBoot compiles the +# wolfCrypt sources itself into a -nostdlib bare-metal image; it can never link +# against the target libwolfssl.so that the wolfssl recipe produces. +WOLFBOOT_WOLFSSL_SRC ?= "" + +# Private copy of that tree, made by do_stage_external_wolfssl below. The copy +# is not an optimisation: wolfBoot compiles objects *alongside* the wolfCrypt +# sources ($(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/*.o, in both options.mk and +# tools/keytools/Makefile), so building in place would write into the caller's +# tree -- and race with it if it belongs to another recipe. Copying also lets +# WOLFBOOT_WOLFSSL_SRC live somewhere read-only or shared. +WOLFBOOT_WOLFSSL_STAGED_SRC = "${WORKDIR}/wolfssl-external" + # NOTE: SRCREVs below are pinned to wolfSSL/wolfBoot master and # wolfSSL/wolfssl master tips at the time of writing. Bump these as # upstream evolves. Downstream users can override via local.conf: @@ -18,11 +39,93 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=1ebbd3e34237af26da5dc08a4e440464" # SRCREV_wolfboot:pn-wolfboot-keytools-native = "" SRC_URI = " \ git://github.com/wolfssl/wolfBoot.git;protocol=https;branch=master;name=wolfboot;destsuffix=git \ - git://github.com/wolfssl/wolfssl.git;protocol=https;branch=master;name=wolfssl;destsuffix=git/lib/wolfssl \ + ${@'' if d.getVar('WOLFBOOT_WOLFSSL_SRC') else 'git://github.com/wolfssl/wolfssl.git;protocol=https;branch=master;name=wolfssl;destsuffix=git/lib/wolfssl'} \ " SRCREV_wolfboot ?= "9a667f2a7527da2b8e490ae7923665321af2d3ac" SRCREV_wolfssl ?= "1d363f3adceba9d1478230ede476a37b0dcdef24" -SRCREV_FORMAT = "wolfboot_wolfssl" +SRCREV_FORMAT = "${@'wolfboot' if d.getVar('WOLFBOOT_WOLFSSL_SRC') else 'wolfboot_wolfssl'}" + +def wolfboot_wolfssl_src_id(d): + """Fingerprint the external wolfSSL tree: relative path, size and mtime of + every file. Fed into the task hashes below so that editing the tree rebuilds + wolfBoot. BitBake otherwise hashes only the WOLFBOOT_WOLFSSL_SRC *string*, + and changes to its contents would silently reuse a stale wolfboot.elf. + Returns '' (and costs nothing) when the feature is not in use.""" + import os + import hashlib + + src = d.getVar('WOLFBOOT_WOLFSSL_SRC') + if not src or not os.path.isdir(src): + return '' + + h = hashlib.sha256() + for root, dirs, files in os.walk(src): + # Same pruning as the copy below, so the fingerprint tracks exactly + # what gets staged. + dirs[:] = sorted(x for x in dirs if x not in ('.git', '.libs')) + for name in sorted(files): + path = os.path.join(root, name) + try: + st = os.lstat(path) + except OSError: + continue + entry = '%s %d %d\n' % (os.path.relpath(path, src), + st.st_size, st.st_mtime_ns) + h.update(entry.encode()) + return h.hexdigest() + +WOLFBOOT_WOLFSSL_SRC_ID = "${@wolfboot_wolfssl_src_id(d)}" + +python check_wolfboot_wolfssl_src() { + import os + + src = d.getVar('WOLFBOOT_WOLFSSL_SRC') or '' + if not src: + return + if not os.path.isabs(src): + bb.fatal("WOLFBOOT_WOLFSSL_SRC='%s' must be an absolute path. wolfBoot's " + "Makefile only abspaths WOLFBOOT_LIB_WOLFSSL when the value " + "comes from a makefile; the recipe passes it on the make " + "command line, which overrides that assignment, so a relative " + "path reaches every sub-makefile verbatim and resolves against " + "whichever working directory each one runs in." % src) + if not os.path.isdir(os.path.join(src, 'wolfcrypt', 'src')): + bb.fatal("WOLFBOOT_WOLFSSL_SRC='%s' does not look like a wolfSSL source " + "tree (no wolfcrypt/src directory)." % src) +} + +do_stage_external_wolfssl() { + if [ -z "${WOLFBOOT_WOLFSSL_SRC}" ]; then + return 0 + fi + + rm -rf "${WOLFBOOT_WOLFSSL_STAGED_SRC}" + mkdir -p "${WOLFBOOT_WOLFSSL_STAGED_SRC}" + + # Prebuilt objects are excluded so a natively-configured wolfSSL tree + # (./configure && make) cannot leak host x86 .o/.a files into the cross + # build: wolfBoot's object paths sit inside this tree, and make would + # happily reuse an existing .o that is newer than its .c. + tar -cf - -C "${WOLFBOOT_WOLFSSL_SRC}" \ + --exclude=.git --exclude=.libs \ + --exclude='*.o' --exclude='*.lo' --exclude='*.a' --exclude='*.la' \ + --exclude='*.so' --exclude='*.so.*' \ + . | tar -xf - -C "${WOLFBOOT_WOLFSSL_STAGED_SRC}" + + # Guard against a half-copied tree: the pipeline above reports only the + # extract side's exit status under a plain POSIX shell. + if [ ! -f "${WOLFBOOT_WOLFSSL_STAGED_SRC}/wolfcrypt/src/asn.c" ]; then + bbfatal "Failed to stage WOLFBOOT_WOLFSSL_SRC='${WOLFBOOT_WOLFSSL_SRC}':" \ + "wolfcrypt/src/asn.c is missing from the copy at" \ + "${WOLFBOOT_WOLFSSL_STAGED_SRC}." + fi + + chmod -R u+w "${WOLFBOOT_WOLFSSL_STAGED_SRC}" +} +do_stage_external_wolfssl[prefuncs] += "check_wolfboot_wolfssl_src" +do_stage_external_wolfssl[vardeps] += "WOLFBOOT_WOLFSSL_SRC_ID" +do_compile[vardeps] += "WOLFBOOT_WOLFSSL_SRC_ID" +addtask stage_external_wolfssl after do_unpack before do_compile python () { if d.getVar('UNPACKDIR', False): diff --git a/recipes-wolfssl/wolfboot/wolfboot_git.bb b/recipes-wolfssl/wolfboot/wolfboot_git.bb index 182be2e..b39b8ae 100644 --- a/recipes-wolfssl/wolfboot/wolfboot_git.bb +++ b/recipes-wolfssl/wolfboot/wolfboot_git.bb @@ -133,6 +133,21 @@ do_compile() { # the resulting AArch64 binary on the x86_64 build host. Point it at # the native keygen from wolfboot-keytools-native instead. NATIVE_KEYGEN="$(command -v wolfboot-keygen)" + + # Build wolfCrypt from a caller-supplied wolfSSL tree when asked. + # WOLFBOOT_LIB_WOLFSSL is wolfBoot's way to set an external wolfSSL source + # location. Leaving it unset keeps the in-tree lib/wolfssl fetched + # by wolfboot.inc. Always point it at the WORKDIR copy staged by + # do_stage_external_wolfssl, never at the caller's tree because the build + # needs to write object files into this directory. Unquoted below so it + # vanishes when empty. + WOLFSSL_LIB_ARG="" + if [ -n "${WOLFBOOT_WOLFSSL_SRC}" ]; then + WOLFSSL_LIB_ARG="WOLFBOOT_LIB_WOLFSSL=${WOLFBOOT_WOLFSSL_STAGED_SRC}" + bbnote "wolfBoot: building wolfCrypt from ${WOLFBOOT_WOLFSSL_SRC}" \ + "(staged at ${WOLFBOOT_WOLFSSL_STAGED_SRC})" + fi + make wolfboot.elf \ CROSS_COMPILE=${TARGET_PREFIX} \ CC="${TARGET_PREFIX}gcc $SYSROOT_FLAG" \ @@ -140,6 +155,7 @@ do_compile() { USER_PRIVATE_KEY="${WOLFBOOT_SIGNING_KEY}" \ USER_PUBLIC_KEY="$PUBKEY_FOR_MAKE" \ KEYGEN_TOOL="$NATIVE_KEYGEN" \ + $WOLFSSL_LIB_ARG \ ${WOLFBOOT_EXTRA_MAKE_FLAGS} \ V=1 } diff --git a/recipes-wolfssl/wolfhsm/README.md b/recipes-wolfssl/wolfhsm/README.md new file mode 100644 index 0000000..d85bb2c --- /dev/null +++ b/recipes-wolfssl/wolfhsm/README.md @@ -0,0 +1,103 @@ +# 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` for `${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` for `${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. + +Both are set from an anonymous python function via `wolfssl_varSet()` from +`wolfssl-compatibility.bbclass`, because the colon override syntax +(`FILES:${PN}`) does not parse on releases older than honister, which this +layer still supports. + +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 = "" +``` + +The snippets above use the colon override syntax of honister and later. On +sumo through hardknott, write them with underscores instead +(`RDEPENDS_${PN}-dev`, `TOOLCHAIN_TARGET_TASK_append`, `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..a401750 --- /dev/null +++ b/recipes-wolfssl/wolfhsm/wolfhsm_git.bb @@ -0,0 +1,97 @@ +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 + +# For wolfssl_varSet(): the package variables below have to be written with +# either ':' or '_' depending on the Yocto release, and this layer still +# supports both (LAYERSERIES_COMPAT reaches back to sumo). +inherit wolfssl-compatibility + +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" + +python __anonymous() { + # Everything lands in -dev. wolfHSM source has no business in a target + # rootfs: it is a build input, not a runtime artifact. FILES for ${PN} is + # emptied because the default value claims ${datadir}/${BPN}, which is + # exactly our staging dir. + wolfssl_varSet(d, 'FILES', '${PN}', '') + wolfssl_varSet(d, 'FILES', '${PN}-dev', + d.expand('${datadir}/wolfhsm ${includedir}/wolfhsm')) + + # bitbake.conf defaults RDEPENDS for ${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. + wolfssl_varSet(d, 'RDEPENDS', '${PN}-dev', '') +} + +BBCLASSEXTEND = "native nativesdk" diff --git a/recipes-wolfssl/wolfssl/wolfssl-fips-ready.bb b/recipes-wolfssl/wolfssl/wolfssl-fips-ready.bb index a482b19..00c90a1 100644 --- a/recipes-wolfssl/wolfssl/wolfssl-fips-ready.bb +++ b/recipes-wolfssl/wolfssl/wolfssl-fips-ready.bb @@ -33,16 +33,30 @@ DEFAULT_PREFERENCE = "-1" # WOLFSSL_VERSION = "x.x.x" # WOLFSSL_SRC = "wolfssl-x.x.x-commercial-fips-ready" # WOLFSSL_SRC_SHA = "sha256sum of bundle" -# WOLFSSL_SRC_DIR = "/path/to/directory/containing/zip" # WOLFSSL_BUNDLE_FILE = "wolfssl-x.x.x-commercial-fips-ready.zip" # WOLFSSL_LICENSE_MD5 = "md5sum of COPYING file" # FIPS_HASH = "hash value after first build" (for FIPS validation, if using manual mode) +# plus one of WOLFSSL_SRC_URL / WOLFSSL_SRC_DIR / WOLFSSL_SRC_DIRECTORY to say +# where the bundle comes from; see the block below. -# Commercial bundle configuration -# WOLFSSL_SRC_DIR must be set in local.conf to the directory containing the .zip bundle -# Optionally set WOLFSSL_SRC_DIRECTORY to point directly to already-extracted source +# Bundle configuration. Three ways to supply the FIPS Ready sources, in the +# order the class resolves them: +# +# 1. WOLFSSL_SRC_DIRECTORY - an already-extracted source tree. No fetch. +# 2. WOLFSSL_SRC_URL - a URL bitbake downloads the archive from. The +# GPLv3 FIPS Ready bundles are published openly, so +# this needs no credentials and no manual staging: +# WOLFSSL_VERSION = "5.9.2" +# WOLFSSL_SRC_URL = "https://www.wolfssl.com/${WOLFSSL_SRC}.zip" +# Set WOLFSSL_SRC_SHA to the archive's sha256sum. +# 3. WOLFSSL_SRC_DIR - a local directory holding the archive, for build +# hosts with no outbound network access. +# +# Left unset, the recipe parses but does not build, which is what keeps it out +# of the way of unrelated bitbake invocations. WOLFSSL_SRC_DIR ?= "" WOLFSSL_SRC_DIRECTORY ?= "" +WOLFSSL_SRC_URL ?= "" WOLFSSL_BUNDLE_FILE ?= "" # Map to commercial class variables @@ -52,6 +66,7 @@ COMMERCIAL_BUNDLE_FILE = "${WOLFSSL_BUNDLE_FILE}" COMMERCIAL_BUNDLE_PASS = "" COMMERCIAL_BUNDLE_SHA = "${WOLFSSL_SRC_SHA}" COMMERCIAL_BUNDLE_TARGET = "${WORKDIR}" +COMMERCIAL_BUNDLE_URL = "${WOLFSSL_SRC_URL}" COMMERCIAL_BUNDLE_GCS_URI = "" COMMERCIAL_BUNDLE_GCS_TOOL = "" COMMERCIAL_BUNDLE_SRC_DIR = "${WOLFSSL_SRC_DIRECTORY}"