Skip to content
Merged
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
3 changes: 2 additions & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ jobs:
fi
done < <(git ls-files 'src/*.c' 'src/**/*.c' 'wolfcert/*.h' \
'cli/*.c' 'tests/*.c' 'tests/*.h' \
'tests/**/*.c' 'tests/**/*.h')
'tests/**/*.c' 'tests/**/*.h' \
'scripts/ci/*.c' 'scripts/ci/*.h')
if [ "$missing" -ne 0 ]; then
echo "One or more sources lack the GPL copyright block (copy it"
echo "verbatim from any existing .c/.h)."
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ jobs:
ctest --test-dir build -j "$(nproc)" --output-on-failure

no-posix-arm:
name: freestanding ARM compile (no POSIX)
name: header hygiene (freestanding ARM + config resolution)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
Expand All @@ -258,6 +258,10 @@ jobs:
run: |
scripts/ci/compile-freestanding.sh \
--wolfssl-src "$RUNNER_TEMP/wolfssl"
- name: Assert both entry headers resolve wolfSSL's config identically
run: |
scripts/ci/check-config-resolution.sh \
--wolfssl-src "$RUNNER_TEMP/wolfssl"

negative-config:
name: negative-config (fail-fast gates)
Expand Down
27 changes: 15 additions & 12 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,21 @@ if(TARGET PkgConfig::WOLFSSL)
elseif(TARGET wolfssl::wolfssl)
get_target_property(_wolfssl_cflags wolfssl::wolfssl INTERFACE_INCLUDE_DIRECTORIES)
endif()
# Probe wolfSSL's feature set the way wolfcert/check_config.h resolves it, so
# configure and the headers agree.
set(_wc_wolfssl_prologue "
#if !defined(WOLFSSL_USER_SETTINGS) && !defined(WOLFSSL_USE_OPTIONS_H) && !defined(WOLFSSL_NO_OPTIONS_H) && !defined(WOLFSSL_CUSTOM_CONFIG) && !defined(ARDUINO) && !defined(PLATFORMIO) && !defined(USE_HAL_DRIVER) && !defined(NUCLEUS_PLUS_2_3) && !defined(WOLFSSL_MX2_CONF_INCLUDE)
#define WOLFSSL_USE_OPTIONS_H
#endif
#include <wolfssl/wolfcrypt/settings.h>
")

set(CMAKE_REQUIRED_INCLUDES ${_wolfssl_cflags})
foreach(sym IN ITEMS HAVE_PKCS7 WOLFSSL_CERT_GEN WOLFSSL_CERT_REQ WOLFSSL_CERT_EXT
WOLFSSL_KEY_GEN WOLF_CRYPTO_CB WOLFSSL_BASE64_ENCODE
OPENSSL_EXTRA WOLFSSL_ALT_NAMES WOLFSSL_CERT_NAME_ALL)
unset(_have_${sym} CACHE)
check_c_source_compiles("
#include <wolfssl/options.h>
check_c_source_compiles("${_wc_wolfssl_prologue}
#ifndef ${sym}
#error missing
#endif
Expand All @@ -140,8 +148,7 @@ foreach(entry IN ITEMS "NO_AES;AES;--enable-aes"
list(GET entry 1 _name)
list(GET entry 2 _flag)
unset(_off_${_neg} CACHE)
check_c_source_compiles("
#include <wolfssl/options.h>
check_c_source_compiles("${_wc_wolfssl_prologue}
#ifdef ${_neg}
#error disabled
#endif
Expand All @@ -159,8 +166,7 @@ endforeach()
# optional for an EST-only build (ECC / Ed25519 / Ed448 / ML-DSA). The
# WOLFCERT_HAVE_RSA definition gates the RSA key algorithm.
unset(_off_NO_RSA CACHE)
check_c_source_compiles("
#include <wolfssl/options.h>
check_c_source_compiles("${_wc_wolfssl_prologue}
#ifdef NO_RSA
#error disabled
#endif
Expand All @@ -183,16 +189,14 @@ endif()
# TLS: the HTTPS transport needs at least TLS 1.2 or TLS 1.3; the endpoints pin
# their floor to whichever lower version is available.
unset(_have_tls12 CACHE)
check_c_source_compiles("
#include <wolfssl/options.h>
check_c_source_compiles("${_wc_wolfssl_prologue}
#ifdef WOLFSSL_NO_TLS12
#error notls12
#endif
int main(void) { return 0; }
" _have_tls12)
unset(_have_tls13 CACHE)
check_c_source_compiles("
#include <wolfssl/options.h>
check_c_source_compiles("${_wc_wolfssl_prologue}
#ifndef WOLFSSL_TLS13
#error notls13
#endif
Expand All @@ -214,8 +218,7 @@ foreach(entry IN ITEMS "HAVE_ECC;ECC;--enable-ecc"
list(GET entry 1 _short)
list(GET entry 2 _flag)
unset(_have_${_sym} CACHE)
check_c_source_compiles("
#include <wolfssl/options.h>
check_c_source_compiles("${_wc_wolfssl_prologue}
#ifndef ${_sym}
#error missing
#endif
Expand Down
1 change: 0 additions & 1 deletion cli/wolfcert_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
# include <wolfcert/scep.h>
#endif

#include <wolfssl/options.h>
#include <wolfssl/wolfcrypt/asn_public.h>
#include <wolfssl/wolfcrypt/hash.h>

Expand Down
27 changes: 18 additions & 9 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,19 @@ PKG_CHECK_MODULES([WOLFSSL], [wolfssl >= 5.9.2],
WOLFSSL_ALT_NAMES, WOLFSSL_CERT_NAME_ALL.])])
AM_CONDITIONAL([HAVE_WOLFSSL], [test "x$have_wolfssl" = "xyes"])

# Probe wolfSSL's feature set the way wolfcert/check_config.h resolves it, so
# configure and the headers agree.
m4_define([WOLFCERT_WOLFSSL_PROLOGUE], [[
#if !defined(WOLFSSL_USER_SETTINGS) && !defined(WOLFSSL_USE_OPTIONS_H) && !defined(WOLFSSL_NO_OPTIONS_H) && !defined(WOLFSSL_CUSTOM_CONFIG) && !defined(ARDUINO) && !defined(PLATFORMIO) && !defined(USE_HAL_DRIVER) && !defined(NUCLEUS_PLUS_2_3) && !defined(WOLFSSL_MX2_CONF_INCLUDE)
#define WOLFSSL_USE_OPTIONS_H
#endif
#include <wolfssl/wolfcrypt/settings.h>
]])

AC_DEFUN([WOLFCERT_REJECT_NEG],
[AC_MSG_CHECKING([that wolfSSL does not define $1])
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM([[#include <wolfssl/options.h>]],
[AC_LANG_PROGRAM(WOLFCERT_WOLFSSL_PROLOGUE,
[[#ifdef $1
#error disabled
#endif]])],
Expand Down Expand Up @@ -212,7 +221,7 @@ AC_CHECK_DECLS([HAVE_PKCS7, WOLFSSL_CERT_GEN, WOLFSSL_CERT_REQ, WOLFSSL_CERT_EXT
--enable-certgen --enable-certreq --enable-certext --enable-keygen
--enable-base64encode --enable-opensslextra
CPPFLAGS="-DWOLFSSL_ALT_NAMES -DWOLFSSL_CERT_NAME_ALL".])],
[[#include <wolfssl/options.h>]])
WOLFCERT_WOLFSSL_PROLOGUE)

# wolfSSL default-on features that wolfCert requires unconditionally: AES,
# SHA-256. Each must NOT be marked disabled by a NO_* macro.
Expand All @@ -224,7 +233,7 @@ WOLFCERT_REJECT_NEG([NO_SHA256], [SHA-256])
# WOLFCERT_HAVE_RSA gates the RSA key algorithm.
AC_MSG_CHECKING([whether wolfSSL provides RSA])
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM([[#include <wolfssl/options.h>]],
[AC_LANG_PROGRAM(WOLFCERT_WOLFSSL_PROLOGUE,
[[#ifdef NO_RSA
#error disabled
#endif]])],
Expand All @@ -242,14 +251,14 @@ AM_CONDITIONAL([WOLFCERT_HAVE_RSA], [test "x$have_rsa" = "xyes"])
# their floor to whichever lower version is available.
AC_MSG_CHECKING([whether wolfSSL provides TLS 1.2])
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM([[#include <wolfssl/options.h>]],
[AC_LANG_PROGRAM(WOLFCERT_WOLFSSL_PROLOGUE,
[[#ifdef WOLFSSL_NO_TLS12
#error notls12
#endif]])],
[AC_MSG_RESULT([yes]); have_tls12=yes], [AC_MSG_RESULT([no]); have_tls12=no])
AC_MSG_CHECKING([whether wolfSSL provides TLS 1.3])
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM([[#include <wolfssl/options.h>]],
[AC_LANG_PROGRAM(WOLFCERT_WOLFSSL_PROLOGUE,
[[#ifndef WOLFSSL_TLS13
#error notls13
#endif]])],
Expand All @@ -263,22 +272,22 @@ AS_IF([test "x$have_tls12" = "xno" && test "x$have_tls13" = "xno"],
AC_CHECK_DECL([HAVE_ECC], [have_ecc=yes],
[have_ecc=no; AC_MSG_WARN([wolfSSL was built without HAVE_ECC; ECC key
types will return WOLFCERT_ERR_UNSUPPORTED. Rebuild with --enable-ecc.])],
[[#include <wolfssl/options.h>]])
WOLFCERT_WOLFSSL_PROLOGUE)
AC_CHECK_DECL([HAVE_ED25519], [have_ed25519=yes],
[have_ed25519=no; AC_MSG_WARN([wolfSSL was built without HAVE_ED25519;
Ed25519 key types will return WOLFCERT_ERR_UNSUPPORTED. Rebuild with
--enable-ed25519.])],
[[#include <wolfssl/options.h>]])
WOLFCERT_WOLFSSL_PROLOGUE)
AC_CHECK_DECL([HAVE_ED448], [have_ed448=yes],
[have_ed448=no; AC_MSG_WARN([wolfSSL was built without HAVE_ED448; Ed448
key types will return WOLFCERT_ERR_UNSUPPORTED. Rebuild with
--enable-ed448.])],
[[#include <wolfssl/options.h>]])
WOLFCERT_WOLFSSL_PROLOGUE)
AC_CHECK_DECL([WOLFSSL_HAVE_MLDSA], [have_mldsa=yes],
[have_mldsa=no; AC_MSG_WARN([wolfSSL was built without WOLFSSL_HAVE_MLDSA;
mldsa:44/65/87 will return WOLFCERT_ERR_UNSUPPORTED. Rebuild with
--enable-mldsa.])],
[[#include <wolfssl/options.h>]])
WOLFCERT_WOLFSSL_PROLOGUE)
CPPFLAGS="$save_CPPFLAGS"

# Require at least one key algorithm.
Expand Down
16 changes: 15 additions & 1 deletion docs/CI.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ build entirely.

| Workflow | Trigger | What it does |
|----------|---------|--------------|
| `pr.yml` | PR + push | Merge gate: CMake (`-Werror`) + autoconf + ASan/UBSan on the canonical config, plus per-PR feature/config gating — EST-only, SCEP-only, server-off, the key-alg variants (NO_RSA, ECC-only, RSA-only, no-3DES), TLS 1.3-only, the three ML-DSA per-level builds, the static-memory and no-malloc constrained builds, the platform-pieces-off build (`cmake-no-builtin-transport`), the freestanding ARM compile (`no-posix-arm`), the header-only (`WOLFCERT_USER_SETTINGS`) build, a macOS build, and the two cheapest configure-must-fail assertions. |
| `pr.yml` | PR + push | Merge gate: CMake (`-Werror`) + autoconf + ASan/UBSan on the canonical config, plus per-PR feature/config gating — EST-only, SCEP-only, server-off, the key-alg variants (NO_RSA, ECC-only, RSA-only, no-3DES), TLS 1.3-only, the three ML-DSA per-level builds, the static-memory and no-malloc constrained builds, the platform-pieces-off build (`cmake-no-builtin-transport`), the freestanding ARM compile and config-resolution check (`no-posix-arm`), the header-only (`WOLFCERT_USER_SETTINGS`) build, a macOS build, and the two cheapest configure-must-fail assertions. |
| `lint.yml` | PR + push | GPL license-header check and CMake↔autoconf parity of both the library and test source lists (`scripts/ci/check-buildsystem-parity.sh`). No wolfSSL build — fails in seconds. |
| `nightly.yml` | schedule + dispatch | Re-runs the wolfSSL-variant build matrix against fresh wolfSSL `master`, the macOS extras, and the full negative-config set. Also **reseeds the wolfSSL prefix caches** so the next day's PRs restore instead of build. The feature/config gating itself now runs per-PR (see `pr.yml`). |
| `sanitizers.yml` | schedule + dispatch | ASan+UBSan over the full test suite, ThreadSanitizer over the threaded integration roundtrips (against a TSAN-instrumented wolfSSL), and valgrind over a representative subset. |
Expand Down Expand Up @@ -76,3 +76,17 @@ scripts/ci/compile-freestanding.sh --wolfssl-src /tmp/wolfssl-src

Its companion is the `cmake-no-builtin-transport` row: the ARM job proves the
code is header-clean, that row proves the gated build links and passes tests.

The same job then runs `scripts/ci/check-config-resolution.sh`, which preprocesses
one `<wolfcert/est.h>` translation unit and one `<wolfcert/wolfcert.h>` one and
requires the two to resolve the same wolfSSL feature set, since `memory.h` and
`check_config.h` reach that config separately. Each case stages an
`<wolfssl/options.h>` that disagrees with `user_settings.h`; an absent or
forwarding one would let the two agree for the wrong reason. The cases cover
both provenances - `user_settings.h` winning under `WOLFSSL_USER_SETTINGS`, and
`options.h` winning without it. It uses the host `cc` and takes seconds.

```sh
scripts/ci/check-config-resolution.sh --wolfssl-src /tmp/wolfssl-src
scripts/ci/check-config-resolution.sh --wolfssl-src /tmp/wolfssl-src optionsh-decoy
```
36 changes: 30 additions & 6 deletions docs/EMBEDDED.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ as a header, exactly like wolfSSL's `WOLFSSL_USER_SETTINGS` / `user_settings.h`:
to `user_settings.h` somewhere on your include path and edit which
`WOLFCERT_HAVE_*` macros are defined.
2. Compile wolfCert (and your application) with `-DWOLFCERT_USER_SETTINGS`.
`wolfcert/types.h` then pulls your `user_settings.h` in place of the
generated `options.h`.
`wolfcert/types.h` then reads it instead.

The macro set is small and closed - three protocol switches
(`WOLFCERT_HAVE_EST` / `_SCEP` / `_SERVER`) and five key algorithms
Expand All @@ -37,10 +36,35 @@ rules the configure step does: at least one key algorithm, SCEP requires RSA
(RFC 8894), and the wolfSSL feature set wolfCert depends on (PKCS#7, cert
gen/req/ext, key gen, CryptoCb, base64 encode, OpenSSL-extra, alt names,
`WOLFSSL_CERT_NAME_ALL`, AES, SHA-256, and TLS 1.2 or 1.3). The header you copy
documents the matching wolfSSL configure flags. If wolfSSL itself is configured
through *its own* `user_settings.h` (so `<wolfssl/options.h>` does not reflect
its real feature set), define `WOLFCERT_NO_WOLFSSL_FEATURE_CHECK` to skip just
the wolfSSL half of the validation.
documents the matching wolfSSL configure flags.

wolfCert reads that feature set through `<wolfssl/wolfcrypt/settings.h>`, so a
wolfSSL configured by its own `user_settings.h` is validated correctly and needs
nothing extra. Define `WOLFCERT_NO_WOLFSSL_FEATURE_CHECK` to skip the wolfSSL
half of the validation. wolfCert reaches for `<wolfssl/options.h>` only when
none of `WOLFSSL_USER_SETTINGS`, `WOLFSSL_NO_OPTIONS_H`,
`WOLFSSL_CUSTOM_CONFIG`, `ARDUINO`, `PLATFORMIO`, `USE_HAL_DRIVER`,
`NUCLEUS_PLUS_2_3` or `WOLFSSL_MX2_CONF_INCLUDE` is defined - the platform names
among those because `settings.h` picks a config for them itself, past the point
it would read `options.h`.

STM32CubeMX2 needs one extra step on wolfSSL 5.9.2, where `settings.h` locates
`mx_wolfSSL_conf.h` by probing with `__has_include` and so sets
`WOLFSSL_MX2_CONF_INCLUDE` too late for the test above to see. Those builds need
`WOLFSSL_CUSTOM_CONFIG` *and* an explicit `#include "mx_wolfSSL_conf.h"`, since
the probe is itself switched off by `WOLFSSL_CUSTOM_CONFIG`, as it is by
`WOLFSSL_NO_OPTIONS_H`. Later wolfSSL drops the probe and takes
`WOLFSSL_MX2_CONF_INCLUDE` from the pack's generated `Pre_Include_Global.h`,
which the test above does see, so nothing extra is needed there.

This makes include order matter for a build that takes its feature set from
`<wolfssl/options.h>` - the ordinary autoconf or CMake wolfSSL. `settings.h` is
header-guarded, so a wolfSSL header reaching it before any wolfCert header
leaves `options.h` unread, and wolfCert fails the build rather than compile
against wolfSSL's defaults. Include `<wolfcert/wolfcert.h>` first, or define
`WOLFSSL_USE_OPTIONS_H` before the wolfSSL header. A `WOLFSSL_USER_SETTINGS`
build is unaffected, since `settings.h` reads `user_settings.h` whichever
header reaches it first.

When you build wolfCert's own tree this way, the `WOLFCERT_ENABLE_EST` /
`_SCEP` / `_SERVER` options (CMake `-DWOLFCERT_USER_SETTINGS=ON
Expand Down
1 change: 0 additions & 1 deletion examples/enroll_cryptocb.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
# error "examples/enroll_cryptocb.c needs wolfCert built with EST enabled"
#endif

#include <wolfssl/options.h>
#include <wolfssl/wolfcrypt/cryptocb.h>
#include <wolfssl/wolfcrypt/error-crypt.h>

Expand Down
1 change: 0 additions & 1 deletion examples/enroll_scep.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
#endif
#include <wolfcert/scep.h>

#include <wolfssl/options.h>
#include <wolfssl/wolfcrypt/asn_public.h>

#include <stdio.h>
Expand Down
15 changes: 8 additions & 7 deletions examples/user_settings.h.example
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@
* their own build. wolfcert/check_config.h validates the result at compile
* time, so a contradictory or incomplete config fails with a clear #error.
*
* The "user_settings.h" name is shared with wolfSSL's own WOLFSSL_USER_SETTINGS
* header on purpose. The macro namespaces are disjoint (wolfCert reads only
* WOLFCERT_HAVE_*; wolfSSL reads HAVE_* / NO_* / WOLFSSL_*), so a single
* user_settings.h can carry both libraries' config - the recommended setup.
* Keep only one file by this name on your include path: both wolfSSL and
* wolfCert include it unqualified, so the first one found serves both. See
* docs/EMBEDDED.md.
* This template carries wolfCert's half of the config only. The
* "user_settings.h" name is shared with wolfSSL's own WOLFSSL_USER_SETTINGS
* header on purpose, and the macro namespaces are disjoint (wolfCert reads only
* WOLFCERT_HAVE_*; wolfSSL reads HAVE_* / NO_* / WOLFSSL_*), so one file can
* carry both. Building wolfSSL that way means adding its macros here too - the
* flag list at the end of this comment names them. Keep only one file by this
* name on your include path: both wolfSSL and wolfCert include it unqualified,
* so the first one found serves both. See docs/EMBEDDED.md.
*
* RULES
* - To ENABLE a feature, leave its #define uncommented.
Expand Down
Loading
Loading