Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: "gitsubmodule"
directory: "/"
schedule:
interval: "daily"
commit-message:
prefix: "[ci]"
75 changes: 75 additions & 0 deletions .github/workflows/wolfssl-submodule.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: wolfSSL submodule build

# Builds the pinned lib/wolfssl submodule with the documented flags and builds
# plus tests wolfSPDM against it, so Dependabot pin bumps are gated on CI. The
# wolfssl-versions.yml matrix still covers latest-stable and master.

on:
push:
branches: [ 'main', 'release/**' ]
pull_request:
branches: [ '*' ]
repository_dispatch:
types: [nightly-trigger]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
pinned-build:
name: Build against the pinned wolfSSL submodule
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- name: Checkout wolfSPDM with direct submodules
uses: actions/checkout@v4
with:
submodules: true

- name: Test submodule checkout policy
run: sh scripts/test-submodule-policy.sh

- name: Opt in to the standalone wolfSSL checkout
run: git submodule update --init --checkout lib/wolfssl

- name: Install build deps
run: |
sudo apt-get update
sudo apt-get install -y autoconf automake libtool

- name: Build and install the bundled wolfSSL
run: |
cd lib/wolfssl
./autogen.sh
./configure --enable-wolftpm --enable-ecc --enable-sha384 \
--enable-aesgcm --enable-hkdf --enable-sp \
--prefix=$HOME/wolfssl-install
make -j"$(nproc)"
make install

- name: Build wolfSPDM against the submodule
run: |
./autogen.sh
./configure --with-wolfssl=$HOME/wolfssl-install \
--enable-nuvoton --enable-nations --enable-responder
make -j"$(nproc)"

- name: Run unit tests
run: |
export LD_LIBRARY_PATH="$PWD/src/.libs:$HOME/wolfssl-install/lib"
make check

- name: Upload failure logs
if: failure()
uses: actions/upload-artifact@v4
with:
name: wolfssl-submodule-logs
path: |
config.log
test/*.log
test-suite.log
retention-days: 5
7 changes: 7 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[submodule "lib/wolfssl"]
path = lib/wolfssl
Comment thread
aidangarske marked this conversation as resolved.
url = https://github.com/wolfSSL/wolfssl.git
branch = master
ignore = dirty
# Opt in explicitly for standalone builds; skip nested recursive checkouts.
update = none
82 changes: 77 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,22 +75,94 @@ Asymmetric sessions need firmware advertising `KEY_EX_CAP` and `PUB_KEY_ID_CAP`.

## Prerequisites (wolfSSL)

wolfSPDM requires [wolfSSL](https://www.wolfssl.com/) configured with ECC P-384, SHA-384, AES-GCM, and HKDF:
wolfSPDM links against [wolfSSL](https://www.wolfssl.com/). wolfSSL is bundled as
the `lib/wolfssl` submodule at a known-good commit. For a standalone build,
run `git submodule update --init --checkout lib/wolfssl` and build it from
there, or use an existing installation as described below. **`--enable-wolftpm` is required**
on the wolfSSL build (it turns on
the wolfCrypt features wolfSPDM relies on); the remaining flags select the SPDM
algorithm set.

The bundled wolfSSL is opt-in: `.gitmodules` sets `update = none`, so ordinary
recursive clones and submodule updates skip it, including when wolfSPDM is
embedded in another project. `--checkout` explicitly overrides that policy for
a standalone build. Existing local `submodule.lib/wolfssl.update` settings take
precedence; this default does not remove a copy you already initialized.

**Supported wolfSSL versions:**

- Classical SPDM (Algorithm Set B): wolfSSL **>= v5.8.0-stable** (hard minimum, enforced by `configure`).
- ML-KEM key exchange: works on the classical floor, wolfSSL **>= v5.8.0-stable** (detected by the `wc_MlKemKey` API probe).
- ML-DSA authentication: wolfSSL **>= v5.9.2-stable** (the `wc_MlDsaKey` context API lands there). Requesting ML-DSA against older wolfSSL fails `configure`; in auto mode ML-DSA is disabled with a warning.

### Default build (Algorithm Set B: ECC P-384, SHA-384, AES-256-GCM, HKDF)

```bash
git clone https://github.com/wolfSSL/wolfssl.git
cd wolfssl
cd lib/wolfssl
./autogen.sh
./configure --enable-wolftpm --enable-ecc --enable-sha384 \
--enable-aesgcm --enable-hkdf --enable-sp
make
sudo make install
sudo ldconfig
cd ../..
```

`--enable-sp` enables Single Precision math with optimized ECC P-384, required
for Algorithm Set B on ARM64 and other constrained targets. `--enable-all` works
as a superset.

### Post-quantum build (ML-KEM key exchange + ML-DSA authentication)

Needs wolfSSL **>= v5.9.2-stable** for ML-DSA (ML-KEM alone works on v5.8.0).
Post-quantum extends Algorithm Set B rather
than replacing it: ML-KEM and ML-DSA take over key exchange and authentication,
while the secured session still uses SHA-384, AES-256-GCM, and HKDF. Keep the
Set B flags and add `--enable-mldsa` (FIPS 204) and `--enable-mlkem` (FIPS 203):

```bash
cd lib/wolfssl
./autogen.sh
./configure --enable-wolftpm --enable-ecc --enable-sha384 \
--enable-aesgcm --enable-hkdf --enable-sp \
--enable-mldsa --enable-mlkem
make
sudo make install
sudo ldconfig
cd ../..
```

`--enable-sp` enables Single Precision math with optimized ECC P-384, required for SPDM Algorithm Set B on ARM64 and other constrained targets. `--enable-all` works as a superset.
wolfSPDM auto-enables each when the linked wolfSSL provides it (`--disable-mldsa`
/ `--disable-mlkem` force them off).

The pinned wolfSSL submodule can be updated like any checkout: `cd lib/wolfssl`,
check out or rebuild whatever you need (at or above the floors above), then
reinstall. `git submodule update --checkout lib/wolfssl` restores the pin. To
build against your own installed wolfSSL instead, pass `--with-wolfssl=PATH`
to wolfSPDM.

### Sharing wolfSSL with wolfTPM

When wolfSPDM is embedded as `wolfTPM/lib/wolfSPDM`, wolfTPM compiles its sources
directly against wolfTPM's selected wolfSSL. The nested `lib/wolfssl` submodule
does not need to be initialized or built. Initialize wolfTPM's direct dependencies
with `git submodule update --init lib/wolfSPDM lib/wolfssl`, without `--recursive`.

For separate wolfTPM and wolfSPDM builds, install wolfSSL once with the features
required by both consumers, then select that installation in each checkout:

```sh
# In the wolfSPDM checkout, after ./autogen.sh:
./configure --with-wolfssl=/path/to/wolfssl-install

# In the wolfTPM checkout, after ./autogen.sh:
./configure --with-wolfcrypt=/path/to/wolfssl-install --enable-spdm
```

For post-quantum support, build wolfSSL with `--enable-mldsa` (FIPS 204) and/or `--enable-mlkem` (FIPS 203). Use wolfSSL master or a release carrying the `wc_MlDsaKey` and `wc_MlKemKey` APIs. wolfSPDM auto-enables each when the linked wolfSSL provides it (`--disable-mldsa` / `--disable-mlkem` force them off); enabling both gives a fully post-quantum handshake (ML-KEM key exchange + ML-DSA authentication).
With an existing installation, neither bundled wolfSSL checkout is required.
Use the matching wolfSSL headers and library for both builds. Each project's
submodule pin remains available for standalone builds; the integrating project
chooses the shared version and configuration.

## Build

Expand Down
21 changes: 21 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,27 @@ AC_ARG_ENABLE([mlkem],
[enable_mlkem=$enableval],
[enable_mlkem=auto])

# ML-DSA's wc_MlDsaKey context API was introduced in wolfSSL v5.9.2-stable.
# ML-KEM (wc_MlKemKey) is older and is detected by its own probe below, so the
# version floor applies to ML-DSA only.
if test "x$enable_mldsa" != "xno"; then
AC_MSG_CHECKING([wolfSSL version >= v5.9.2-stable for ML-DSA])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
#include <wolfssl/version.h>
#if !defined(LIBWOLFSSL_VERSION_HEX) || LIBWOLFSSL_VERSION_HEX < 0x05009002
#error "wolfSSL < v5.9.2"
#endif
int main(void) { return 0; }
]])],
[AC_MSG_RESULT([yes])],
[AC_MSG_RESULT([no])
if test "x$enable_mldsa" = "xyes"; then
AC_MSG_ERROR([ML-DSA requires wolfSSL >= v5.9.2-stable])
fi
AC_MSG_WARN([wolfSSL < v5.9.2-stable: disabling ML-DSA])
enable_mldsa=no])
fi

have_mldsa_api=no
if test "x$enable_mldsa" != "xno" && test "x$enable_cert" = "xyes"; then
AC_MSG_CHECKING([for the wolfSSL wc_MlDsaKey context API])
Expand Down
1 change: 1 addition & 0 deletions lib/wolfssl
Submodule wolfssl added at 3c5eea
81 changes: 81 additions & 0 deletions scripts/test-submodule-policy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/bin/sh
# Verify the real .gitmodules policy using small, local Git repositories.
set -eu

root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
fixture=$(mktemp -d "${TMPDIR:-/tmp}/wolfspdm-submodules.XXXXXX")
trap 'rm -rf "$fixture"' EXIT
export GIT_CONFIG_GLOBAL=/dev/null
export GIT_CONFIG_NOSYSTEM=1

commit_fixture() {
git -C "$1" -c user.name=Test -c user.email=test@example.com \
commit --quiet -m fixture
}

assert_skipped() {
git_dir=$(git -C "$1" rev-parse --absolute-git-dir)
if [ -e "$1/lib/wolfssl/.git" ] ||
[ -d "$git_dir/modules/lib/wolfssl" ]; then
echo "FAIL: wolfSPDM downloaded its opt-in wolfSSL: $1" >&2
exit 1
fi
}

git -c init.defaultBranch=master init --quiet "$fixture/wolfssl"
echo fixture > "$fixture/wolfssl/marker"
git -C "$fixture/wolfssl" add marker
commit_fixture "$fixture/wolfssl"
wolfssl_sha=$(git -C "$fixture/wolfssl" rev-parse HEAD)

git init --quiet "$fixture/wolfspdm"
cp "$root/.gitmodules" "$fixture/wolfspdm/.gitmodules"
git config -f "$fixture/wolfspdm/.gitmodules" \
submodule.lib/wolfssl.url "$fixture/wolfssl"
git -C "$fixture/wolfspdm" add .gitmodules
git -C "$fixture/wolfspdm" update-index --add --cacheinfo \
160000 "$wolfssl_sha" lib/wolfssl
commit_fixture "$fixture/wolfspdm"
wolfspdm_sha=$(git -C "$fixture/wolfspdm" rev-parse HEAD)

git init --quiet "$fixture/wolftpm"
for name in wolfssl wolfspdm; do
case "$name" in
wolfssl) path=lib/wolfssl; sha=$wolfssl_sha ;;
wolfspdm) path=lib/wolfSPDM; sha=$wolfspdm_sha ;;
esac
git -C "$fixture/wolftpm" config -f .gitmodules \
"submodule.$path.path" "$path"
git -C "$fixture/wolftpm" config -f .gitmodules \
"submodule.$path.url" "$fixture/$name"
git -C "$fixture/wolftpm" update-index --add --cacheinfo \
160000 "$sha" "$path"
done
git -C "$fixture/wolftpm" add .gitmodules
commit_fixture "$fixture/wolftpm"

git -c protocol.file.allow=always clone --quiet --recursive \
"$fixture/wolftpm" "$fixture/recursive"
test -f "$fixture/recursive/lib/wolfssl/marker"
assert_skipped "$fixture/recursive/lib/wolfSPDM"
echo 'PASS: recursive parent clone gets one wolfSSL'

git clone --quiet "$fixture/wolftpm" "$fixture/update"
git -C "$fixture/update" -c protocol.file.allow=always \
submodule update --quiet --init --force --recursive
test -f "$fixture/update/lib/wolfssl/marker"
assert_skipped "$fixture/update/lib/wolfSPDM"
echo 'PASS: recursive forced update skips nested wolfSSL'

git -c protocol.file.allow=always clone --quiet --recursive \
"$fixture/wolfspdm" "$fixture/standalone"
assert_skipped "$fixture/standalone"
git -C "$fixture/standalone" -c protocol.file.allow=always \
submodule update --quiet --init lib/wolfssl
assert_skipped "$fixture/standalone"
echo 'PASS: standalone wolfSSL requires explicit opt-in'

git -C "$fixture/standalone" -c protocol.file.allow=always \
submodule update --quiet --init --checkout lib/wolfssl
test -f "$fixture/standalone/lib/wolfssl/marker"
echo 'PASS: explicit standalone checkout gets the pinned wolfSSL'
Loading