Skip to content
Open
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
97 changes: 97 additions & 0 deletions .github/scripts/run-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/usr/bin/env bash
#
# Copyright (C) 2026 wolfSSL Inc.
# SPDX-License-Identifier: GPL-3.0-or-later
#
# Build and run every wolfPSA Zephyr sample and test on native_sim, reporting a
# pass/fail summary and exiting non-zero on any failure. Used by CI
# (.github/workflows/zephyr-4.x.yml, via .github/scripts/zephyr-4.x/zephyr-test.sh)
# and handy locally.
#
# Must be invoked from inside a west workspace that has the Zephyr RTOS, the
# wolfSSL module, and the mbedtls module (the latter defines the
# PSA_CRYPTO_PROVIDER choice wolfPSA plugs into). The wolfPSA module itself is
# added via EXTRA_ZEPHYR_MODULES, so no manifest entry is required.
#
# Env:
# BOARD board/qualifier (default native_sim/native/64)
# BUILD_ROOT where build dirs go (default: $PWD/build-wolfpsa-ci)
# EXTRA_MODULE_ARG CMake arg to locate the wolfPSA module. Defaults to
# -DEXTRA_ZEPHYR_MODULES=<repo root> (for workspaces where
# wolfPSA is NOT in the west manifest). Set it to "" when
# wolfPSA is already a manifest module (e.g. CI, where
# zephyr-test.sh injects it into the manifest), to avoid
# registering it twice.

set -u

script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
# repo root is two levels up: .github/scripts/ -> repo root
WOLFPSA_ROOT=$(CDPATH= cd -- "${script_dir}/../.." && pwd)
BOARD="${BOARD:-native_sim/native/64}"
BUILD_ROOT="${BUILD_ROOT:-${PWD}/build-wolfpsa-ci}"
EXTRA_MODULE_ARG="${EXTRA_MODULE_ARG--DEXTRA_ZEPHYR_MODULES=${WOLFPSA_ROOT}}"

# name | app path | mode(ztest|sample) | extra west-build args (optional)
#
# psa_consumer_st reruns the consumer with CONFIG_WOLFPSA_THREAD_SAFE=n so the
# single-threaded baseline path (SINGLE_THREADED, no-op lock macros) is built
# and run in CI, not just the default thread-safe (=y) path.
TARGETS="
psa_smoke|${WOLFPSA_ROOT}/zephyr/samples/psa_smoke|sample
psa_consumer|${WOLFPSA_ROOT}/zephyr/tests/psa_consumer|ztest
psa_consumer_st|${WOLFPSA_ROOT}/zephyr/tests/psa_consumer|ztest|-DCONFIG_WOLFPSA_THREAD_SAFE=n
psa_concurrency|${WOLFPSA_ROOT}/zephyr/tests/psa_concurrency|ztest
psa_entropy|${WOLFPSA_ROOT}/zephyr/tests/psa_entropy|ztest
psa_transform|${WOLFPSA_ROOT}/zephyr/tests/psa_transform|ztest
psa_purge|${WOLFPSA_ROOT}/zephyr/tests/psa_purge|ztest
psa_secure_storage|${WOLFPSA_ROOT}/zephyr/tests/psa_secure_storage|ztest
psa_store_unavailable|${WOLFPSA_ROOT}/zephyr/tests/psa_store_unavailable|ztest
psa_tls_coexist|${WOLFPSA_ROOT}/zephyr/tests/psa_tls_coexist|ztest
psa_its|${WOLFPSA_ROOT}/zephyr/tests/psa_its|sample
psa_persistent_key|${WOLFPSA_ROOT}/zephyr/tests/psa_persistent_key|sample
"

mkdir -p "${BUILD_ROOT}"

rc=0
printf '%-16s %-8s %s\n' "TARGET" "BUILD" "RESULT"
printf '%-16s %-8s %s\n' "------" "-----" "------"

while IFS='|' read -r name app mode extra; do
[ -z "${name}" ] && continue
bdir="${BUILD_ROOT}/${name}"
if ! west build -p always -b "${BOARD}" -d "${bdir}" "${app}" \
-- ${EXTRA_MODULE_ARG} ${extra} \
> "${bdir}.build.log" 2>&1; then
printf '%-16s %-8s %s\n' "${name}" "FAIL" "(build failed; see ${bdir}.build.log)"
rc=1
continue
fi
# native_sim block-buffers piped stdout; stdbuf forces line buffering so the
# (kernel-idle-looping) sample still flushes before the timeout kill.
timeout 120 stdbuf -oL -eL "${bdir}/zephyr/zephyr.exe" > "${bdir}.run.log" 2>&1
if [ "${mode}" = ztest ]; then
if grep -q "PROJECT EXECUTION SUCCESSFUL" "${bdir}.run.log" && \
! grep -q "PROJECT EXECUTION FAILED" "${bdir}.run.log"; then
printf '%-16s %-8s %s\n' "${name}" "ok" "PASS"
else
printf '%-16s %-8s %s\n' "${name}" "ok" "FAIL (see ${bdir}.run.log)"
rc=1
fi
else
if grep -q "ALL PSA SMOKE TESTS PASSED" "${bdir}.run.log" || \
grep -q "Sample finished successfully." "${bdir}.run.log"; then
printf '%-16s %-8s %s\n' "${name}" "ok" "PASS"
else
printf '%-16s %-8s %s\n' "${name}" "ok" "FAIL (see ${bdir}.run.log)"
rc=1
fi
fi
done <<EOF
${TARGETS}
EOF

echo ""
[ "${rc}" -eq 0 ] && echo "ALL wolfPSA ZEPHYR TARGETS PASSED" || echo "SOME wolfPSA ZEPHYR TARGETS FAILED"
exit "${rc}"
148 changes: 148 additions & 0 deletions .github/scripts/zephyr-4.x/zephyr-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#!/bin/bash
#
# zephyr-test.sh - Build and run the wolfPSA Zephyr samples/tests in a Docker
# container, across the supported Zephyr 4.x range. Mirrors the wolfSSL module's
# .github/scripts/zephyr-4.x/zephyr-test.sh (same GHCR images / west-init flow),
# but sets up BOTH modules a wolfPSA build needs -- the wolfSSL module (which
# supplies the wolfCrypt core, including the native Zephyr threading that a
# thread-safe wolfPSA build depends on) and wolfPSA itself -- and then runs the
# shared test runner (.github/scripts/run-tests.sh) rather than one sample.
#
# Usage:
# ./zephyr-test.sh [options]
#
# Options:
# -r, --repo <url> wolfPSA git repo URL (the code under test)
# -b, --branch <ref> wolfPSA branch/revision (the code under test)
# -z, --zephyr <version> Zephyr version tag (>= v4.3.0)
# --wolfssl-repo <url> wolfSSL repo URL (default: wolfSSL/wolfssl)
# --wolfssl-ref <ref> wolfSSL revision (default: master; must carry the
# native Zephyr threading -- override until it lands)
# -h, --help Show this help
#
# Examples:
# ./zephyr-test.sh -z v4.3.0
# ./zephyr-test.sh -r https://github.com/me/wolfPSA -b my-fix -z v4.4.0

set -euo pipefail

# Defaults point at the upstream wolfSSL org so this script is ready for the
# eventual upstream PR. The workflow overrides --wolfssl-repo/--wolfssl-ref (and
# -r/-b) as needed; in particular the native Zephyr threading a thread-safe
# wolfPSA build needs is not yet in upstream wolfSSL master, so CI overrides the
# wolfSSL ref until it lands.
WOLFPSA_REPO="https://github.com/wolfSSL/wolfPSA"
WOLFPSA_BRANCH="master"
ZEPHYR_VERSION="v4.4.0"
WOLFSSL_REPO="https://github.com/wolfSSL/wolfssl"
WOLFSSL_REF="master"

GHCR="ghcr.io/zephyrproject-rtos/zephyr-build"

usage() { sed -n '3,/^$/s/^# \?//p' "$0"; exit 0; }

# wolfPSA needs Zephyr >= 4.3 (the PSA_CRYPTO_PROVIDER_CUSTOM hook). Pick the SDK
# image the way the wolfSSL 4.x driver does.
select_docker_image() {
local ver="${1#v}"
local major="${ver%%.*}"
local minor="${ver#*.}"
minor="${minor%%.*}"
if [[ "$major" -ge 4 && "$minor" -ge 4 ]]; then
echo "${GHCR}:v0.29.2" # Zephyr 4.4+ : SDK 1.x
elif [[ "$major" -ge 4 && "$minor" -ge 3 ]]; then
echo "${GHCR}:v0.28.8" # Zephyr 4.3 : SDK 0.17.x
else
echo "ERROR: wolfPSA requires Zephyr >= 4.3 (got v${ver})" >&2
exit 1
fi
}

while [[ $# -gt 0 ]]; do
case "$1" in
-r|--repo) WOLFPSA_REPO="$2"; shift 2 ;;
-b|--branch) WOLFPSA_BRANCH="$2"; shift 2 ;;
-z|--zephyr) ZEPHYR_VERSION="$2"; shift 2 ;;
--wolfssl-repo) WOLFSSL_REPO="$2"; shift 2 ;;
--wolfssl-ref) WOLFSSL_REF="$2"; shift 2 ;;
-h|--help) usage ;;
*) echo "Unknown option: $1"; usage ;;
esac
done

DOCKER_IMAGE=$(select_docker_image "$ZEPHYR_VERSION")
CONTAINER_NAME="wolfpsa-zephyr-${ZEPHYR_VERSION#v}-$$"

echo "==> wolfPSA repo/ref: ${WOLFPSA_REPO}@${WOLFPSA_BRANCH}"
echo "==> wolfSSL repo/ref: ${WOLFSSL_REPO}@${WOLFSSL_REF}"
echo "==> Zephyr version: ${ZEPHYR_VERSION}"
echo "==> Docker image: ${DOCKER_IMAGE}"

echo "==> Pulling Docker image..."
docker pull "${DOCKER_IMAGE}"

BUILD_SCRIPT=$(cat <<'INNER_SCRIPT'
#!/bin/bash
set -euo pipefail

ZEPHYR_VERSION="__ZEPHYR_VERSION__"
WOLFPSA_REPO="__WOLFPSA_REPO__"
WOLFPSA_BRANCH="__WOLFPSA_BRANCH__"
WOLFSSL_REPO="__WOLFSSL_REPO__"
WOLFSSL_REF="__WOLFSSL_REF__"

cd /workdir

echo "==> [container] west init (${ZEPHYR_VERSION})..."
west init --mr "${ZEPHYR_VERSION}" zephyrproject
cd zephyrproject/zephyr

# Inject the wolfSSL module (wolfCrypt core) and wolfPSA (self) into west.yml,
# the same sed approach the wolfSSL CI uses.
WSSL_BASE=$(echo "${WOLFSSL_REPO}" | sed 's|/[^/]*$||')
WPSA_BASE=$(echo "${WOLFPSA_REPO}" | sed 's|/[^/]*$||')
WSSL_REF=$(echo "${WOLFSSL_REF}" | sed 's/\//\\\//g')
WPSA_REF=$(echo "${WOLFPSA_BRANCH}" | sed 's/\//\\\//g')

sed -i "s|remotes:|remotes:\n - name: wolfssl\n url-base: ${WSSL_BASE}\n - name: wolfpsa\n url-base: ${WPSA_BASE}|" west.yml
sed -i "s|projects:|projects:\n - name: wolfssl\n path: modules/crypto/wolfssl\n remote: wolfssl\n revision: ${WSSL_REF}\n - name: wolfPSA\n path: modules/crypto/wolfPSA\n remote: wolfpsa\n revision: ${WPSA_REF}|" west.yml
echo "==> [container] west.yml module entries:"; grep -A2 -E "wolfssl|wolfpsa|wolfPSA" west.yml
cd ..

echo "==> [container] west update..."
export GIT_TERMINAL_PROMPT=0
west update -n -o=--depth=1
west zephyr-export

echo "==> [container] Python deps..."
sudo apt-get update -qq && sudo apt-get install -y -qq python3-venv >/dev/null 2>&1 || true
python3 -m venv .venv && source .venv/bin/activate
pip3 install west >/dev/null
pip3 install -r zephyr/scripts/requirements.txt >/dev/null

export ZEPHYR_BASE="/workdir/zephyrproject/zephyr"
if [[ -z "${ZEPHYR_SDK_INSTALL_DIR:-}" ]]; then
SDK_DIR=$(find /opt -maxdepth 2 -name "zephyr-sdk-*" -type d 2>/dev/null | head -1)
[[ -n "$SDK_DIR" ]] && export ZEPHYR_SDK_INSTALL_DIR="$SDK_DIR"
fi

# Both modules are in the manifest now, so the runner must NOT also add wolfPSA
# via EXTRA_ZEPHYR_MODULES.
echo "==> [container] Running the wolfPSA test suite..."
EXTRA_MODULE_ARG="" \
bash modules/crypto/wolfPSA/.github/scripts/run-tests.sh
INNER_SCRIPT
)

BUILD_SCRIPT="${BUILD_SCRIPT//__ZEPHYR_VERSION__/$ZEPHYR_VERSION}"
BUILD_SCRIPT="${BUILD_SCRIPT//__WOLFPSA_REPO__/$WOLFPSA_REPO}"
BUILD_SCRIPT="${BUILD_SCRIPT//__WOLFPSA_BRANCH__/$WOLFPSA_BRANCH}"
BUILD_SCRIPT="${BUILD_SCRIPT//__WOLFSSL_REPO__/$WOLFSSL_REPO}"
BUILD_SCRIPT="${BUILD_SCRIPT//__WOLFSSL_REF__/$WOLFSSL_REF}"

cleanup() { docker rm -f "${CONTAINER_NAME}" 2>/dev/null || true; }
trap cleanup EXIT
docker rm -f "${CONTAINER_NAME}" 2>/dev/null || true

echo "==> Starting container..."
docker run --name "${CONTAINER_NAME}" --rm "${DOCKER_IMAGE}" bash -c "${BUILD_SCRIPT}"
71 changes: 71 additions & 0 deletions .github/workflows/zephyr-4.x.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Zephyr 4.x tests

# Builds and runs the wolfPSA Zephyr samples/tests across the supported Zephyr
# range. Mirrors the wolfSSL module's zephyr-4.x workflow: a matrix over Zephyr
# versions driving a Docker-based test script (.github/scripts/zephyr-4.x/
# zephyr-test.sh), which sets up the wolfSSL + wolfPSA modules and runs the
# shared test runner (.github/scripts/run-tests.sh). wolfPSA requires Zephyr
# >= 4.3 (the PSA_CRYPTO_PROVIDER_CUSTOM hook), so the floor of the matrix is 4.3.

on:
push:
branches: [ 'release/**' ]
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
branches: [ '*' ]

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

jobs:
build:
name: ${{ matrix.zephyr-ref }}
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }}
runs-on: ubuntu-22.04
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
# 4.3 = the wolfPSA floor (upstream), 4.4 = current target.
zephyr-ref: [ v4.3.0, v4.4.0 ]
steps:
- name: Checkout wolfPSA CI driver
uses: actions/checkout@v5
with:
sparse-checkout: .github/scripts
fetch-depth: 1

- name: Free disk space
run: |
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc \
/opt/hostedtoolcache/CodeQL "$AGENT_TOOLSDIRECTORY" || true
docker system prune -af || true
df -h /

- name: Resolve wolfPSA repo and ref
id: src
run: |
# Test the PR merged into its base (matches the wolfSSL workflow), so
# fixes already on the target branch are included.
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
echo "repo=https://github.com/${{ github.repository }}" >> "$GITHUB_OUTPUT"
echo "ref=refs/pull/${{ github.event.pull_request.number }}/merge" >> "$GITHUB_OUTPUT"
else
echo "repo=https://github.com/${{ github.repository }}" >> "$GITHUB_OUTPUT"
echo "ref=${{ github.ref_name }}" >> "$GITHUB_OUTPUT"
fi

- name: Build and run wolfPSA samples/tests
working-directory: .github/scripts/zephyr-4.x
run: |
# The wolfCrypt native Zephyr threading a thread-safe wolfPSA build
# needs is not yet in upstream wolfSSL master (the script's default),
# so point the wolfSSL dependency at the PR owner's fork/branch that
# carries it. Drop this override once the change lands upstream.
bash ./zephyr-test.sh \
-r "${{ steps.src.outputs.repo }}" \
-b "${{ steps.src.outputs.ref }}" \
-z "${{ matrix.zephyr-ref }}" \
--wolfssl-repo "https://github.com/${{ github.repository_owner }}/wolfssl" \
--wolfssl-ref zephyr_fixes
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,10 @@ test/psa_lms_xmss_verify_test
test/psa_ascon_xchacha_test
test/psa_sp800_108_test
test/psa_14_misc_test

# Local-only editor/agent context (not part of the module)
CLAUDE.md

# Real-hardware board configs are kept local, not in-tree (CI is native_sim only)
zephyr/**/boards/nucleo_*.conf
zephyr/**/boards/nucleo_*.overlay
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,29 @@ wolfSSL master.
SLH-DSA key types are recognized but report NOT_SUPPORTED.
- New coverage tests: ML-DSA, ML-KEM/KEM API, XOF, AES-KW, signature
contexts, LMS/XMSS verify, Ascon/XChaCha, SP800-108 and 1.4 misc.
- `psa_purge_key()`: confirms a key exists and reports its status (wolfPSA
keeps no in-RAM cache of persistent key material, so there is nothing to
evict).
- Optional thread-safe key store: with `WOLFPSA_THREAD_SAFE` a single mutex
built on wolfCrypt's portable `wc_*Mutex` API (created in `psa_crypto_init()`)
guards the volatile-key list and id counter for concurrent PSA callers; a
no-op in single-threaded builds.

### Zephyr module

- Added wolfPSA as a Zephyr **PSA Crypto provider**: selected via
`CONFIG_PSA_CRYPTO_PROVIDER_CUSTOM` (Zephyr >= 4.3), it supplies the PSA Crypto
API in place of Mbed TLS while reusing the wolfCrypt core built by the wolfSSL
Zephyr module. Volatile and persistent keys, RNG, and the standard PSA surface
work with no Mbed TLS symbol in the image.
- wolfCrypt AES-256-GCM custom ITS transform
(`CONFIG_SECURE_STORAGE_ITS_TRANSFORM_IMPLEMENTATION_CUSTOM`) provides
encryption-at-rest for persistent keys, replacing Zephyr's Mbed-TLS-coupled
AEAD transform. `psa_get_key_attributes()` restores the key id; persistent
keys use the crypto-provider ITS namespace (isolated from application
`psa_its_*`/`psa_ps_*`).
- wolfPSA follows the user's wolfCrypt configuration and exposes exactly the
enabled, wolfPSA-implemented algorithms as the PSA API.


## v5.9.1
Expand Down
5 changes: 0 additions & 5 deletions src/psa_api_stub.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,6 @@ psa_status_t psa_pake_setup(psa_pake_operation_t *operation, psa_key_id_t passwo
return wolfPSA_StubNotSupported();
}

psa_status_t psa_purge_key(psa_key_id_t key) {
(void)key;
return wolfPSA_StubNotSupported();
}

/* --- Key attachment (hardware-bound keys) --- */

psa_status_t psa_attach_key(const psa_key_attributes_t *attributes,
Expand Down
Loading
Loading