Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
e373823
Resolve the wolfSSL FIPS Ready bundle at CI run time
aidangarske Aug 6, 2026
e947190
Address skoll findings on the FIPS Ready auto-resolve PR
aidangarske Aug 6, 2026
6fe3667
Fix retry semantics, add resolver tests, add wp_hash_copy coverage
aidangarske Aug 6, 2026
610ba51
Fix bundle_exists 4xx classification, portable sort, dist manifest, C…
aidangarske Aug 6, 2026
fd85c6f
Fix errexit masking the indeterminate-bundle marker in list_versions
aidangarske Aug 6, 2026
398db2a
Fail loudly on degraded FIPS discovery, add v7 tag mapping, split dup…
aidangarske Aug 7, 2026
55c0852
Remove em-dash and shorten long comments
aidangarske Aug 7, 2026
78f3415
Show the resolved FIPS module version in job names
aidangarske Aug 7, 2026
99dd4e7
Fix multi-line JSON breaking GITHUB_OUTPUT in FIPS version map
aidangarske Aug 7, 2026
6ae9280
Harden FIPS Ready bundle fetch and gate SHA-512/224 on FIPS v8
aidangarske Aug 8, 2026
7c4c5c3
Fail closed on FIPS bundle verify and fix SHA-512/224 FIPS-v7 gate
aidangarske Aug 10, 2026
73fe7c1
Fix FIPS v8 SHA-512 truncated digest declarations
aidangarske Aug 20, 2026
8d02456
Serve FIPS Ready bundles from ghcr instead of scraping wolfssl.com
aidangarske Aug 25, 2026
a5e4800
Decouple FIPS discovery from publish so fail-closed guard runs
aidangarske Aug 26, 2026
2f83453
Fall back to verified wolfssl.com download until ghcr is seeded
aidangarske Aug 26, 2026
6c11a50
Drop wolfssl.com fallback and verify publish push landed
aidangarske Aug 26, 2026
a5bde77
Unify FIPS ORAS install into scripts/install-oras.sh
aidangarske Aug 26, 2026
262024a
Fail the FIPS version report when options.h or the macro is missing
aidangarske Aug 26, 2026
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
50 changes: 50 additions & 0 deletions .github/actions/fetch-fips-bundle/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Fetch FIPS Ready bundle
description: Pull a wolfSSL FIPS Ready bundle from ghcr (published by the FIPS Ready publish job) and expose its extracted path via FIPS_BUNDLE_PATH.

inputs:
version:
description: Bundle version, e.g. 5.9.2.
required: true
github_token:
description: Token for ghcr login.
required: true

outputs:
bundle_path:
description: Path to the extracted bundle directory.
value: ${{ steps.pull.outputs.bundle_path }}

runs:
using: composite
steps:
- name: Install ORAS (if needed)
shell: bash
run: |
"$GITHUB_WORKSPACE/scripts/install-oras.sh" \
--dest "$GITHUB_WORKSPACE/.bin" --skip-if-present

- name: Pull bundle from ghcr
id: pull
shell: bash
env:
GH_TOKEN: ${{ inputs.github_token }}
run: |
set -euo pipefail
export PATH="$GITHUB_WORKSPACE/.bin:$PATH"
echo "$GH_TOKEN" | oras login --username "${{ github.actor }}" \
--password-stdin ghcr.io || true
OWNER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
VER="${{ inputs.version }}"
DIR="$GITHUB_WORKSPACE/wolfssl-$VER-gplv3-fips-ready"
REF="ghcr.io/$OWNER/wolfprovider/fips-ready:$VER"
# Integrity is the ghcr registry digest. A miss means the bundle was
# never published, so fail loudly rather than silently reaching back to
# wolfssl.com; the publish job (nightly, or a wolfSSL/wolfProvider
# branch) is what seeds ghcr.
if ! "$GITHUB_WORKSPACE/scripts/oras-build-deps.sh" pull "$REF" "$DIR"; then
echo "::error::FIPS Ready bundle $VER is not in ghcr ($REF); run the FIPS Ready publish job to seed it."
exit 1
fi
echo "FIPS_BUNDLE_PATH=$DIR" >> "$GITHUB_ENV"
echo "bundle_path=$DIR" >> "$GITHUB_OUTPUT"
echo "Pulled FIPS bundle from ghcr: $DIR"
19 changes: 15 additions & 4 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ ready_for_review) and on every push to `master`, `main`, or
| `simple.yml` | Builds wolfProvider against the matrix of supported wolfSSL + OpenSSL refs and runs `make check`. The baseline "did anything obvious break" check. |
| `smoke-test.yml` | Minimal end-to-end: build, load the provider into stock OpenSSL, run `openssl list -providers` and a handful of `openssl` subcommands. Catches link-time and provider-registration regressions. |
| `cmdline.yml` | Runs `scripts/cmd_test/do-cmd-tests.sh` — exercises every `openssl` CLI verb (genrsa, pkeyutl, enc, dgst, …) through wolfProvider. |
| `fips-ready.yml` | Same as `simple` but builds wolfSSL with `--enable-fips=ready`. Sanity check that FIPS-ready compiles and basic tests pass without the full FIPS bundle. |
| `fips-ready.yml` | Pulls FIPS Ready bundles from ghcr (`ghcr.io/<owner>/wolfprovider/fips-ready:<ver>`, published by `nightly-fips-ready.yml`) and builds wolfSSL with `--enable-fips=ready` against each. Matrix resolved at run time from `_discover-versions.yml`'s `fips_ready_pr_array` (opt-in via `resolve_fips`, listed from ghcr tags): the back-compat anchors plus the newest bundle. The resolved FIPS module version (`ready` is v7 through 5.9.1, v8 from 5.9.2 on) is read from `HAVE_FIPS_VERSION_MAJOR` at build time and shown in the job summary. |
| `seed-src.yml` | Builds with `--enable-seed-src` (entropy seed source variant) and `-DWP_TEST_SECCOMP_SANDBOX`, then runs the unit tests including the OpenSSH fork+seccomp-sandbox regression suite. |
| `multi-compiler.yml` | Cross-compiler sweep: gcc-9 through gcc-14 and clang-12 through latest. Catches toolchain-specific warnings / UB. |
| `codespell.yml` | Spell-check on tracked source. `*.patch` is excluded because OSP patches mirror upstream source whose original spelling we shouldn't silently rewrite. |
Expand Down Expand Up @@ -59,6 +59,15 @@ push and dominate the merge queue. Nightly is the right cadence for
catching regressions in third-party integration that wouldn't show up
in our unit tests.

`nightly-fips-ready.yml` rides along out-of-wave, running the same steps
as `fips-ready.yml` over `fips_ready_all_array`: every FIPS Ready bundle
in ghcr at or above the 5.8.2 floor, restoring the versions the PR set
leaves out. It also carries the `publish_fips_ready` job, the only place
that touches wolfssl.com: it mirrors any newly hosted bundle into
`ghcr.io/wolfssl/wolfprovider/fips-ready` (skipping ones already there),
so new releases join every FIPS workflow automatically with no PR to
merge. Dispatch this workflow once to seed ghcr.

### Running a nightly job on a PR (label toggles)

`pr-osp-select.yml` lets you pull any nightly job into a PR on demand —
Expand All @@ -69,7 +78,7 @@ per-app workflows stay untouched.
| Label | Effect |
|-------|--------|
| `ci:<name>` | Run that one job (e.g. `ci:hostap`, `ci:curl`, `ci:static-analysis`). Add several to run several. |
| `ci:all` | Run the whole fan-out (all 43 jobs). |
| `ci:all` | Run the whole fan-out (all 44 jobs). |
| (no label) | Nothing runs — a normal PR is unaffected. |

`<name>` is the job key in the table below (the workflow base name, e.g.
Expand All @@ -96,8 +105,9 @@ gh workflow run pr-osp-select.yml --ref <branch> -f jobs="all"

### What runs in the nightly fan-out

43 workflows total: 40 third-party OSS integrations, 2 internal
validations, and the static-analysis suite. Every one of these patches
44 workflows total: 40 third-party OSS integrations, 3 internal
validations (including nightly-fips-ready.yml, reachable via
`ci:fips-ready`), and the static-analysis suite. Every one of these patches
the upstream project (where needed) via `osp/wolfProvider/<app>/*.patch`
from [wolfssl/osp](https://github.com/wolfssl/osp), builds it against
the replace-default wolfProvider `.deb` stack, and runs the project's
Expand Down Expand Up @@ -176,6 +186,7 @@ exercised, with and without `WOLFPROV_FORCE_FAIL=1`.
| `debian-package.yml` | End-to-end check: builds the wolfprov `.deb`s and confirms they install cleanly on a fresh container and the provider loads. |
| `openssl-version.yml` | Sweeps every upstream `openssl-3.X.Y` release tag — catches breakage from OpenSSL point releases before they hit our matrix defaults. |
| `static-analysis.yml` | cppcheck, clang scan-build, Facebook Infer. Heavy enough that it lives in the nightly fan-out rather than per-PR. |
| `nightly-fips-ready.yml` | Builds and tests every resolved FIPS Ready bundle — the nightly superset of the per-PR `fips-ready.yml` gate. Toggle on a PR with `ci:fips-ready`. |

Sanitizers (ASan+UBSan, TSan) run on every PR/push — see the PR table
above. They're fast enough with caching to gate merges, so they don't
Expand Down
69 changes: 69 additions & 0 deletions .github/workflows/_discover-versions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ name: Discover wolfSSL + OpenSSL versions

on:
workflow_call:
inputs:
resolve_fips:
description: 'Resolve FIPS Ready bundle versions too. Only the FIPS workflows set this; every other caller skips the FIPS work it never uses.'
required: false
default: false
type: boolean
outputs:
wolfssl_ref:
description: 'Plain string, actual wolfSSL version in the wolfprov nonfips .deb on ghcr (e.g. v5.8.4-stable)'
Expand Down Expand Up @@ -30,6 +36,18 @@ on:
openssl_all_releases_array:
description: 'JSON array of every upstream openssl-3.X.Y release tag, sorted ascending. Used by openssl-version.yml so the sweep tracks upstream automatically.'
value: ${{ jobs.discover.outputs.openssl_all_releases_array }}
fips_ready_latest:
description: 'Plain string, newest hosted FIPS Ready bundle version (e.g. 5.9.2)'
value: ${{ jobs.discover.outputs.fips_ready_latest }}
fips_ready_pr_array:
description: 'JSON array of FIPS Ready bundle versions for PR runs: the back-compat anchors plus the newest'
value: ${{ jobs.discover.outputs.fips_ready_pr_array }}
fips_ready_all_array:
description: 'JSON array of every hosted FIPS Ready bundle version at or above the floor. Used by the nightly sweep.'
value: ${{ jobs.discover.outputs.fips_ready_all_array }}
fips_ready_resolved:
description: '"true" if FIPS Ready bundle discovery succeeded, "false" if the outputs above are a degraded floor-only fallback. FIPS-specific callers must fail on "false" rather than silently testing a stale matrix.'
value: ${{ jobs.discover.outputs.fips_ready_resolved }}

jobs:
discover:
Expand All @@ -49,6 +67,10 @@ jobs:
openssl_latest_ref: ${{ steps.resolve.outputs.openssl_latest_ref }}
openssl_latest_ref_array: ${{ steps.resolve.outputs.openssl_latest_ref_array }}
openssl_all_releases_array: ${{ steps.resolve.outputs.openssl_all_releases_array }}
fips_ready_latest: ${{ steps.resolve.outputs.fips_ready_latest }}
fips_ready_pr_array: ${{ steps.resolve.outputs.fips_ready_pr_array }}
fips_ready_all_array: ${{ steps.resolve.outputs.fips_ready_all_array }}
fips_ready_resolved: ${{ steps.resolve.outputs.fips_ready_resolved }}
steps:
- name: Install ORAS (best-effort)
run: |
Expand Down Expand Up @@ -85,6 +107,8 @@ jobs:

- name: Resolve versions
id: resolve
env:
RESOLVE_FIPS: ${{ inputs.resolve_fips }}
run: |
set -euo pipefail

Expand Down Expand Up @@ -169,6 +193,45 @@ jobs:
OSSL_ALL_JSON=$(printf '%s\n' "$OSSL_ALL" | jq -R . | jq -s -c .)
OSSL_LATEST=$(echo "$OSSL_ALL" | tail -n 1)

# FIPS Ready bundle versions are the tags published to ghcr by the
# FIPS Ready publish job (nightly-fips-ready.yml), so this lists ghcr
# tags instead of scraping wolfssl.com. Only the FIPS workflows opt in
# through resolve_fips; the ~40 other callers skip it entirely and
# never carry FIPS outputs they don't use.
if [ "${RESOLVE_FIPS:-false}" = "true" ]; then
FR_FLOOR="5.8.2"
FR_OK=true
FR_OWNER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
FR_REPO="ghcr.io/$FR_OWNER/wolfprovider/fips-ready"
FLOOR_KEY=$(printf '%s' "$FR_FLOOR" | awk -F. '{printf "%05d.%05d.%05d",$1,$2,$3}')
FR_ALL=$(oras repo tags "$FR_REPO" 2>/dev/null \
| grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' \
| awk -F. -v fk="$FLOOR_KEY" \
'{k=sprintf("%05d.%05d.%05d",$1,$2,$3); if (k>=fk) print k" "$0}' \
| sort | awk '{print $2}') || true
if [ -z "${FR_ALL:-}" ]; then
# ghcr empty or unreachable: refuse to report green on a
# near-empty matrix. FR_OK lets the FIPS callers fail loudly.
echo "::warning::No FIPS Ready bundle tags in $FR_REPO; falling back to $FR_FLOOR (FIPS callers will fail this run; run the FIPS Ready publish job to seed ghcr)"
FR_ALL="$FR_FLOOR"
FR_OK=false
fi
FR_ALL_JSON=$(printf '%s\n' "$FR_ALL" | jq -R . | jq -s -c .)
FR_LATEST=$(echo "$FR_ALL" | tail -n 1)
# 5.8.4 and 5.9.1 are the back-compat anchors: the last two bundles
# where --enable-fips=ready still meant FIPS v7, which 5.9.2 moved to v8.
# Intersected with the hosted set so a delisted anchor drops out.
FR_PR_JSON=$(printf '%s\n' "5.8.4" "5.9.1" "$FR_LATEST" | jq -R . \
| jq -s -c --argjson all "$FR_ALL_JSON" \
'[.[] | select(. as $v | $all | index($v))] | unique_by(split(".")|map(tonumber))')
if [ "$FR_PR_JSON" = "[]" ]; then
FR_PR_JSON="[\"$FR_LATEST\"]"
fi
echo "FIPS Ready latest bundle: $FR_LATEST"
echo "FIPS Ready PR set: $FR_PR_JSON"
echo "FIPS Ready full set (floor $FR_FLOOR): $FR_ALL_JSON"
fi

echo "wolfSSL .deb ref (actual ghcr deb): $WOLFSSL_DEB_REF"
echo "wolfSSL upstream latest -stable: $WOLFSSL_LATEST"
echo "OpenSSL Bookworm stock: openssl-$OSSL (raw: $OSSL_RAW)"
Expand All @@ -186,4 +249,10 @@ jobs:
echo "openssl_latest_ref=$OSSL_LATEST"
echo "openssl_latest_ref_array=[\"$OSSL_LATEST\"]"
echo "openssl_all_releases_array=$OSSL_ALL_JSON"
if [ "${RESOLVE_FIPS:-false}" = "true" ]; then
echo "fips_ready_latest=$FR_LATEST"
echo "fips_ready_pr_array=$FR_PR_JSON"
echo "fips_ready_all_array=$FR_ALL_JSON"
echo "fips_ready_resolved=$FR_OK"
fi
} >> "$GITHUB_OUTPUT"
81 changes: 60 additions & 21 deletions .github/workflows/fips-ready.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,55 @@ concurrency:
# END OF COMMON SECTION

jobs:
test_fetch_script:
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
name: Test fetch-fips-ready.sh
runs-on: ubuntu-22.04
timeout-minutes: 5
steps:
- name: Checkout wolfProvider
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Run network-free resolver tests
run: ./scripts/test-fetch-fips-ready.sh

discover_versions:
needs: test_fetch_script
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
uses: ./.github/workflows/_discover-versions.yml
with:
resolve_fips: true

fips_ready_test:
# A resolution failure makes discover_versions fall back to testing only
# the floor version. Fine for the ~40 unrelated workflows sharing that
# job, but here it would report green while skipping most of the matrix.
require_fips_resolved:
needs: discover_versions
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
name: FIPS Ready Bundle Test
name: Require FIPS Ready bundle discovery succeeded
runs-on: ubuntu-22.04
timeout-minutes: 2
steps:
- name: Fail if discovery fell back to the floor
if: needs.discover_versions.outputs.fips_ready_resolved != 'true'
run: |
echo "::error::FIPS Ready bundle discovery failed and fell back to a single floor version; refusing to report success on a silently degraded matrix. See the discover_versions job log."
exit 1

fips_ready_test:
needs: [discover_versions, require_fips_resolved]
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
name: FIPS Ready ${{ matrix.wolfssl_bundle_ref }} (${{ matrix.openssl_ref }})
runs-on: ubuntu-22.04
permissions:
contents: read
packages: write
timeout-minutes: 20
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
wolfssl_bundle_ref: [ '5.8.2' ]
wolfssl_bundle_ref: ${{ fromJson(needs.discover_versions.outputs.fips_ready_pr_array) }}
openssl_ref: ${{ fromJson(needs.discover_versions.outputs.openssl_latest_ref_array) }}
# force_fail collapsed into sequential runs in the test step
steps:
Expand All @@ -64,23 +96,10 @@ jobs:
github_token: ${{ secrets.GITHUB_TOKEN }}

- name: Download FIPS Ready Bundle
run: |
# Download FIPS ready bundle from wolfSSL website
BUNDLE_URL="https://www.wolfssl.com/wolfssl-${{matrix.wolfssl_bundle_ref}}-gplv3-fips-ready.zip"

wget -O wolfssl-fips-ready.zip "$BUNDLE_URL"
unzip wolfssl-fips-ready.zip

# Find the extracted directory (build script requires directory, not zip)
BUNDLE_DIR=$(find . -maxdepth 1 -type d -name "*fips-ready*" | head -n 1)
if [ -z "$BUNDLE_DIR" ]; then
echo "ERROR: Could not find FIPS ready bundle directory after extraction"
ls -la
exit 1
fi

echo "FIPS_BUNDLE_PATH=$(pwd)/$BUNDLE_DIR" >> $GITHUB_ENV
echo "Found FIPS bundle directory at: $BUNDLE_DIR"
uses: ./.github/actions/fetch-fips-bundle
with:
version: ${{ matrix.wolfssl_bundle_ref }}
github_token: ${{ secrets.GITHUB_TOKEN }}

- name: Build wolfProvider with FIPS Ready Bundle
run: |
Expand All @@ -91,6 +110,26 @@ jobs:
./scripts/build-wolfprovider.sh --fips-bundle="$FIPS_BUNDLE_PATH" \
--fips-check=ready --wolfssl-ver=v${{matrix.wolfssl_bundle_ref}}-stable

- name: Report resolved FIPS module version
run: |
# Authoritative source: the FIPS version wolfSSL's own build resolved
# `ready` to, read from HAVE_FIPS_VERSION_MAJOR rather than guessed
# from the bundle version. A missing file or macro means the FIPS
# build did not install as expected, so fail rather than report
# "unknown".
OPTS="wolfssl-install/include/wolfssl/options.h"
if [ ! -f "$OPTS" ]; then
echo "::error::wolfSSL options.h not found at $OPTS; FIPS build did not install as expected"
exit 1
fi
MAJ=$(grep -oE '#define +HAVE_FIPS_VERSION_MAJOR +[0-9]+' "$OPTS" | awk '{print $3}')
if [ -z "$MAJ" ]; then
echo "::error::HAVE_FIPS_VERSION_MAJOR not found in $OPTS"
exit 1
fi
echo "Bundle ${{ matrix.wolfssl_bundle_ref }} builds FIPS module v$MAJ"
echo "FIPS module v$MAJ from bundle ${{ matrix.wolfssl_bundle_ref }}" >> "$GITHUB_STEP_SUMMARY"

- name: Push build dependencies
uses: ./.github/actions/oras-build-deps-push
with:
Expand Down
Loading
Loading