diff --git a/.github/workflows/README.md b/.github/workflows/README.md index b199923d..49842623 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -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` | Downloads FIPS Ready bundles from wolfssl.com (via `scripts/fetch-fips-ready.sh`) and builds wolfSSL with `--enable-fips=ready` against each. Matrix resolved at run time from `_discover-versions.yml`'s `fips_ready_pr_array`: the back-compat anchors plus the newest bundle. `ready` is not a fixed FIPS version: v7 through bundle 5.9.1, v8 from 5.9.2 on. | | `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. | @@ -59,6 +59,11 @@ 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 +hosted at or above the 5.8.2 floor, restoring the versions the PR set +leaves out. + ### Running a nightly job on a PR (label toggles) `pr-osp-select.yml` lets you pull any nightly job into a PR on demand — @@ -69,7 +74,7 @@ per-app workflows stay untouched. | Label | Effect | |-------|--------| | `ci:` | 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. | `` is the job key in the table below (the workflow base name, e.g. @@ -96,8 +101,9 @@ gh workflow run pr-osp-select.yml --ref -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//*.patch` from [wolfssl/osp](https://github.com/wolfssl/osp), builds it against the replace-default wolfProvider `.deb` stack, and runs the project's @@ -176,6 +182,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 diff --git a/.github/workflows/_discover-versions.yml b/.github/workflows/_discover-versions.yml index 43845662..4ff13b5a 100644 --- a/.github/workflows/_discover-versions.yml +++ b/.github/workflows/_discover-versions.yml @@ -30,6 +30,21 @@ 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 }} + fips_ready_version_map: + description: 'JSON object mapping each FIPS Ready bundle version to the FIPS module (v5/v7/v8) that --enable-fips=ready actually resolves to for that bundle.' + value: ${{ jobs.discover.outputs.fips_ready_version_map }} jobs: discover: @@ -49,7 +64,17 @@ 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 }} + fips_ready_version_map: ${{ steps.resolve.outputs.fips_ready_version_map }} steps: + - name: Checkout wolfProvider + uses: actions/checkout@v4 + with: + fetch-depth: 1 + - name: Install ORAS (best-effort) run: | set -uo pipefail @@ -169,11 +194,61 @@ jobs: OSSL_ALL_JSON=$(printf '%s\n' "$OSSL_ALL" | jq -R . | jq -s -c .) OSSL_LATEST=$(echo "$OSSL_ALL" | tail -n 1) + # FIPS Ready: every bundle hosted on wolfssl.com at or above the floor. + # Floor 5.8.2: the oldest bundle wolfProvider is tested against. + FR_FLOOR="5.8.2" + FR_OK=true + # Bound the probe so a hung wolfssl.com cannot eat this shared job's + # timeout and take all ~40 callers down; a timeout degrades to FR_OK=false. + FR_ALL=$(timeout 240 ./scripts/fetch-fips-ready.sh --list --floor "$FR_FLOOR") || true + if [ -z "${FR_ALL:-}" ]; then + # ~40 workflows share this job for wolfssl/openssl outputs only, + # so a FIPS resolution failure must not fail the whole job. FR_OK + # lets the FIPS-specific callers fail loudly on their own instead + # of reporting green on a stale, near-empty fallback matrix. + echo "::warning::Could not list FIPS Ready bundles; falling back to $FR_FLOOR (FIPS callers will fail this run)" + 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) + + # `ready` does not name a fixed FIPS module; map each hosted bundle + # to the module it actually builds (from wolfSSL's configure.ac + # `ready|vN-ready` case, checked per tag). Extend the boundaries + # below when a new FIPS generation ships. + fips_module_for() { + local key + key=$(printf '%s' "$1" | awk -F. '{ printf "%05d.%05d.%05d", $1, $2, $3 }') + if [[ "$key" > "00005.00009.00001" ]]; then echo "v8" # > 5.9.1 + elif [[ "$key" > "00005.00007.00000" ]]; then echo "v7" # > 5.7.0 + else echo "v5" + fi + } + FR_MAP_JSON="{}" + while IFS= read -r ver; do + FR_MAP_JSON=$(jq -c --arg v "$ver" --arg f "$(fips_module_for "$ver")" \ + '. + {($v): $f}' <<< "$FR_MAP_JSON") + done <<< "$FR_ALL" + # 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 "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)" echo "OpenSSL upstream latest: $OSSL_LATEST" echo "OpenSSL upstream releases tracked: $(echo "$OSSL_ALL" | wc -l) tags" + 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" + echo "FIPS Ready version map: $FR_MAP_JSON" { echo "wolfssl_ref=$WOLFSSL_DEB_REF" @@ -186,4 +261,9 @@ jobs: echo "openssl_latest_ref=$OSSL_LATEST" echo "openssl_latest_ref_array=[\"$OSSL_LATEST\"]" echo "openssl_all_releases_array=$OSSL_ALL_JSON" + 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" + echo "fips_ready_version_map=$FR_MAP_JSON" } >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/fips-ready.yml b/.github/workflows/fips-ready.yml index 699fd4fa..0a267ffb 100644 --- a/.github/workflows/fips-ready.yml +++ b/.github/workflows/fips-ready.yml @@ -25,23 +25,53 @@ 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 - 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 }} (FIPS ${{ fromJson(needs.discover_versions.outputs.fips_ready_version_map)[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: @@ -65,21 +95,8 @@ jobs: - 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 + BUNDLE_DIR=$(./scripts/fetch-fips-ready.sh "${{ matrix.wolfssl_bundle_ref }}") + echo "FIPS_BUNDLE_PATH=$BUNDLE_DIR" >> $GITHUB_ENV echo "Found FIPS bundle directory at: $BUNDLE_DIR" - name: Build wolfProvider with FIPS Ready Bundle diff --git a/.github/workflows/nightly-fips-ready.yml b/.github/workflows/nightly-fips-ready.yml new file mode 100644 index 00000000..a7de97c2 --- /dev/null +++ b/.github/workflows/nightly-fips-ready.yml @@ -0,0 +1,117 @@ +name: Nightly FIPS Ready Bundle Tests + +# Sweeps EVERY hosted FIPS Ready bundle at or above the floor. PR-time +# fips-ready.yml runs the narrower fips_ready_pr_array (the back-compat +# anchors plus the newest) to keep PR turnaround short; this restores the +# versions it drops. Both sets are resolved at run time by +# _discover-versions.yml, so a new FIPS Ready release joins the sweep on its +# own. + +on: + workflow_call: {} + workflow_dispatch: {} + +# No concurrency group: github.workflow is the caller's here, so any group collides with sibling calls. + +jobs: + test_fetch_script: + 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 + uses: ./.github/workflows/_discover-versions.yml + + # See fips-ready.yml for why this can't be left to a silent floor fallback. + require_fips_resolved: + needs: discover_versions + 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] + name: Nightly FIPS Ready ${{ matrix.wolfssl_bundle_ref }} (FIPS ${{ fromJson(needs.discover_versions.outputs.fips_ready_version_map)[matrix.wolfssl_bundle_ref] }}, ${{ matrix.openssl_ref }}) + runs-on: ubuntu-22.04 + permissions: + contents: read + packages: write + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + wolfssl_bundle_ref: ${{ fromJson(needs.discover_versions.outputs.fips_ready_all_array) }} + openssl_ref: ${{ fromJson(needs.discover_versions.outputs.openssl_latest_ref_array) }} + steps: + - name: Checkout wolfProvider + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Cache build dependencies + id: deps + uses: ./.github/actions/oras-build-deps + with: + variant: fips-ready + openssl_ref: ${{ matrix.openssl_ref }} + wolfssl_fixed_key: wolfssl-fips-${{ matrix.wolfssl_bundle_ref }}-${{ matrix.openssl_ref }} + # The build below sets WOLFPROV_FIPS_FORCE_FAIL, which adds + # -DHAVE_FORCE_FIPS_FAILURE to the wolfSSL build; key the cached + # install on it so a wolfSSL built without the define is never reused. + extra_key: forcefail + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Download FIPS Ready Bundle + run: | + BUNDLE_DIR=$(./scripts/fetch-fips-ready.sh "${{ matrix.wolfssl_bundle_ref }}") + echo "FIPS_BUNDLE_PATH=$BUNDLE_DIR" >> $GITHUB_ENV + echo "Found FIPS bundle directory at: $BUNDLE_DIR" + + - name: Build wolfProvider with FIPS Ready Bundle + run: | + # WOLFPROV_FIPS_FORCE_FAIL is an internal, test-only knob (no CLI + # flag) that enables HAVE_FORCE_FIPS_FAILURE for the fips_status test. + OPENSSL_TAG=${{ matrix.openssl_ref }} \ + WOLFPROV_FIPS_FORCE_FAIL=1 \ + ./scripts/build-wolfprovider.sh --fips-bundle="$FIPS_BUNDLE_PATH" \ + --fips-check=ready --wolfssl-ver=v${{matrix.wolfssl_bundle_ref}}-stable + + - name: Push build dependencies + uses: ./.github/actions/oras-build-deps-push + with: + registry: ${{ steps.deps.outputs.registry }} + openssl_install_tag: ${{ steps.deps.outputs.openssl_install_tag }} + wolfssl_install_tag: ${{ steps.deps.outputs.wolfssl_install_tag }} + openssl_hit: ${{ steps.deps.outputs.openssl_hit }} + wolfssl_hit: ${{ steps.deps.outputs.wolfssl_hit }} + + - name: Run FIPS Command Tests + run: | + export WOLFSSL_ISFIPS=1 + source scripts/env-setup + + # --- normal mode --- + ./scripts/cmd_test/do-cmd-tests.sh + + # --- force-fail mode --- + WOLFPROV_FORCE_FAIL=1 ./scripts/cmd_test/do-cmd-tests.sh + + - name: Run FIPS Status Test + run: | + export WOLFSSL_ISFIPS=1 + source scripts/env-setup + ./test/standalone/tests/fips_status/run.sh diff --git a/.github/workflows/nightly-osp.yml b/.github/workflows/nightly-osp.yml index 32a423b9..788f485a 100644 --- a/.github/workflows/nightly-osp.yml +++ b/.github/workflows/nightly-osp.yml @@ -4,8 +4,8 @@ name: Nightly OSP Suite # wolfssl stable). Stays dynamic across version bumps. # Wave 2 = v5.8.4-stable pinned (back-compat line), gated on wave1-done # with if: always() so a single Wave 1 failure doesn't skip the -# older-line coverage. multi-compiler and static-analysis run once -# outside the waves. +# older-line coverage. multi-compiler, fips-ready and static-analysis run +# once outside the waves. # nginx-pqc is Wave 1 only: PQC has a v5.9.2-stable floor, so the # v5.8.4-stable Wave 2 line has no eligible ref to run. @@ -169,3 +169,4 @@ jobs: # === Out-of-wave: not wolfssl-version-split === static-analysis: { uses: ./.github/workflows/static-analysis.yml } multi-compiler: { uses: ./.github/workflows/nightly-multi-compiler.yml } + fips-ready: { uses: ./.github/workflows/nightly-fips-ready.yml } diff --git a/.github/workflows/perf-regression.yml b/.github/workflows/perf-regression.yml index 2f44c0e2..e9755919 100644 --- a/.github/workflows/perf-regression.yml +++ b/.github/workflows/perf-regression.yml @@ -47,15 +47,32 @@ jobs: path: perf_outputs/results.json retention-days: 7 - perf_fips: + # Without this gate, a discovery failure (falling back to the 5.8.2 floor) + # would let perf_fips silently benchmark a stale bundle labeled "latest". + # See fips-ready.yml for the fuller rationale. + require_fips_resolved: needs: discover_versions - name: Overhead regression (FIPS) + 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 benchmark a stale bundle under the 'latest' label." + exit 1 + + perf_fips: + needs: [discover_versions, require_fips_resolved] + name: Overhead regression (FIPS ${{ fromJson(needs.discover_versions.outputs.fips_ready_version_map)[needs.discover_versions.outputs.fips_ready_latest] }}) runs-on: ubuntu-22.04 timeout-minutes: 30 strategy: fail-fast: false matrix: - wolfssl_bundle_ref: [ '5.8.2' ] + # Newest bundle only: perf-baseline.fips.json is a single calibrated + # baseline, so fanning it across versions makes the numbers meaningless. + wolfssl_bundle_ref: [ '${{ needs.discover_versions.outputs.fips_ready_latest }}' ] openssl_ref: ${{ fromJson(needs.discover_versions.outputs.openssl_latest_ref_array) }} steps: - name: Checkout wolfProvider @@ -65,16 +82,8 @@ jobs: - name: Download FIPS Ready Bundle run: | - 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 - 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 + BUNDLE_DIR=$(./scripts/fetch-fips-ready.sh "${{ matrix.wolfssl_bundle_ref }}") + echo "FIPS_BUNDLE_PATH=$BUNDLE_DIR" >> $GITHUB_ENV - name: Build wolfProvider with FIPS Ready Bundle run: | diff --git a/.github/workflows/pr-osp-select.yml b/.github/workflows/pr-osp-select.yml index c98eb7c8..de75d9eb 100644 --- a/.github/workflows/pr-osp-select.yml +++ b/.github/workflows/pr-osp-select.yml @@ -223,6 +223,10 @@ jobs: needs: select if: contains(needs.select.outputs.run, ' all ') || contains(needs.select.outputs.run, ' multi-compiler ') uses: ./.github/workflows/nightly-multi-compiler.yml + fips-ready: + needs: select + if: contains(needs.select.outputs.run, ' all ') || contains(needs.select.outputs.run, ' fips-ready ') + uses: ./.github/workflows/nightly-fips-ready.yml perf: needs: select if: contains(needs.select.outputs.run, ' all ') || contains(needs.select.outputs.run, ' perf ') diff --git a/docs/FIPS_INTEGRATION_GUIDE.md b/docs/FIPS_INTEGRATION_GUIDE.md index cb2c3586..734cdff4 100644 --- a/docs/FIPS_INTEGRATION_GUIDE.md +++ b/docs/FIPS_INTEGRATION_GUIDE.md @@ -142,6 +142,35 @@ The `--fips-check` option tells the build system which FIPS bundle type you have | `wolfssl-5.8.4-commercial-fips-linuxv5.7z` | `linuxv5` | | `wolfssl-5.8.4-commercial-fips-linuxv5.2.1.7z` | `linuxv5.2.1` | | `wolfssl-5.8.4-commercial-fips-v6.0.0.7z` | `v6.0.0` | +| `wolfssl-5.9.2-commercial-fips-v7.0.0.7z` | `v7.0.0` | + +### What `ready` Resolves To + +`ready` is not a fixed FIPS version. It tracks one release ahead of the newest +certificate, so the module version it selects moves with the bundle: + +| FIPS Ready bundle | `--enable-fips=ready` gives | +|-------------------|-----------------------------| +| 5.6.6 to 5.7.0 | `HAVE_FIPS_VERSION=5` | +| 5.7.2 to 5.9.1 | `HAVE_FIPS_VERSION=7` | +| 5.9.2 and newer | `HAVE_FIPS_VERSION=8` | + +When you need to pin a specific generation rather than tracking `ready`, use +the alias that the bundle's own `configure.ac` accepts; these differ per +release, so check the bundle you have. As of this writing: 5.8.2 accepts +`ready` and `v6-ready`; 5.9.1 accepts `ready` only; 5.9.2 accepts `ready` and +`v7-ready`. The `v5-ready` arm remains a separate, valid option across these +releases. + +The FIPS Ready bundles are published as +`https://www.wolfssl.com/wolfssl--gplv3-fips-ready.zip`. +`scripts/fetch-fips-ready.sh` resolves, downloads and verifies them: + +```bash +./scripts/fetch-fips-ready.sh --list # every hosted bundle >= 5.8.2 +./scripts/fetch-fips-ready.sh --latest # newest hosted bundle +./scripts/fetch-fips-ready.sh 5.9.2 # download + extract, prints the path +``` --- diff --git a/include/wolfprovider/settings.h b/include/wolfprovider/settings.h index 438d5812..cbf8d156 100644 --- a/include/wolfprovider/settings.h +++ b/include/wolfprovider/settings.h @@ -70,10 +70,18 @@ #ifdef WOLFSSL_SHA512 #define WP_HAVE_SHA512 #endif -#if (LIBWOLFSSL_VERSION_HEX >= 0x05000000) && !defined(WOLFSSL_NOSHA512_224) +/* SHA-512/224 and /256 are in-boundary from FIPS v7 (the [v7|ready|dev] arm); + * earlier modules set WOLFSSL_NOSHA512_* and lack wc_HashInit_ex support. */ +#if defined(WP_HAVE_SHA512) && (LIBWOLFSSL_VERSION_HEX >= 0x05000000) && \ + !defined(WOLFSSL_NOSHA512_224) && !defined(HAVE_SELFTEST) && \ + !defined(SELF_TEST) && (!defined(HAVE_FIPS) || \ + (defined(HAVE_FIPS_VERSION_MAJOR) && HAVE_FIPS_VERSION_MAJOR >= 7)) #define WP_HAVE_SHA512_224 #endif -#if (LIBWOLFSSL_VERSION_HEX >= 0x05000000) && !defined(WOLFSSL_NOSHA512_256) +#if defined(WP_HAVE_SHA512) && (LIBWOLFSSL_VERSION_HEX >= 0x05000000) && \ + !defined(WOLFSSL_NOSHA512_256) && !defined(HAVE_SELFTEST) && \ + !defined(SELF_TEST) && (!defined(HAVE_FIPS) || \ + (defined(HAVE_FIPS_VERSION_MAJOR) && HAVE_FIPS_VERSION_MAJOR >= 7)) #define WP_HAVE_SHA512_256 #endif #ifdef WOLFSSL_SHA3 diff --git a/scripts/build-wolfprovider.sh b/scripts/build-wolfprovider.sh index 41236bc9..15955430 100755 --- a/scripts/build-wolfprovider.sh +++ b/scripts/build-wolfprovider.sh @@ -17,7 +17,8 @@ show_help() { echo " --wolfssl-ver=VER Which version of wolfSSL to clone" echo " --enable-fips Build wolfProvider with a cloned FIPS bundle. Use with --fips-check to specify tag (default: v5.2.4)" echo " --fips-bundle=DIR Build wolfProvider with a directory containing a wolfSSL FIPS bundle. Use with --fips-check to specify tag" - echo " --fips-check=TAG Choose a FIPS tag (v5.2.1, v5.2.4, linuxv5.2.1, v6.0.0, ready, etc). Automatically determines configure option" + echo " --fips-check=TAG Choose a FIPS tag (v5.2.1, v5.2.4, linuxv5.2.1, v6.0.0, v7.0.0, ready, etc). Automatically determines configure option" + echo " 'ready' follows the bundle: FIPS v7 up to bundle 5.9.1, v8 from 5.9.2 on. Fetch bundles with scripts/fetch-fips-ready.sh" echo " --debian Build a Debian package" echo " --debian --enable-fips Build a Debian package with FIPS support" echo " --quicktest Disable some tests for a faster testing suite" diff --git a/scripts/fetch-fips-ready.sh b/scripts/fetch-fips-ready.sh new file mode 100755 index 00000000..5fe8dad0 --- /dev/null +++ b/scripts/fetch-fips-ready.sh @@ -0,0 +1,318 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Resolves and downloads wolfSSL FIPS Ready bundles from wolfssl.com. +# Usage: ./fetch-fips-ready.sh --list [--floor X.Y.Z] +# ./fetch-fips-ready.sh --latest [--floor X.Y.Z] +# ./fetch-fips-ready.sh [--dest DIR] +# Example: ./fetch-fips-ready.sh 5.9.2 --dest /tmp + +# 5.8.2 is the oldest bundle wolfProvider is tested against. +FLOOR="5.8.2" +DEST="$PWD" +MODE="" +VERSION="" + +DOWNLOAD_PAGE="https://www.wolfssl.com/download/" +WOLFSSL_GIT="https://github.com/wolfSSL/wolfssl.git" + +usage() { + echo "Usage: $0 --list|--latest [--floor X.Y.Z]" >&2 + echo " $0 [--dest DIR]" >&2 + exit 1 +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --list|--latest) + MODE="${1#--}" + shift + ;; + --floor) + [[ $# -ge 2 ]] || usage + FLOOR="$2" + shift 2 + ;; + --floor=*) + FLOOR="${1#--floor=}" + shift + ;; + --dest) + [[ $# -ge 2 ]] || usage + DEST="$2" + shift 2 + ;; + --dest=*) + DEST="${1#--dest=}" + shift + ;; + -h|--help) + usage + ;; + -*) + echo "fetch-fips-ready: unknown option $1" >&2 + usage + ;; + *) + [[ -z "$VERSION" ]] || usage + VERSION="$1" + MODE="${MODE:-fetch}" + shift + ;; + esac +done + +[[ -n "$MODE" ]] || usage +[[ "$MODE" != "fetch" || -n "$VERSION" ]] || usage + +bundle_url() { + echo "https://www.wolfssl.com/wolfssl-$1-gplv3-fips-ready.zip" +} + +retry_out() { + local out attempt + for attempt in 1 2 3; do + if out=$("$@" 2>/dev/null) && [[ -n "$out" ]]; then + printf '%s' "$out" + return 0 + fi + if [[ "$attempt" -lt 3 ]]; then + echo "fetch-fips-ready: retry $attempt/3: '$*' failed; retrying..." >&2 + sleep $((attempt * 5)) + fi + done + return 1 +} + +# Sorts newline-separated X.Y.Z version strings ascending without `sort -V`, +# a GNU extension this script (used on macOS too) can't rely on. Zero-pads +# each numeric field so plain lexicographic sort orders them correctly. +ver_sort() { + awk -F. '{ printf "%05d.%05d.%05d %s\n", $1, $2, $3, $0 }' \ + | sort \ + | awk '{ print $2 }' +} + +# ver_ge A B: true when A >= B +ver_ge() { + [[ "$(printf '%s\n%s\n' "$2" "$1" | ver_sort | head -n1)" == "$2" ]] +} + +get_page() { + curl -fsSL "$DOWNLOAD_PAGE" +} + +# The download page advertises only the newest bundle. +page_latest() { + get_page \ + | grep -o 'wolfssl-[0-9][0-9.]*-gplv3-fips-ready\.zip' \ + | sed -E 's/^wolfssl-(.*)-gplv3-fips-ready\.zip$/\1/' \ + | ver_sort | tail -n 1 +} + +page_sha256() { + get_page | tr -d '\n' \ + | grep -o "wolfssl-$1-gplv3-fips-ready\.zip[^(]*(SHA256: *[0-9a-f]\{64\}" \ + | head -n 1 | grep -o '[0-9a-f]\{64\}' +} + +ls_wolfssl_stable() { + git ls-remote --tags --refs "$WOLFSSL_GIT" 'v*-stable' \ + | awk -F/ '{print $NF}' \ + | sed -E 's/^v(.*)-stable$/\1/' \ + | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' +} + +# Retries transient failures (timeout, 429, 5xx, connection error); only a +# confirmed 404/410 means "not hosted" and skips retry. Returns 0 exists, +# 1 confirmed absent, 2 indeterminate. Callers must not treat 1 and 2 alike. +bundle_exists() { + local ver="$1" code attempt + for attempt in 1 2 3; do + # A genuine connection failure (not just a non-200 status) makes + # curl itself exit nonzero; `|| code=""` keeps that from tripping + # `set -e` before the retry loop below gets to run. + code=$(curl -sS -L -I -o /dev/null -w '%{http_code}' --max-time 30 \ + "$(bundle_url "$ver")" 2>/dev/null) || code="" + case "$code" in + 200) return 0 ;; + 404|410) return 1 ;; + *) + if [[ "$attempt" -lt 3 ]]; then + sleep $((attempt * 3)) + fi + ;; + esac + done + echo "fetch-fips-ready: could not confirm bundle $ver after retries" \ + "(last status: ${code:-none})" >&2 + return 2 +} + +# Candidates are the union of upstream -stable tags and the advertised latest, +# because neither set alone matches what is hosted: 5.8.6 has a tag but no +# bundle, and 5.9.1 has a bundle the page never lists. +list_versions() { + local candidates ver found probe_dir + local -a indeterminate=() + candidates=$(retry_out ls_wolfssl_stable) || { + echo "fetch-fips-ready: could not list wolfSSL tags" >&2 + return 1 + } + candidates="$candidates +$(page_latest 2>/dev/null || true)" + candidates=$(printf '%s\n' "$candidates" | grep -E '^[0-9.]+$' | sort -u \ + | ver_sort \ + | while read -r ver; do ver_ge "$ver" "$FLOOR" && echo "$ver"; done) \ + || true + [[ -n "$candidates" ]] || return 1 + + # Runs inside the shared _discover-versions.yml job that every caller + # pays for even without touching FIPS outputs, so probe candidates in + # parallel: N versions costs one round trip, not N. + probe_dir=$(mktemp -d) + trap 'rm -rf "$probe_dir"' RETURN + for ver in $candidates; do + # Cap concurrent probes so a growing candidate set does not hit + # wolfssl.com with one big HEAD burst, the most likely 429 trigger. + while [[ "$(jobs -rp | wc -l)" -ge 6 ]]; do + sleep 0.2 + done + ( + # `|| rc=$?` is load-bearing under `set -e`: an unguarded call + # returning 2 kills this subshell before the case below runs, + # silently losing the indeterminate marker. + rc=0 + bundle_exists "$ver" || rc=$? + case "$rc" in + 0) touch "$probe_dir/$ver.exists" ;; + 2) touch "$probe_dir/$ver.indeterminate" ;; + # 1 (confirmed absent): no marker, correctly omitted below. + esac + ) & + done + wait + + # An indeterminate probe must fail the whole resolution, not just drop + # that candidate: otherwise a flaky bundle vanishes silently from a + # list that still reports success. + while IFS= read -r ver; do + indeterminate+=("$ver") + done < <(find "$probe_dir" -maxdepth 1 -name '*.indeterminate' \ + -exec basename {} .indeterminate \;) + if [[ "${#indeterminate[@]}" -gt 0 ]]; then + echo "fetch-fips-ready: could not determine availability for:" \ + "${indeterminate[*]}" >&2 + return 1 + fi + + found="" + for ver in $(printf '%s\n' "$candidates" | ver_sort); do + [[ -e "$probe_dir/$ver.exists" ]] && found="$found$ver"$'\n' + done + + [[ -n "$found" ]] || return 1 + printf '%s' "$found" +} + +sha256_of() { + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$1" | awk '{print $1}' + else + shasum -a 256 "$1" | awk '{print $1}' + fi +} + +fetch_bundle() { + local ver="$1" zip dir want got attempt scratch scratch_dir latest page + + mkdir -p "$DEST" + # Absolutize so the RETURN trap and the printed path stay valid even if a + # later cd changes the shell's cwd (a relative --dest would otherwise leak + # the scratch dir on cleanup). + DEST=$(cd "$DEST" && pwd) + # Download and verify into a scratch dir first; the caller's existing + # $dir is only touched after a verified download, so a failed fetch + # never destroys a bundle that was already there. + scratch=$(mktemp -d "$DEST/.fetch-fips-ready.XXXXXX") + trap 'rm -rf "$scratch"' RETURN + zip="$scratch/wolfssl-$ver-gplv3-fips-ready.zip" + dir="$DEST/wolfssl-$ver-gplv3-fips-ready" + + for attempt in 1 2 3; do + if curl -fsSL --max-time 900 -o "$zip" "$(bundle_url "$ver")"; then + break + fi + if [[ "$attempt" -eq 3 ]]; then + echo "fetch-fips-ready: could not download bundle $ver" >&2 + return 1 + fi + echo "fetch-fips-ready: download attempt $attempt failed; retrying..." >&2 + sleep $((attempt * 5)) + done + + # Fetch the download page once, with retry, so verification can never fall + # open on a transient page failure. An unreachable page refuses every + # bundle rather than downgrading to the CRC-only path. + if ! page=$(retry_out get_page); then + echo "fetch-fips-ready: could not reach download page to verify $ver;" \ + "refusing unverified download" >&2 + return 1 + fi + want=$(printf '%s' "$page" | tr -d '\n' \ + | grep -o "wolfssl-$ver-gplv3-fips-ready\.zip[^(]*(SHA256: *[0-9a-f]\{64\}" \ + | head -n 1 | grep -o '[0-9a-f]\{64\}' || true) + latest=$(printf '%s' "$page" \ + | grep -o 'wolfssl-[0-9][0-9.]*-gplv3-fips-ready\.zip' \ + | sed -E 's/^wolfssl-(.*)-gplv3-fips-ready\.zip$/\1/' \ + | ver_sort | tail -n 1 || true) + if [[ -n "$want" ]]; then + got=$(sha256_of "$zip") + if [[ "$got" != "$want" ]]; then + echo "fetch-fips-ready: SHA256 mismatch for $ver (want $want, got $got)" >&2 + return 1 + fi + elif [[ "$ver" == "$latest" ]]; then + echo "fetch-fips-ready: no published SHA256 for newest bundle $ver;" \ + "refusing unverified download" >&2 + return 1 + else + # Older bundles publish no hash; verify the archive itself instead. + echo "fetch-fips-ready: no published SHA256 for $ver, verifying archive" >&2 + if ! unzip -tqq "$zip" >/dev/null; then + echo "fetch-fips-ready: downloaded archive for $ver failed integrity check" >&2 + return 1 + fi + fi + + unzip -q "$zip" -d "$scratch" + rm -f "$zip" + + if [[ ! -d "$scratch/wolfssl-$ver-gplv3-fips-ready" ]]; then + scratch_dir=$(find "$scratch" -maxdepth 1 -mindepth 1 -type d -name '*fips-ready*' | head -n 1) + else + scratch_dir="$scratch/wolfssl-$ver-gplv3-fips-ready" + fi + if [[ -z "$scratch_dir" || ! -d "$scratch_dir" ]]; then + echo "fetch-fips-ready: no bundle directory after extracting $ver" >&2 + return 1 + fi + + # Verified: safe to replace whatever was at $dir. + rm -rf "$dir" + mv "$scratch_dir" "$dir" + + echo "$dir" +} + +case "$MODE" in + list) + list_versions + ;; + latest) + list_versions | tail -n 1 + ;; + fetch) + fetch_bundle "$VERSION" + ;; +esac diff --git a/scripts/include.am b/scripts/include.am index f192d304..566e62eb 100644 --- a/scripts/include.am +++ b/scripts/include.am @@ -1,5 +1,7 @@ dist_noinst_SCRIPTS += scripts/build-wolfprovider.sh dist_noinst_SCRIPTS += scripts/env-setup +dist_noinst_SCRIPTS += scripts/fetch-fips-ready.sh +dist_noinst_SCRIPTS += scripts/test-fetch-fips-ready.sh dist_noinst_SCRIPTS += scripts/patch-libcrypto-exports.sh dist_noinst_SCRIPTS += scripts/patch-openssl-fips.sh dist_noinst_SCRIPTS += scripts/resolve-ref.sh diff --git a/scripts/test-fetch-fips-ready.sh b/scripts/test-fetch-fips-ready.sh new file mode 100755 index 00000000..942bc679 --- /dev/null +++ b/scripts/test-fetch-fips-ready.sh @@ -0,0 +1,607 @@ +#!/bin/bash +# +# Copyright (C) 2006-2026 wolfSSL Inc. +# +# This file is part of wolfProvider. +# +# wolfProvider is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# wolfProvider is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with wolfProvider. If not, see . +# +# Isolated, network-free tests for scripts/fetch-fips-ready.sh: mocks curl +# and git so the resolver and extraction logic, especially its destructive +# and failure paths, run deterministically in CI without hitting +# wolfssl.com or GitHub. +set -euo pipefail + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +TARGET="${SCRIPT_DIR}/fetch-fips-ready.sh" +WORK=$(mktemp -d) +MOCKBIN="${WORK}/bin" +FNS="${WORK}/fns.sh" +PASS=0 +FAIL=0 + +cleanup() { + rm -rf "${WORK}" +} +trap cleanup EXIT + +mkdir -p "${MOCKBIN}" + +# Source only fetch-fips-ready.sh's function definitions, not its argv +# parser or the case-dispatch at the bottom (both would run immediately, +# and call exit, if sourced as-is). Boundaries are located by marker +# pattern, not hardcoded line numbers, so this stays in sync as the +# script grows. +# shellcheck disable=SC2016 # single-quoted on purpose: matching a literal +# '$' in fetch-fips-ready.sh's source, not expanding a variable here. +while_line=$(grep -n '^while \[\[ \$# -gt 0' "${TARGET}" | head -n1 | cut -d: -f1) +bundle_url_line=$(grep -n '^bundle_url()' "${TARGET}" | head -n1 | cut -d: -f1) +# shellcheck disable=SC2016 +case_line=$(grep -n '^case "\$MODE"' "${TARGET}" | head -n1 | cut -d: -f1) +if [[ -z "${while_line}" || -z "${bundle_url_line}" || -z "${case_line}" ]]; then + echo "test-fetch-fips-ready: could not locate expected markers in" \ + "${TARGET}; has its structure changed?" >&2 + exit 1 +fi +{ + sed -n "1,$((while_line - 1))p" "${TARGET}" + sed -n "${bundle_url_line},$((case_line - 1))p" "${TARGET}" +} > "${FNS}" + +pass() { + PASS=$((PASS + 1)) + echo "PASS: $1" +} + +fail() { + FAIL=$((FAIL + 1)) + echo "FAIL: $1" +} + +# Runs $2.. in a subshell with the mock bin dir first on PATH and the +# resolver functions sourced, so each test gets an isolated MOCKBIN/DEST +# without polluting other tests' mock scripts or fixtures. +run_case() { + local dest="$1" + shift + # DEST must be set AFTER sourcing: the sourced preamble itself sets + # DEST="$PWD", which would otherwise clobber a prefix assignment made + # before the source builtin runs. DEST is consumed inside fns.sh, not + # this file, hence the two shellcheck disables below. + # SC2030: the PATH override is deliberately scoped to this subshell, + # so each call gets an isolated mock PATH without polluting later tests. + # shellcheck disable=SC1090,SC2030,SC2034 + ( PATH="${MOCKBIN}:${PATH}"; source "${FNS}"; DEST="${dest}"; "$@" ) +} + +# bundle_exists: retries a transient failure (connection error, no HTTP +# response) and succeeds once the server recovers. +test_bundle_exists_retries_transient() { + local d counter + d=$(mktemp -d) + counter="${d}/count" + cat > "${MOCKBIN}/curl" < "${counter}" +if [[ "\$*" == *"-I"* && "\$*" == *"%{http_code}"* ]]; then + if [[ "\$n" -lt 3 ]]; then exit 7; fi + printf '200' + exit 0 +fi +exit 1 +EOF + chmod +x "${MOCKBIN}/curl" + + if run_case "${d}" bundle_exists "9.9.9"; then + if [[ "$(cat "${counter}")" == "3" ]]; then + pass "bundle_exists retries transient failures and succeeds" + else + fail "bundle_exists succeeded but call count was $(cat "${counter}"), want 3" + fi + else + fail "bundle_exists did not recover from a transient failure" + fi + rm -rf "${d}" +} + +# bundle_exists: a confirmed 4xx is definitive, no retry burned. +test_bundle_exists_confirmed_404_no_retry() { + local d counter rc + d=$(mktemp -d) + counter="${d}/count" + cat > "${MOCKBIN}/curl" < "${counter}" +if [[ "\$*" == *"-I"* && "\$*" == *"%{http_code}"* ]]; then + printf '404' + exit 0 +fi +exit 1 +EOF + chmod +x "${MOCKBIN}/curl" + + rc=0 + run_case "${d}" bundle_exists "9.9.9" || rc=$? + if [[ "${rc}" -ne 1 ]]; then + fail "bundle_exists returned ${rc} for a confirmed 404, want 1 (absent)" + elif [[ "$(cat "${counter}")" == "1" ]]; then + pass "bundle_exists treats a confirmed 404 as final, no retry" + else + fail "bundle_exists retried after a confirmed 404 ($(cat "${counter}") calls)" + fi + rm -rf "${d}" +} + +# bundle_exists: a 429 (rate limit) is transient, not a confirmed absence. +# A prior review round flagged classifying all 4xx as permanently absent. +test_bundle_exists_429_is_transient() { + local d counter rc + d=$(mktemp -d) + counter="${d}/count" + cat > "${MOCKBIN}/curl" < "${counter}" +if [[ "\$*" == *"-I"* && "\$*" == *"%{http_code}"* ]]; then + if [[ "\$n" -lt 2 ]]; then printf '429'; exit 0; fi + printf '200' + exit 0 +fi +exit 1 +EOF + chmod +x "${MOCKBIN}/curl" + + rc=0 + run_case "${d}" bundle_exists "9.9.9" || rc=$? + if [[ "${rc}" -eq 0 ]]; then + pass "bundle_exists retries a 429 instead of treating it as absent" + else + fail "bundle_exists returned ${rc} for a 429-then-200 sequence, want 0 (exists)" + fi + rm -rf "${d}" +} + +# bundle_exists: exhausts retries and reports indeterminate (rc=2), not +# confirmed-absent (rc=1), when every attempt is transient. +test_bundle_exists_exhausts_on_persistent_transient() { + local d rc + d=$(mktemp -d) + cat > "${MOCKBIN}/curl" <<'EOF' +#!/usr/bin/env bash +if [[ "$*" == *"-I"* && "$*" == *"%{http_code}"* ]]; then + exit 7 +fi +exit 1 +EOF + chmod +x "${MOCKBIN}/curl" + + rc=0 + run_case "${d}" bundle_exists "9.9.9" 2>/dev/null || rc=$? + if [[ "${rc}" -eq 2 ]]; then + pass "bundle_exists reports indeterminate (rc=2), distinct from confirmed absent" + else + fail "bundle_exists returned ${rc} for persistent transient failure, want 2" + fi + rm -rf "${d}" +} + +# list_versions: unions git tags with the page's advertised latest, +# applies the floor, and only keeps versions that probe as hosted. +test_list_versions_filters_and_sorts() { + local d html + d=$(mktemp -d) + html="${d}/page.html" + cat > "${html}" <<'EOF' + (SHA256: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)
+EOF + cat > "${MOCKBIN}/git" <<'EOF' +#!/usr/bin/env bash +if [[ "$1" == "ls-remote" ]]; then + cat < "${MOCKBIN}/curl" < "${MOCKBIN}/git" <<'EOF' +#!/usr/bin/env bash +exit 1 +EOF + chmod +x "${MOCKBIN}/git" + cat > "${MOCKBIN}/curl" <<'EOF' +#!/usr/bin/env bash +exit 1 +EOF + chmod +x "${MOCKBIN}/curl" + + if run_case "${d}" list_versions 2>/dev/null; then + fail "list_versions succeeded despite git ls-remote failing entirely" + else + pass "list_versions fails cleanly when upstream tags are unavailable" + fi + rm -rf "${d}" +} + +# list_versions: one candidate hosted, one persistently indeterminate +# (5xx/timeout throughout). The whole resolution must fail rather than +# publishing a list that quietly omits the flaky candidate. +test_list_versions_fails_on_indeterminate_candidate() { + local d rc + d=$(mktemp -d) + cat > "${MOCKBIN}/git" <<'EOF' +#!/usr/bin/env bash +if [[ "$1" == "ls-remote" ]]; then + cat < "${MOCKBIN}/curl" <<'EOF' +#!/usr/bin/env bash +if [[ "$*" == *"-I"* && "$*" == *"%{http_code}"* ]]; then + for a in "$@"; do + # 5.8.2 is persistently flaky (503 forever); 5.9.2 is healthy. + if [[ "$a" == *"5.8.2"* ]]; then printf '503'; exit 0; fi + done + printf '200' + exit 0 +fi +exit 1 +EOF + chmod +x "${MOCKBIN}/curl" + + rc=0 + run_case "${d}" list_versions 2>/dev/null || rc=$? + if [[ "${rc}" -ne 0 ]]; then + pass "list_versions fails when a real candidate is indeterminate" + else + fail "list_versions succeeded despite an indeterminate candidate (5.8.2 always 503)" + fi + rm -rf "${d}" +} + +# Entrypoint-level regression for the indeterminate-candidate case. Bash +# disables errexit for the whole call tree beneath a command on the left +# of ||/&&/if, so every other test's `run_case ... || rc=$?` masked a real +# errexit bug that only showed up running the real binary unconditioned. +# This runs `bash "$TARGET" --list` directly to match production CI. +test_entrypoint_list_fails_on_indeterminate_candidate() { + local d out status + d=$(mktemp -d) + cat > "${MOCKBIN}/git" <<'EOF' +#!/usr/bin/env bash +if [[ "$1" == "ls-remote" ]]; then + cat < "${MOCKBIN}/curl" <<'EOF' +#!/usr/bin/env bash +if [[ "$*" == *"-I"* && "$*" == *"%{http_code}"* ]]; then + for a in "$@"; do + # 5.8.2 is persistently flaky (503 forever); 5.9.2 is healthy. + if [[ "$a" == *"5.8.2"* ]]; then printf '503'; exit 0; fi + done + printf '200' + exit 0 +fi +exit 1 +EOF + chmod +x "${MOCKBIN}/curl" + + status=0 + # SC2031: the PATH override only needs to reach the `bash "$TARGET"` + # call on this line; it isn't meant to escape the subshell. + # shellcheck disable=SC2031 + out=$(cd "${d}" && PATH="${MOCKBIN}:${PATH}" bash "${TARGET}" --list \ + --floor 5.8.2 2>&1) || status=$? + + if [[ "${status}" -ne 0 ]]; then + pass "entrypoint --list fails on an indeterminate candidate (real subprocess, no errexit masking)" + else + fail "entrypoint --list exited 0 despite an indeterminate candidate; got: [${out}]" + fi + rm -rf "${d}" +} + +# Builds a valid zip fixture at $2 containing wolfssl-$1-gplv3-fips-ready/. +make_fixture_zip() { + local ver="$1" out="$2" stage + stage=$(mktemp -d) + mkdir -p "${stage}/wolfssl-${ver}-gplv3-fips-ready" + echo "fixture" > "${stage}/wolfssl-${ver}-gplv3-fips-ready/README" + ( cd "${stage}" && zip -q -r "${out}" "wolfssl-${ver}-gplv3-fips-ready" ) + rm -rf "${stage}" +} + +# fetch_bundle: a download failure must not destroy a bundle already at +# the destination (the bug the HIGH finding flagged). +test_fetch_bundle_preserves_existing_on_download_failure() { + local d + d=$(mktemp -d) + mkdir -p "${d}/wolfssl-9.9.9-gplv3-fips-ready" + touch "${d}/wolfssl-9.9.9-gplv3-fips-ready/CANARY" + + cat > "${MOCKBIN}/curl" <<'EOF' +#!/usr/bin/env bash +if [[ "$*" == *"-I"* && "$*" == *"%{http_code}"* ]]; then + printf '200' + exit 0 +fi +if [[ "$*" == *"--max-time 900"* || "$*" == *"-o"* ]]; then + for a in "$@"; do + if [[ "$a" == *"gplv3-fips-ready.zip"* ]]; then + exit 1 + fi + done +fi +exit 1 +EOF + chmod +x "${MOCKBIN}/curl" + + if run_case "${d}" fetch_bundle "9.9.9" 2>/dev/null; then + fail "fetch_bundle reported success despite the download failing" + elif [[ -f "${d}/wolfssl-9.9.9-gplv3-fips-ready/CANARY" ]]; then + pass "fetch_bundle preserves an existing bundle when download fails" + else + fail "fetch_bundle destroyed the existing bundle on a failed download" + fi + rm -rf "${d}" +} + +# fetch_bundle: a checksum mismatch must also preserve whatever was +# already there, not just a hard network failure. +test_fetch_bundle_checksum_mismatch_preserves_existing() { + local d html zip + d=$(mktemp -d) + mkdir -p "${d}/wolfssl-9.9.9-gplv3-fips-ready" + touch "${d}/wolfssl-9.9.9-gplv3-fips-ready/CANARY" + + zip="${d}/fixture.zip" + make_fixture_zip "9.9.9" "${zip}" + html="${d}/page.html" + # Advertise a hash that will never match the fixture's real SHA256. + cat > "${html}" <<'EOF' + (SHA256: 0000000000000000000000000000000000000000000000000000000000000000)
+EOF + cat > "${MOCKBIN}/curl" </dev/null; then + fail "fetch_bundle accepted a bundle with a mismatched checksum" + elif [[ -f "${d}/wolfssl-9.9.9-gplv3-fips-ready/CANARY" ]]; then + pass "fetch_bundle preserves an existing bundle on checksum mismatch" + else + fail "fetch_bundle destroyed the existing bundle on checksum mismatch" + fi + rm -rf "${d}" +} + +# fetch_bundle: a verified, successful fetch does replace whatever was +# there; the fix must not make replacement impossible, only safe. +test_fetch_bundle_replaces_on_success() { + local d zip out + d=$(mktemp -d) + mkdir -p "${d}/wolfssl-9.9.9-gplv3-fips-ready" + touch "${d}/wolfssl-9.9.9-gplv3-fips-ready/CANARY" + + zip="${d}/fixture.zip" + make_fixture_zip "9.9.9" "${zip}" + if command -v sha256sum >/dev/null 2>&1; then + real=$(sha256sum "${zip}" | awk '{print $1}') + else + real=$(shasum -a 256 "${zip}" | awk '{print $1}') + fi + html="${d}/page.html" + # 9.9.9 is the newest bundle and its published hash matches the fixture. + cat > "${html}" < (SHA256: ${real})
+EOF + cat > "${MOCKBIN}/curl" </dev/null); then + if [[ -f "${d}/wolfssl-9.9.9-gplv3-fips-ready/CANARY" ]]; then + fail "fetch_bundle left the stale bundle in place after a verified success" + elif [[ -f "${d}/wolfssl-9.9.9-gplv3-fips-ready/README" ]] \ + && [[ "${out}" == "${d}/wolfssl-9.9.9-gplv3-fips-ready" ]]; then + pass "fetch_bundle replaces an existing bundle on verified success" + else + fail "fetch_bundle succeeded but left an unexpected result: [${out}]" + fi + else + fail "fetch_bundle failed on a fully valid download" + fi + rm -rf "${d}" +} + +# fetch_bundle: the newest bundle must never be accepted without a published +# SHA256, even when the download page is reachable but lists no hash for it. +test_fetch_bundle_newest_no_hash_fails() { + local d html zip + d=$(mktemp -d) + mkdir -p "${d}/wolfssl-9.9.9-gplv3-fips-ready" + touch "${d}/wolfssl-9.9.9-gplv3-fips-ready/CANARY" + + zip="${d}/fixture.zip" + make_fixture_zip "9.9.9" "${zip}" + html="${d}/page.html" + cat > "${html}" <<'EOF' +
+EOF + cat > "${MOCKBIN}/curl" </dev/null; then + fail "fetch_bundle accepted the newest bundle with no published SHA256" + else + pass "fetch_bundle refuses the newest bundle when no SHA256 is published" + fi + rm -rf "${d}" +} + +# fetch_bundle: an unreachable download page must fail closed, not downgrade +# the newest bundle to a CRC-only check. Regression test for the fail-open. +test_fetch_bundle_unreachable_page_fails() { + local d zip + d=$(mktemp -d) + mkdir -p "${d}/wolfssl-9.9.9-gplv3-fips-ready" + touch "${d}/wolfssl-9.9.9-gplv3-fips-ready/CANARY" + + zip="${d}/fixture.zip" + make_fixture_zip "9.9.9" "${zip}" + cat > "${MOCKBIN}/curl" </dev/null; then + fail "fetch_bundle accepted a bundle while the download page was unreachable" + elif [[ -f "${d}/wolfssl-9.9.9-gplv3-fips-ready/CANARY" ]]; then + pass "fetch_bundle refuses when the download page is unreachable" + else + fail "fetch_bundle destroyed the existing bundle on an unreachable page" + fi + rm -rf "${d}" +} + +test_bundle_exists_retries_transient +test_bundle_exists_confirmed_404_no_retry +test_bundle_exists_429_is_transient +test_bundle_exists_exhausts_on_persistent_transient +test_list_versions_filters_and_sorts +test_list_versions_fails_when_tags_unavailable +test_list_versions_fails_on_indeterminate_candidate +test_entrypoint_list_fails_on_indeterminate_candidate +test_fetch_bundle_preserves_existing_on_download_failure +test_fetch_bundle_checksum_mismatch_preserves_existing +test_fetch_bundle_replaces_on_success +test_fetch_bundle_newest_no_hash_fails +test_fetch_bundle_unreachable_page_fails + +echo "" +echo "fetch-fips-ready tests: ${PASS} passed, ${FAIL} failed" +[[ "${FAIL}" -eq 0 ]] diff --git a/scripts/utils-wolfssl.sh b/scripts/utils-wolfssl.sh index 48b5c204..caf79726 100644 --- a/scripts/utils-wolfssl.sh +++ b/scripts/utils-wolfssl.sh @@ -203,6 +203,9 @@ install_wolfssl() { v6.*|linuxv6.*) fips_configure_arg="v6" ;; + v7.*|linuxv7.*) + fips_configure_arg="v7" + ;; *) # For ready, v5, v6, or other tags, use as-is fips_configure_arg="$fips_tag" diff --git a/src/wp_digests.c b/src/wp_digests.c index 7bd3f1a7..88db8832 100644 --- a/src/wp_digests.c +++ b/src/wp_digests.c @@ -528,25 +528,21 @@ IMPLEMENT_DIGEST(wp_sha512, wc_Sha512, wc_InitSha512_ex, wc_Sha512Update, wc_Sha512Final, wc_Sha512Copy, wc_Sha512Free) -#if LIBWOLFSSL_VERSION_HEX >= 0x05000000 -#if !defined(WOLFSSL_NOSHA512_224) && \ - !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) +#ifdef WP_HAVE_SHA512_224 IMPLEMENT_DIGEST(wp_sha512_224, wc_Sha512_224, WC_SHA512_224_BLOCK_SIZE, WC_SHA512_224_DIGEST_SIZE, WP_SHA2_FLAGS, wc_InitSha512_224_ex, wc_Sha512_224Update, wc_Sha512_224Final, wc_Sha512_224Copy, wc_Sha512_224Free) -#endif /* WOLFSSL_NOSHA512_224 */ +#endif /* WP_HAVE_SHA512_224 */ -#if !defined(WOLFSSL_NOSHA512_256) && \ - !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) +#ifdef WP_HAVE_SHA512_256 IMPLEMENT_DIGEST(wp_sha512_256, wc_Sha512_256, WC_SHA512_256_BLOCK_SIZE, WC_SHA512_256_DIGEST_SIZE, WP_SHA2_FLAGS, wc_InitSha512_256_ex, wc_Sha512_256Update, wc_Sha512_256Final, wc_Sha512_256Copy, wc_Sha512_256Free) -#endif /* WOLFSSL_NOSHA512_256 */ -#endif +#endif /* WP_HAVE_SHA512_256 */ #endif /* WP_HAVE_SHA512 */ diff --git a/src/wp_internal.c b/src/wp_internal.c index 513b7349..1596b0a0 100644 --- a/src/wp_internal.c +++ b/src/wp_internal.c @@ -590,18 +590,16 @@ enum wc_HashType wp_nid_to_wc_hash_type(int nid) case NID_sha512: hashType = WC_HASH_TYPE_SHA512; break; -#if LIBWOLFSSL_VERSION_HEX >= 0x05000000 - #ifndef WOLFSSL_NOSHA512_224 + #ifdef WP_HAVE_SHA512_224 case NID_sha512_224: hashType = WC_HASH_TYPE_SHA512_224; break; #endif - #ifndef WOLFSSL_NOSHA512_256 + #ifdef WP_HAVE_SHA512_256 case NID_sha512_256: hashType = WC_HASH_TYPE_SHA512_256; break; #endif -#endif case NID_sha3_224: hashType = WC_HASH_TYPE_SHA3_224; break; @@ -770,9 +768,7 @@ int wp_hash_copy(wc_HashAlg* src, wc_HashAlg* dst, enum wc_HashType hashType) rc = wc_Sha512Copy(&src->sha512, &dst->sha512); #endif break; -#if LIBWOLFSSL_VERSION_HEX >= 0x05000000 -#if !defined(WOLFSSL_NOSHA512_224) && !defined(HAVE_FIPS) && \ - !defined(SELF_TEST) +#ifdef WP_HAVE_SHA512_224 case WC_HASH_TYPE_SHA512_224: #if LIBWOLFSSL_VERSION_HEX >= 0x05007004 rc = wc_Sha512_224Copy(&src->alg.sha512, &dst->alg.sha512); @@ -780,9 +776,8 @@ int wp_hash_copy(wc_HashAlg* src, wc_HashAlg* dst, enum wc_HashType hashType) rc = wc_Sha512_224Copy(&src->sha512, &dst->sha512); #endif break; -#endif /* !WOLFSSL_NOSHA512_224 */ -#if !defined(WOLFSSL_NOSHA512_256) && !defined(HAVE_FIPS) && \ - !defined(SELF_TEST) +#endif /* WP_HAVE_SHA512_224 */ +#ifdef WP_HAVE_SHA512_256 case WC_HASH_TYPE_SHA512_256: #if LIBWOLFSSL_VERSION_HEX >= 0x05007004 rc = wc_Sha512_256Copy(&src->alg.sha512, &dst->alg.sha512); @@ -790,8 +785,7 @@ int wp_hash_copy(wc_HashAlg* src, wc_HashAlg* dst, enum wc_HashType hashType) rc = wc_Sha512_256Copy(&src->sha512, &dst->sha512); #endif break; -#endif /* !WOLFSSL_NOSHA512_256 */ -#endif /* LIBWOLFSSL_VERSION_HEX >= 0x05000000 */ +#endif /* WP_HAVE_SHA512_256 */ #else case WC_HASH_TYPE_SHA512: case WC_HASH_TYPE_SHA512_224: diff --git a/src/wp_wolfprov.c b/src/wp_wolfprov.c index f09c5607..ccd28ec4 100644 --- a/src/wp_wolfprov.c +++ b/src/wp_wolfprov.c @@ -430,18 +430,16 @@ static const OSSL_ALGORITHM wolfprov_digests[] = { #ifdef WP_HAVE_SHA512 { WP_NAMES_SHA2_512, WOLFPROV_PROPERTIES, wp_sha512_functions, "" }, -#if LIBWOLFSSL_VERSION_HEX >= 0x05000000 -#ifndef WOLFSSL_NOSHA512_224 +#ifdef WP_HAVE_SHA512_224 { WP_NAMES_SHA2_512_224, WOLFPROV_PROPERTIES, wp_sha512_224_functions, "" }, -#endif /* !WOLFSSL_NOSHA512_224 */ -#ifndef WOLFSSL_NOSHA512_256 +#endif /* WP_HAVE_SHA512_224 */ +#ifdef WP_HAVE_SHA512_256 { WP_NAMES_SHA2_512_256, WOLFPROV_PROPERTIES, wp_sha512_256_functions, "" }, -#endif /* !WOLFSSL_NOSHA512_256 */ -#endif +#endif /* WP_HAVE_SHA512_256 */ #endif /* WP_HAVE_SHA512 */ /* SHA-3 */ diff --git a/test/test_digest.c b/test/test_digest.c index 5b30132c..97a95b44 100644 --- a/test/test_digest.c +++ b/test/test_digest.c @@ -149,6 +149,24 @@ int test_sha512(void *data) /******************************************************************************/ +#ifdef WP_HAVE_SHA512_224 +int test_sha512_224(void *data) +{ + return test_create_digest("SHA512-224", data); +} +#endif + +/******************************************************************************/ + +#ifdef WP_HAVE_SHA512_256 +int test_sha512_256(void *data) +{ + return test_create_digest("SHA512-256", data); +} +#endif + +/******************************************************************************/ + #ifdef WP_HAVE_SHA3_224 int test_sha3_224(void *data) { diff --git a/test/test_rsa.c b/test/test_rsa.c index b595b9c4..c6ff774a 100644 --- a/test/test_rsa.c +++ b/test/test_rsa.c @@ -3361,4 +3361,115 @@ int test_rsa_sig_alg_id(void *data) return err; } +#if defined(WP_HAVE_SHA512_224) || defined(WP_HAVE_SHA512_256) +/* EVP_MD_CTX_copy_ex on a partial RSA digest_sign accumulator, exercising + * the wp_hash_copy() SHA512-224/256 branches. Split into two tests since + * WP_HAVE_SHA512_224 and WP_HAVE_SHA512_256 are independently defined. */ +static int test_rsa_dupctx_verify(EVP_PKEY *pkey, const char *md, + const unsigned char *msg, size_t msgLen, unsigned char *sig, + size_t sigLen) +{ + int err; + EVP_MD_CTX *vCtx = EVP_MD_CTX_new(); + + err = vCtx == NULL; + if (err == 0) { + err = EVP_DigestVerifyInit_ex(vCtx, NULL, md, wpLibCtx, NULL, pkey, + NULL) != 1; + } + if (err == 0) { + err = EVP_DigestVerifyUpdate(vCtx, msg, msgLen) != 1; + } + if (err == 0) { + err = EVP_DigestVerifyFinal(vCtx, sig, sigLen) != 1; + } + + EVP_MD_CTX_free(vCtx); + + return err; +} + +static int test_rsa_dupctx_one_digest(const char *md, void *data) +{ + static const unsigned char part1[16] = "rsa-dupctx-part1"; + static const unsigned char part2[16] = "rsa-dupctx-part2"; + int err = 0; + EVP_PKEY *pkey = NULL; + const unsigned char *p = rsa_key_der_2048; + EVP_MD_CTX *a = NULL; + EVP_MD_CTX *b = NULL; + unsigned char sigA[256]; + unsigned char sigB[256]; + size_t sigALen = sizeof(sigA); + size_t sigBLen = sizeof(sigB); + unsigned char msg[32]; + + (void)data; + + XMEMCPY(msg, part1, sizeof(part1)); + XMEMCPY(msg + sizeof(part1), part2, sizeof(part2)); + + PRINT_MSG("RSA dupctx %s", md); + + pkey = d2i_PrivateKey(EVP_PKEY_RSA, NULL, &p, sizeof(rsa_key_der_2048)); + err = pkey == NULL; + + if (err == 0) { + a = EVP_MD_CTX_new(); + err = (a == NULL); + } + if (err == 0) { + err = EVP_DigestSignInit_ex(a, NULL, md, wpLibCtx, NULL, pkey, + NULL) != 1; + } + if (err == 0) { + err = EVP_DigestSignUpdate(a, part1, sizeof(part1)) != 1; + } + if (err == 0) { + b = EVP_MD_CTX_new(); + err = (b == NULL); + } + if (err == 0) { + err = EVP_MD_CTX_copy_ex(b, a) != 1; + } + if (err == 0) { + err = EVP_DigestSignUpdate(a, part2, sizeof(part2)) != 1 + || EVP_DigestSignUpdate(b, part2, sizeof(part2)) != 1; + } + if (err == 0) { + err = EVP_DigestSignFinal(a, sigA, &sigALen) != 1 + || EVP_DigestSignFinal(b, sigB, &sigBLen) != 1; + } + if (err == 0) { + err = test_rsa_dupctx_verify(pkey, md, msg, sizeof(msg), sigA, + sigALen) != 0 + || test_rsa_dupctx_verify(pkey, md, msg, sizeof(msg), sigB, + sigBLen) != 0; + if (err) { + PRINT_ERR_MSG("RSA dupctx sig verify failed"); + } + } + + EVP_MD_CTX_free(a); + EVP_MD_CTX_free(b); + EVP_PKEY_free(pkey); + + return err; +} +#endif /* WP_HAVE_SHA512_224 || WP_HAVE_SHA512_256 */ + +#ifdef WP_HAVE_SHA512_224 +int test_rsa_sha512_224_dupctx(void *data) +{ + return test_rsa_dupctx_one_digest("SHA512-224", data); +} +#endif /* WP_HAVE_SHA512_224 */ + +#ifdef WP_HAVE_SHA512_256 +int test_rsa_sha512_256_dupctx(void *data) +{ + return test_rsa_dupctx_one_digest("SHA512-256", data); +} +#endif /* WP_HAVE_SHA512_256 */ + #endif /* WP_HAVE_RSA */ diff --git a/test/unit.c b/test/unit.c index 24692402..8eca1d19 100644 --- a/test/unit.c +++ b/test/unit.c @@ -197,6 +197,12 @@ TEST_CASE test_case[] = { #ifdef WP_HAVE_SHA512 TEST_DECL(test_sha512, NULL), #endif +#ifdef WP_HAVE_SHA512_224 + TEST_DECL(test_sha512_224, NULL), +#endif +#ifdef WP_HAVE_SHA512_256 + TEST_DECL(test_sha512_256, NULL), +#endif #ifdef WP_HAVE_SHA3_224 TEST_DECL(test_sha3_224, NULL), #endif @@ -362,6 +368,12 @@ TEST_CASE test_case[] = { TEST_DECL(test_rsa_get_params, NULL), #endif TEST_DECL(test_rsa_sig_alg_id, NULL), +#ifdef WP_HAVE_SHA512_224 + TEST_DECL(test_rsa_sha512_224_dupctx, NULL), +#endif +#ifdef WP_HAVE_SHA512_256 + TEST_DECL(test_rsa_sha512_256_dupctx, NULL), +#endif TEST_DECL(test_rsa_pss_salt, NULL), TEST_DECL(test_rsa_pss_restrictions, NULL), TEST_DECL(test_rsa_load_key, NULL), diff --git a/test/unit.h b/test/unit.h index e7829d29..8d6dc82c 100644 --- a/test/unit.h +++ b/test/unit.h @@ -137,6 +137,8 @@ int test_sha224(void *data); int test_sha256(void *data); int test_sha384(void *data); int test_sha512(void *data); +int test_sha512_224(void *data); +int test_sha512_256(void *data); int test_sha3_224(void *data); int test_sha3_256(void *data); int test_sha3_384(void *data); @@ -341,6 +343,8 @@ int test_rsa_pkey_keygen(void *data); int test_rsa_pkey_invalid_key_size(void *data); int test_rsa_get_params(void *data); int test_rsa_sig_alg_id(void *data); +int test_rsa_sha512_224_dupctx(void *data); +int test_rsa_sha512_256_dupctx(void *data); int test_rsa_pss_salt(void *date); int test_rsa_pss_restrictions(void *data);