Skip to content
Draft
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
22 changes: 22 additions & 0 deletions IDE/WIN10/user_settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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; \
Expand Down
101 changes: 99 additions & 2 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,48 @@ 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=<version> is cross-checked against it
# 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_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])
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.])])
])

FIPS_FLAVOR="$ENABLED_FIPS"
AC_SUBST([FIPS_FLAVOR])

Expand Down Expand Up @@ -576,6 +618,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"
Expand Down Expand Up @@ -797,7 +858,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])])
]
)

Expand Down Expand Up @@ -13752,7 +13813,11 @@ else
echo " * Experimental settings: Forbidden"
fi
if test "$ENABLED_FIPS" = "yes"; then
echo " * FIPS: $FIPS_VERSION"
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
Expand Down Expand Up @@ -14103,4 +14168,36 @@ 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"
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)"
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
138 changes: 138 additions & 0 deletions fips-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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=<version> 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.
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
Loading