From b6b4ee206c4120d82efb6aa345611d9d3c660018 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Thu, 20 Aug 2026 14:37:28 -0600 Subject: [PATCH 1/3] fips: auto-detect --enable-fips version from a tree manifest --- IDE/WIN10/user_settings.h | 22 ++++++ Makefile.am | 7 ++ configure.ac | 44 +++++++++++- fips-check.sh | 138 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 210 insertions(+), 1 deletion(-) diff --git a/IDE/WIN10/user_settings.h b/IDE/WIN10/user_settings.h index 1fcb3173182..15e4b823e76 100644 --- a/IDE/WIN10/user_settings.h +++ b/IDE/WIN10/user_settings.h @@ -32,6 +32,28 @@ #define HAVE_FIPS_VERSION_MINOR 3 #endif +/* The region between the two markers below is rewritten by fips-check.sh when + * a FIPS bundle is assembled, so that a Visual Studio build of the bundle uses + * the same HAVE_FIPS_VERSION_* values that ./configure derives for the bundled + * module. It sits after the hand-maintained blocks above so that it wins. + * + * In a plain git checkout there is no bundled module, so it stays inert. + * Do not edit between the markers; edits are overwritten. */ +/* BEGIN GENERATED FIPS VERSION */ +#if 0 +#undef HAVE_FIPS +#define HAVE_FIPS +#undef HAVE_FIPS_VERSION +#define HAVE_FIPS_VERSION 5 +#undef HAVE_FIPS_VERSION_MAJOR +#define HAVE_FIPS_VERSION_MAJOR 5 +#undef HAVE_FIPS_VERSION_MINOR +#define HAVE_FIPS_VERSION_MINOR 2 +#undef HAVE_FIPS_VERSION_PATCH +#define HAVE_FIPS_VERSION_PATCH 1 +#endif +/* END GENERATED FIPS VERSION */ + /* Verify this is Windows */ #ifndef _WIN32 diff --git a/Makefile.am b/Makefile.am index 30c3ccdf0ec..eb447289bc3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -671,7 +671,14 @@ CLEANFILES += advisories/out/*.csaf.json advisories/out/*.cdx.json # downstream `make advisory`. EXTRA_DIST += advisories/vex-overlay.json +# The FIPS bundle manifest is written by fips-check.sh and so exists only in an +# assembled FIPS source tree. It cannot go in EXTRA_DIST, because automake +# requires those files unconditionally and `make dist` would then fail for +# every ordinary non-FIPS release. dist-hook: + @if test -f $(srcdir)/.wolfssl-fips-bundle; then \ + cp -p $(srcdir)/.wolfssl-fips-bundle $(distdir)/.wolfssl-fips-bundle; \ + fi $(MKDIR_P) $(distdir)/advisories/records @for f in $(srcdir)/advisories/records/*.json; do \ test -f "$$f" || continue; \ diff --git a/configure.ac b/configure.ac index 48993b57edc..6b7fe77c284 100644 --- a/configure.ac +++ b/configure.ac @@ -479,6 +479,29 @@ AC_ARG_ENABLE([fips], [ENABLED_FIPS=$enableval], [ENABLED_FIPS="no"]) +# The FIPS bundle manifest is written by fips-check.sh when a FIPS source tree +# is assembled. It records which --enable-fips option matches the FIPS module +# sources actually present in this tree. It is not compiled, and sits outside +# the FIPS module boundary. +# +# A bare --enable-fips (or --enable-fips=auto) takes its version from the +# manifest. An explicit --enable-fips= is cross-checked against it +# further below. +WOLFSSL_FIPS_BUNDLE_FILE=".wolfssl-fips-bundle" +FIPS_BUNDLE_OPTION= +AS_IF([test -r "$srcdir/$WOLFSSL_FIPS_BUNDLE_FILE"], + [FIPS_BUNDLE_OPTION=`sed -n 's/^FIPS_OPTION=//p' "$srcdir/$WOLFSSL_FIPS_BUNDLE_FILE" 2>/dev/null | head -1`]) + +AS_CASE([$ENABLED_FIPS], + [yes|auto],[ + AC_MSG_CHECKING([which FIPS module this source tree contains]) + AS_IF([test "x$FIPS_BUNDLE_OPTION" != "x"], + [ENABLED_FIPS="$FIPS_BUNDLE_OPTION" + AC_MSG_RESULT([$ENABLED_FIPS (from $WOLFSSL_FIPS_BUNDLE_FILE)])], + [AC_MSG_RESULT([unknown]) + AC_MSG_ERROR([--enable-fips cannot auto-detect the FIPS version of this source tree: no $WOLFSSL_FIPS_BUNDLE_FILE manifest was found. Either this tree predates the manifest, or it is not a FIPS bundle. Pass the version explicitly, for example --enable-fips=v5 or --enable-fips=ready.])]) + ]) + FIPS_FLAVOR="$ENABLED_FIPS" AC_SUBST([FIPS_FLAVOR]) @@ -576,6 +599,25 @@ FIPS_DEVREADY_MAJOR=7 FIPS_DEVREADY_MINOR=0 FIPS_DEVREADY_PATCH=0 +# If this tree carries a FIPS bundle manifest and a FIPS version was named +# explicitly, require that the two agree. Configuring a FIPS bundle with the +# wrong --enable-fips version otherwise fails much later, deep in the compile, +# with hundreds of errors that look like library bugs rather than like a +# mismatched build option. +AS_CASE([$ENABLED_FIPS], + [no|disabled],[], + [ + FIPS_REQUESTED_NORM="$ENABLED_FIPS" + AS_CASE([$FIPS_REQUESTED_NORM], + [cert2425],[FIPS_REQUESTED_NORM="v1"], + [cert3389],[FIPS_REQUESTED_NORM="v2"], + [cert4718],[FIPS_REQUESTED_NORM="v5"], + [v7-ready],[FIPS_REQUESTED_NORM="ready"], + [v7-dev],[FIPS_REQUESTED_NORM="dev"]) + AS_IF([test "x$FIPS_BUNDLE_OPTION" != "x" && test "x$FIPS_REQUESTED_NORM" != "x$FIPS_BUNDLE_OPTION"], + [AC_MSG_ERROR([--enable-fips=$ENABLED_FIPS does not match the FIPS module sources in this tree, which are $FIPS_BUNDLE_OPTION according to $WOLFSSL_FIPS_BUNDLE_FILE. Use --enable-fips=$FIPS_BUNDLE_OPTION, or a bare --enable-fips to auto-detect.])]) + ]) + AS_CASE([$ENABLED_FIPS], [no],[ FIPS_VERSION="none" @@ -797,7 +839,7 @@ AS_CASE([$FIPS_VERSION], [], [ AS_IF([ ! test -s $srcdir/wolfcrypt/src/fips.c], - [AC_MSG_ERROR([non-FIPS source tree is incompatible with --enable-fips=$enableval])]) + [AC_MSG_ERROR([non-FIPS source tree is incompatible with --enable-fips=$FIPS_REQUESTED_NORM])]) ] ) diff --git a/fips-check.sh b/fips-check.sh index 2cffedea60a..70cb2acb7ce 100755 --- a/fips-check.sh +++ b/fips-check.sh @@ -498,6 +498,136 @@ function copy_fips_files() { done } +# user_settings.h templates that carry a generated FIPS version region (see +# stamp_user_settings below). Only files whose FIPS version should track +# whatever module this bundle contains belong here. The OE-specific templates +# under IDE/ are deliberately pinned to their own module version and are not +# listed. +FIPS_USER_SETTINGS=( + 'IDE/WIN10/user_settings.h' +) + +# write_fips_manifest records which --enable-fips option matches the FIPS +# module sources just assembled into this tree. configure reads it, so that a +# bare --enable-fips resolves to the right version and so that an explicit +# --enable-fips= that disagrees fails immediately with one clear +# message instead of thousands of downstream compile errors. +# +# This has to be recorded here because it is not recoverable later. A tree is +# assembled from many independently frozen tags, no one of which names the +# result, and from FIPS v6.0.0 on the module version string is built at compile +# time out of the very macros we would be trying to determine. Assembly time is +# the only point at which the answer is actually known. +# +# The manifest is not compiled, and is outside the FIPS module boundary. +function write_fips_manifest() { + local manifest=$1 + local all_tags + + all_tags=$(printf '%s\n' "${WOLFCRYPT_TAGS_NEEDED[@]}" "${FIPS_TAGS_NEEDED[@]}" | + sort -u | tr '\n' ' ') + + { + echo "# wolfSSL FIPS bundle manifest, generated by fips-check.sh." + echo "# Records the FIPS module recipe used to assemble this source tree." + echo "# Not compiled, and outside the FIPS module boundary. Do not edit." + echo "FIPS_FLAVOR=$FLAVOR" + case "$FIPS_OPTION" in + cavp-selftest*) + # Not an --enable-fips value. Leave FIPS_OPTION unset so configure + # neither auto-detects nor cross-checks a selftest-only tree. + echo "SELFTEST_OPTION=$FIPS_OPTION" + ;; + *) + echo "FIPS_OPTION=$FIPS_OPTION" + ;; + esac + echo "FIPS_TAGS=${all_tags% }" + } >"$manifest" + + echo "fips-check: wrote $manifest (FIPS_OPTION=$FIPS_OPTION)" +} + +# stamp_user_settings rewrites the generated FIPS version region of the +# user_settings.h templates listed in FIPS_USER_SETTINGS, so that an IDE or +# bare-metal build of this bundle sees the same HAVE_FIPS_VERSION_* values as +# an autotools build of it. +# +# The values are read back out of the wolfssl/options.h that configure just +# generated, so there is exactly one source of truth: whatever configure +# decided for this --enable-fips option. +function stamp_user_settings() { + local options_h='wolfssl/options.h' + local begin='/* BEGIN GENERATED FIPS VERSION */' + local end='/* END GENERATED FIPS VERSION */' + local body macro line f + + if [ ! -s "$options_h" ]; then + echo "fips-check: $options_h missing, not stamping user_settings.h" 1>&2 + return 0 + fi + + body=$(mktemp "${TMPDIR:-/tmp}/fips-check.XXXXXX") || return 1 + for macro in HAVE_FIPS HAVE_FIPS_VERSION HAVE_FIPS_VERSION_MAJOR \ + HAVE_FIPS_VERSION_MINOR HAVE_FIPS_VERSION_PATCH; do + line=$(grep -E "^#define ${macro}( |\$)" "$options_h" | head -1) + if [ -n "$line" ]; then + printf '#undef %s\n%s\n' "$macro" "$line" >>"$body" + fi + done + + if [ ! -s "$body" ]; then + echo "fips-check: no HAVE_FIPS defines in $options_h, not stamping" 1>&2 + rm -f "$body" + return 0 + fi + + for f in "${FIPS_USER_SETTINGS[@]}"; do + if [ ! -f "$f" ]; then + continue + fi + if ! grep -qF "$begin" "$f"; then + echo "fips-check: $f has no generated FIPS version region" 1>&2 + rm -f "$body" + return 1 + fi + + # awk -v cannot portably carry a value containing newlines (BSD awk + # rejects it outright), so the replacement body is handed to awk by + # filename and read back inside the script. + awk -v bodyfile="$body" -v b="$begin" -v e="$end" ' + $0 == b { + print + print "#if 1" + while ((getline l < bodyfile) > 0) + print l + close(bodyfile) + print "#endif" + drop = 1 + next + } + $0 == e { drop = 0 } + drop == 0 { print } + ' "$f" >"$f.stamped" + + # Never let a silent stamping failure through: an unstamped bundle + # builds fine here and then misbuilds for whoever receives it. + if ! awk -v b="$begin" -v e="$end" \ + '$0 == b { inr = 1 } inr { print } $0 == e { inr = 0 }' \ + "$f.stamped" | grep -qF '#if 1'; then + echo "fips-check: failed to stamp FIPS version into $f" 1>&2 + rm -f "$body" "$f.stamped" + return 1 + fi + + mv "$f.stamped" "$f" + echo "fips-check: stamped FIPS version into $f" + done + + rm -f "$body" +} + + # Note, it would be cleaner to compute the tag lists using associative arrays, # but those were introduced in bash-4. It's more important to maintain backward # compatibility here. @@ -648,6 +778,10 @@ if [ "$FLAVOR" = 'fipsv2-OE-ready' ] && [ -s wolfcrypt/src/fips.c ]; then sed "s/v4.0.0-alpha/fipsv2-OE-ready/" wolfcrypt/src/fips.c.bak >wolfcrypt/src/fips.c fi +# Record which --enable-fips option matches the module sources just assembled, +# so configure can resolve a bare --enable-fips and reject a mismatched one. +write_fips_manifest .wolfssl-fips-bundle || exit 3 + # run the make test if [ "$DOAUTOGEN" = "yes" ]; then ./autogen.sh @@ -666,6 +800,10 @@ if [ "$DOCONFIGURE" = "yes" ]; then ;; esac + # configure has now derived the FIPS version macros; mirror them into the + # user_settings.h templates so IDE builds of this bundle agree with it. + stamp_user_settings || exit 3 + if ! $MAKE; then echo 'fips-check: Make failed. Debris left for analysis.' exit 3 From fa42707307477408bcbd3cd98a7f395c40591799 Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Fri, 28 Aug 2026 11:18:53 -0600 Subject: [PATCH 2/3] fips: print the FIPS module version at the end of configure --- configure.ac | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 6b7fe77c284..459af860dba 100644 --- a/configure.ac +++ b/configure.ac @@ -489,14 +489,18 @@ AC_ARG_ENABLE([fips], # further below. WOLFSSL_FIPS_BUNDLE_FILE=".wolfssl-fips-bundle" FIPS_BUNDLE_OPTION= +FIPS_BUNDLE_FLAVOR= +FIPS_AUTODETECTED=no AS_IF([test -r "$srcdir/$WOLFSSL_FIPS_BUNDLE_FILE"], - [FIPS_BUNDLE_OPTION=`sed -n 's/^FIPS_OPTION=//p' "$srcdir/$WOLFSSL_FIPS_BUNDLE_FILE" 2>/dev/null | head -1`]) + [FIPS_BUNDLE_OPTION=`sed -n 's/^FIPS_OPTION=//p' "$srcdir/$WOLFSSL_FIPS_BUNDLE_FILE" 2>/dev/null | head -1` + FIPS_BUNDLE_FLAVOR=`sed -n 's/^FIPS_FLAVOR=//p' "$srcdir/$WOLFSSL_FIPS_BUNDLE_FILE" 2>/dev/null | head -1`]) AS_CASE([$ENABLED_FIPS], [yes|auto],[ AC_MSG_CHECKING([which FIPS module this source tree contains]) AS_IF([test "x$FIPS_BUNDLE_OPTION" != "x"], [ENABLED_FIPS="$FIPS_BUNDLE_OPTION" + FIPS_AUTODETECTED=yes AC_MSG_RESULT([$ENABLED_FIPS (from $WOLFSSL_FIPS_BUNDLE_FILE)])], [AC_MSG_RESULT([unknown]) AC_MSG_ERROR([--enable-fips cannot auto-detect the FIPS version of this source tree: no $WOLFSSL_FIPS_BUNDLE_FILE manifest was found. Either this tree predates the manifest, or it is not a FIPS bundle. Pass the version explicitly, for example --enable-fips=v5 or --enable-fips=ready.])]) @@ -13794,7 +13798,7 @@ else echo " * Experimental settings: Forbidden" fi if test "$ENABLED_FIPS" = "yes"; then -echo " * FIPS: $FIPS_VERSION" +echo " * FIPS: $FIPS_VERSION (module $HAVE_FIPS_VERSION_MAJOR.$HAVE_FIPS_VERSION_MINOR.$HAVE_FIPS_VERSION_PATCH)" else echo " * FIPS: $ENABLED_FIPS" fi @@ -14145,4 +14149,28 @@ echo "---" echo "Note: Make sure your application includes \"wolfssl/options.h\" before any other wolfSSL headers." echo " You can define \"WOLFSSL_USE_OPTIONS_H\" in your application to include this automatically." +# Restate the FIPS version last, where it cannot be missed. Configuring a FIPS +# bundle against the wrong --enable-fips version is an easy and expensive +# mistake, and nothing else in the build output makes it obvious. +if test "$ENABLED_FIPS" = "yes"; then +echo "---" +echo "FIPS: building with --enable-fips=$FIPS_FLAVOR" +echo " wolfCrypt module version: $HAVE_FIPS_VERSION_MAJOR.$HAVE_FIPS_VERSION_MINOR.$HAVE_FIPS_VERSION_PATCH" +if test -n "$FIPS_BUNDLE_OPTION"; then + if test -n "$FIPS_BUNDLE_FLAVOR"; then + echo " FIPS bundle: $FIPS_BUNDLE_FLAVOR (per $WOLFSSL_FIPS_BUNDLE_FILE)" + fi + if test "$FIPS_AUTODETECTED" = "yes"; then + echo " This version was auto-detected from the bundle in this tree." + else + echo " This version matches the bundle in this tree." + fi +else + echo " WARNING: this tree has no $WOLFSSL_FIPS_BUNDLE_FILE manifest, so the" + echo " version above comes from the --enable-fips option alone and has NOT" + echo " been checked against the FIPS module sources present here. Confirm it" + echo " matches the FIPS bundle you were shipped before relying on this build." +fi +fi + fi From 3478abef79e778dfc0a9b3a61eed44d0870f679f Mon Sep 17 00:00:00 2001 From: jackctj117 Date: Fri, 28 Aug 2026 11:24:31 -0600 Subject: [PATCH 3/3] fips: show the exact module version (v5.2.3) not just the option (v5) --- configure.ac | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 459af860dba..c2f0ec0b864 100644 --- a/configure.ac +++ b/configure.ac @@ -495,6 +495,21 @@ AS_IF([test -r "$srcdir/$WOLFSSL_FIPS_BUNDLE_FILE"], [FIPS_BUNDLE_OPTION=`sed -n 's/^FIPS_OPTION=//p' "$srcdir/$WOLFSSL_FIPS_BUNDLE_FILE" 2>/dev/null | head -1` FIPS_BUNDLE_FLAVOR=`sed -n 's/^FIPS_FLAVOR=//p' "$srcdir/$WOLFSSL_FIPS_BUNDLE_FILE" 2>/dev/null | head -1`]) +# The exact module version, where the module states one. Up to FIPS v5.2.5, +# wolfCrypt_GetVersion_fips() returns a literal naming the certified module, +# e.g. "wolfCrypt v5.2.3". That is more precise than the --enable-fips option, +# because several distinct v5 modules share --enable-fips=v5 and therefore +# share one set of version macros. +# +# The "wolfCrypt " prefix is required deliberately: it selects exactly the v5 +# family. Older modules return the wolfCrypt library lineage instead ("v4.0" for +# wolfRand, "v4.0.0-alpha" for cert 3389), which is not a module version and +# would mislead. From v6.0.0 the string is built at compile time from the +# version macros, so there is no literal and the macros are authoritative. +FIPS_MODULE_VERSION= +AS_IF([test -s "$srcdir/wolfcrypt/src/fips.c"], + [FIPS_MODULE_VERSION=`sed -n 's/^ *return "wolfCrypt \(v@<:@0-9@:>@@<:@^"@:>@*\)";.*$/\1/p' "$srcdir/wolfcrypt/src/fips.c" 2>/dev/null | head -1`]) + AS_CASE([$ENABLED_FIPS], [yes|auto],[ AC_MSG_CHECKING([which FIPS module this source tree contains]) @@ -13798,7 +13813,11 @@ else echo " * Experimental settings: Forbidden" fi if test "$ENABLED_FIPS" = "yes"; then -echo " * FIPS: $FIPS_VERSION (module $HAVE_FIPS_VERSION_MAJOR.$HAVE_FIPS_VERSION_MINOR.$HAVE_FIPS_VERSION_PATCH)" +if test -n "$FIPS_MODULE_VERSION"; then +echo " * FIPS: $FIPS_VERSION (module $FIPS_MODULE_VERSION)" +else +echo " * FIPS: $FIPS_VERSION (module v$HAVE_FIPS_VERSION_MAJOR.$HAVE_FIPS_VERSION_MINOR.$HAVE_FIPS_VERSION_PATCH)" +fi else echo " * FIPS: $ENABLED_FIPS" fi @@ -14155,7 +14174,15 @@ echo " You can define \"WOLFSSL_USE_OPTIONS_H\" in your application to incl if test "$ENABLED_FIPS" = "yes"; then echo "---" echo "FIPS: building with --enable-fips=$FIPS_FLAVOR" -echo " wolfCrypt module version: $HAVE_FIPS_VERSION_MAJOR.$HAVE_FIPS_VERSION_MINOR.$HAVE_FIPS_VERSION_PATCH" +FIPS_MACRO_VERSION="v$HAVE_FIPS_VERSION_MAJOR.$HAVE_FIPS_VERSION_MINOR.$HAVE_FIPS_VERSION_PATCH" +if test -n "$FIPS_MODULE_VERSION"; then + echo " wolfCrypt module version: $FIPS_MODULE_VERSION" + if test "$FIPS_MODULE_VERSION" != "$FIPS_MACRO_VERSION"; then + echo " Version macros: $FIPS_MACRO_VERSION, as set by --enable-fips=$FIPS_FLAVOR" + fi +else + echo " wolfCrypt module version: $FIPS_MACRO_VERSION" +fi if test -n "$FIPS_BUNDLE_OPTION"; then if test -n "$FIPS_BUNDLE_FLAVOR"; then echo " FIPS bundle: $FIPS_BUNDLE_FLAVOR (per $WOLFSSL_FIPS_BUNDLE_FILE)"