add option to use external wolfSSL source with wolfBOOT build

pull/174/head
JacobBarthelmeh 2026-07-28 10:14:03 -06:00
parent 5ed8935a9a
commit 965c98d134
4 changed files with 178 additions and 6 deletions

View File

@ -83,6 +83,47 @@ Artifacts deployed to `tmp/deploy/images/<MACHINE>/`:
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 |

View File

@ -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
}

View File

@ -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 = "<sha>"
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):

View File

@ -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
}