Skip to content
Open
53 changes: 53 additions & 0 deletions .github/actions/apt-retry/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: apt install with retry
description: >-
Install apt packages with bounded retries and an Acquire timeout, so a slow
or flaky mirror retries instead of cancelling the job. Used by the jobs that
cannot move into the ghcr CI image (mixed-OS matrices, version-specific
compilers, large niche package sets). Jobs whose deps are baked into
ghcr.io/wolfssl/wolftpm-ci use `container:` instead of this action.

inputs:
packages:
description: Space-separated list of apt packages to install.
required: true
retries:
description: Number of attempts before failing.
required: false
default: "3"
retry-delay:
description: Initial delay between attempts (seconds, grows each attempt).
required: false
default: "10"
sudo:
description: Prefix apt with sudo (set "false" when already root).
required: false
default: "true"

runs:
using: composite
steps:
- name: Install ${{ inputs.packages }}
shell: bash
run: |
set -u
export DEBIAN_FRONTEND=noninteractive
SUDO=""
if [ "${{ inputs.sudo }}" = "true" ] && command -v sudo >/dev/null 2>&1; then
SUDO="sudo"
fi
RETRIES="${{ inputs.retries }}"
DELAY="${{ inputs.retry-delay }}"
TIMEOUT="-o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30"
for i in $(seq 1 "$RETRIES"); do
if $SUDO apt-get $TIMEOUT update -q && \
$SUDO apt-get install -y ${{ inputs.packages }}; then
exit 0
fi
if [ "$i" -eq "$RETRIES" ]; then
echo "::error::apt-get failed after $RETRIES attempts"
exit 1
fi
echo "::warning::apt-get failed (attempt $i/$RETRIES), retrying in ${DELAY}s..."
sleep "$DELAY"
DELAY=$((DELAY * 2))
done
94 changes: 94 additions & 0 deletions .github/actions/setup-ibmswtpm/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Setup ibmswtpm2
description: >-
Build (or restore from cache) the IBM SW TPM 2.0 simulator for wolfTPM CI.
Resolves the requested ref to a commit so the cache key is stable per
upstream commit, restores the previously built ./ibmswtpm2 tree on a hit, and
otherwise does a shallow clone + parallel build. The built simulator is left
at ./ibmswtpm2/src/tpm_server so callers can launch it (port/lifecycle stay
in the workflow, since they differ per job).

inputs:
ref:
description: ibmswtpm2 git ref (branch, tag, or commit) to build.
required: false
default: master
max-loaded-objects:
description: >-
When set, patch TpmProfile_Misc.h MAX_LOADED_OBJECTS to this value before
building (tpm2-tools ESYS needs extra transient object slots). Empty keeps
the upstream default. Folded into the cache key so variants do not collide.
required: false
default: ""
parallel:
description: Pass -j$(nproc) to make when "true".
required: false
default: "true"

outputs:
server-path:
description: Path to the built tpm_server binary.
value: ibmswtpm2/src/tpm_server

runs:
using: composite
steps:
- name: Resolve ibmswtpm2 ${{ inputs.ref }} and compute cache key
id: key
shell: bash
run: |
set -eo pipefail
# Resolve the ref to a commit so the cache key tracks the upstream tree,
# not a moving branch name. ls-remote returns nothing for a raw SHA, in
# which case the ref already is the commit.
URL=https://github.com/kgoldman/ibmswtpm2.git
SHA=$(git ls-remote "$URL" "${{ inputs.ref }}^{}" | awk 'NR==1{print $1}')
if [ -z "$SHA" ]; then
SHA=$(git ls-remote "$URL" "${{ inputs.ref }}" | awk 'NR==1{print $1}')
fi
if [ -z "$SHA" ]; then
SHA="${{ inputs.ref }}"
fi
# The MAX_LOADED_OBJECTS patch produces a different binary, so fold it
# into the key as a variant suffix (empty -> "default").
VARIANT="${{ inputs.max-loaded-objects }}"
if [ -z "$VARIANT" ]; then
VARIANT="default"
else
VARIANT="mlo${VARIANT}"
fi
echo "sha=$SHA" >> "$GITHUB_OUTPUT"
echo "cache-key=ibmswtpm2-${{ runner.os }}-${SHA}-${VARIANT}-v1" >> "$GITHUB_OUTPUT"
echo "ibmswtpm2 ${{ inputs.ref }} -> $SHA (variant $VARIANT)"

- name: Restore ibmswtpm2 build cache
id: cache
uses: actions/cache@v4
with:
path: ibmswtpm2
key: ${{ steps.key.outputs.cache-key }}

- name: Build ibmswtpm2 (cache miss)
if: steps.cache.outputs.cache-hit != 'true'
shell: bash
run: |
set -eo pipefail
rm -rf ibmswtpm2
# Fetch the exact resolved commit so the built tree matches the cache
# key (GitHub allows fetching any reachable commit).
git init -q ibmswtpm2
cd ibmswtpm2
git remote add origin https://github.com/kgoldman/ibmswtpm2.git
git fetch --depth 1 origin "${{ steps.key.outputs.sha }}"
git checkout -q --detach FETCH_HEAD
cd src
if [ -n "${{ inputs.max-loaded-objects }}" ]; then
# Increase transient object slots: tpm2-tools ESYS creates transient
# salt keys for HMAC sessions which consume extra slots.
sed -i 's/#define MAX_LOADED_OBJECTS.*/#define MAX_LOADED_OBJECTS ${{ inputs.max-loaded-objects }}/' \
TpmProfile_Misc.h
fi
if [ "${{ inputs.parallel }}" = "true" ]; then
make -j"$(nproc 2>/dev/null || echo 2)"
else
make
fi
101 changes: 101 additions & 0 deletions .github/actions/setup-wolfcose/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
name: Setup wolfCOSE
description: >-
Build (or restore from cache) wolfCOSE for wolfTPM CI. Resolves the requested
ref to a commit so the cache key is stable per upstream commit, restores the
previously built ./wolfcose tree on a hit, and otherwise does a shallow clone
+ parallel build. wolfCOSE links against an already-installed wolfSSL (its
sole crypto dependency), so pass the wolfSSL commit it builds against via
wolfssl-sha to keep the cache keyed to that pairing. Run setup-wolfssl first.

inputs:
ref:
description: wolfCOSE git ref (branch, tag, or commit) to build.
required: false
default: master
wolfssl-sha:
description: >-
The wolfSSL commit wolfCOSE is built against (from setup-wolfssl). Folded
into the cache key so a wolfSSL bump invalidates the wolfCOSE build.
required: false
default: ""
extra-cflags:
description: Extra CFLAGS passed to wolfCOSE make as EXTRA_CFLAGS.
required: false
default: ""
make-target:
description: make target to build (empty builds the default static lib).
required: false
default: ""
cc:
description: C compiler to build wolfCOSE with.
required: false
default: gcc
parallel:
description: Pass -j$(nproc) to make when "true".
required: false
default: "true"

outputs:
path:
description: Path to the built wolfCOSE source tree.
value: wolfcose

runs:
using: composite
steps:
- name: Resolve wolfCOSE ${{ inputs.ref }} and compute cache key
id: key
shell: bash
run: |
set -eo pipefail
# Resolve the ref to a commit so the cache key tracks the upstream tree,
# not a moving branch name. ls-remote returns nothing for a raw SHA.
URL=https://github.com/wolfSSL/wolfCOSE.git
SHA=$(git ls-remote "$URL" "${{ inputs.ref }}^{}" | awk 'NR==1{print $1}')
if [ -z "$SHA" ]; then
SHA=$(git ls-remote "$URL" "${{ inputs.ref }}" | awk 'NR==1{print $1}')
fi
if [ -z "$SHA" ]; then
SHA="${{ inputs.ref }}"
fi
# Fold the wolfSSL SHA and build-affecting inputs into the key so the
# wolfCOSE cache is invalidated when any of them change.
HASH_CMD="sha256sum"
command -v sha256sum >/dev/null 2>&1 || HASH_CMD="shasum -a 256"
CFGHASH=$(printf '%s\037%s\037%s\037%s' \
"${{ inputs.wolfssl-sha }}" \
"${{ inputs.extra-cflags }}" \
"${{ inputs.make-target }}" \
"${{ inputs.cc }}" | $HASH_CMD | cut -c1-16)
echo "sha=$SHA" >> "$GITHUB_OUTPUT"
echo "cache-key=wolfcose-${{ runner.os }}-${SHA}-${CFGHASH}-v1" >> "$GITHUB_OUTPUT"
echo "wolfCOSE ${{ inputs.ref }} -> $SHA (cfg $CFGHASH)"

- name: Restore wolfCOSE build cache
id: cache
uses: actions/cache@v4
with:
path: wolfcose
key: ${{ steps.key.outputs.cache-key }}

- name: Build wolfCOSE (cache miss)
if: steps.cache.outputs.cache-hit != 'true'
shell: bash
run: |
set -eo pipefail
rm -rf wolfcose
# Fetch the exact resolved commit so the built tree matches the key.
git init -q wolfcose
cd wolfcose
git remote add origin https://github.com/wolfSSL/wolfCOSE.git
git fetch --depth 1 origin "${{ steps.key.outputs.sha }}"
git checkout -q --detach FETCH_HEAD
ARGS=""
if [ -n "${{ inputs.extra-cflags }}" ]; then
ARGS="$ARGS EXTRA_CFLAGS=\"${{ inputs.extra-cflags }}\""
fi
JOBS=""
if [ "${{ inputs.parallel }}" = "true" ]; then
JOBS="-j$(nproc 2>/dev/null || echo 2)"
fi
eval CC="${{ inputs.cc }}" make $JOBS ${{ inputs.make-target }} $ARGS
37 changes: 37 additions & 0 deletions .github/docker/wolftpm-ci/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# wolfTPM CI base image.
#
# Bakes the apt dependencies that wolfTPM CI jobs install on every run so
# the apt mirror is never contacted on the happy path (the source of the
# intermittent `apt-get update` timeouts). Mirrors the wolfBoot CI image
# model (ghcr.io/wolfssl/wolfboot-ci-*): jobs run inside this image via
# `container: image: ghcr.io/wolfssl/wolftpm-ci:<tag>` instead of calling
# `sudo apt-get install`.
#
# Built and pushed by .github/workflows/publish-ci-image.yml. Pin consumers
# to a version tag (e.g. :v1.0), never :latest, and bump the tag when this
# file changes.
FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install --no-install-recommends -y \
autoconf \
automake \
build-essential \
ca-certificates \
clang \
clang-tools \
cmake \
git \
iproute2 \
libclang-rt-18-dev \
libssl-dev \
libtool \
libtss2-tcti-mssim0 \
openssl \
pkg-config \
sudo \
tpm2-tools \
valgrind \
zip \
&& rm -rf /var/lib/apt/lists/*
20 changes: 11 additions & 9 deletions .github/workflows/cmake-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,13 @@ jobs:
#pull wolfTPM
- uses: actions/checkout@master

# Install cmake
# Install cmake (matrix includes a Windows leg, so this job stays on the host
# runner; apt is made resilient instead of moving into the Linux CI image)
- name: Install cmake
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y cmake
uses: ./.github/actions/apt-retry
with:
packages: cmake

#pull and build wolfssl
# Every matrix entry builds the identical wolfSSL (only the wolfTPM options
Expand Down Expand Up @@ -220,14 +221,15 @@ jobs:
name: CMake static export check
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
runs-on: ubuntu-latest
# cmake baked into the CI image — no apt mirror on the happy path.
container:
image: ghcr.io/wolfssl/wolftpm-ci:v1.0
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@master

- name: Install cmake
run: |
sudo apt-get update
sudo apt-get install -y cmake

# Same wolfSSL config as the matrix build above, so it shares that cache
# entry (key includes only OS + commit, not the wolfTPM options).
- name: Resolve wolfSSL commit
Expand Down
8 changes: 5 additions & 3 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ jobs:
- name: Checkout wolfTPM
uses: actions/checkout@v4

# CodeQL's action is fiddly inside a custom container, so keep this job
# on the host runner and just make apt resilient to mirror timeouts.
- name: Install build tools
run: |
sudo apt-get update
sudo apt-get install -y autoconf automake libtool
uses: ./.github/actions/apt-retry
with:
packages: autoconf automake libtool

# Full-feature wolfSSL so every wolfTPM code path is compiled and
# therefore visible to CodeQL.
Expand Down
18 changes: 18 additions & 0 deletions .github/workflows/coverity-scan-fixes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,35 @@ jobs:
with:
ref: master

# Cache the built wolfSSL tree keyed on the resolved upstream commit so it
# is rebuilt only when wolfSSL master moves, not on every run.
- name: Resolve wolfSSL commit
id: wolfssl_rev
run: echo "sha=$(git ls-remote https://github.com/wolfSSL/wolfssl.git master | awk 'NR==1{print $1}')" >> "$GITHUB_OUTPUT"

- name: Cache wolfSSL build
id: wolfssl_cache
uses: actions/cache@v4
with:
path: wolfssl
key: wolfssl-coverity-fixes-${{ runner.os }}-${{ steps.wolfssl_rev.outputs.sha }}-v1

- uses: actions/checkout@master
if: steps.wolfssl_cache.outputs.cache-hit != 'true'
with:
repository: wolfssl/wolfssl
ref: ${{ steps.wolfssl_rev.outputs.sha }}
path: wolfssl
- name: wolfssl autogen
if: steps.wolfssl_cache.outputs.cache-hit != 'true'
working-directory: ./wolfssl
run: ./autogen.sh
- name: wolfssl configure
if: steps.wolfssl_cache.outputs.cache-hit != 'true'
working-directory: ./wolfssl
run: ./configure --enable-wolftpm --enable-pkcallbacks --enable-keygen CFLAGS="-DWC_RSA_NO_PADDING"
- name: wolfssl make
if: steps.wolfssl_cache.outputs.cache-hit != 'true'
working-directory: ./wolfssl
run: make
- name: wolfssl make install
Expand Down
Loading
Loading