Skip to content

Commit f8055b7

Browse files
committed
Fix FIPS integrity hash patching for Cargo builds on Windows
The FIPS hash injection on Windows uses capture_hash.go, which runs fips_empty_main.exe to capture the correct integrity hash, then patches it into crypto.dll. Previously this ran as a POST_BUILD on fips_empty_main — but fips_empty_main depends on crypto (not vice versa), so when Cargo's cmake crate builds with '--target install', fips_empty_main's POST_BUILD could be skipped, leaving crypto.dll with the placeholder hash. Replace the POST_BUILD with an OUTPUT-based custom command that produces a stamp file, driven by a custom target marked ALL. Since 'install' depends on 'all', the dependency chain is now: install → all → fips_integrity → stamp → capture_hash → fips_empty_main → crypto This ensures the hash injection always runs before install copies crypto.dll, regardless of how cmake is invoked.
1 parent bad0516 commit f8055b7

File tree

2 files changed

+30
-31
lines changed

2 files changed

+30
-31
lines changed

.github/workflows/aws-lc-rs.yml

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -288,26 +288,27 @@ jobs:
288288
- name: Build
289289
working-directory: ./aws-lc-rs
290290
run: cargo xwin build -p aws-lc-rs --target ${{ matrix.target }} ${{ matrix.fips && '--features fips' || '' }}
291-
- name: FIPS integrity sanity check
291+
- name: FIPS sanity test (via Wine)
292292
if: matrix.fips
293293
working-directory: ./aws-lc-rs
294294
env:
295295
WINEDEBUG: "-all"
296296
DISPLAY: ""
297297
run: |
298-
# fips_empty_main.exe is built alongside crypto.dll during the FIPS
299-
# build. It loads crypto.dll which triggers the FIPS power-on
300-
# self-test. If the integrity hash was patched correctly the self-test
301-
# passes and main() runs; if not the process aborts immediately.
302-
CRYPTO_DIR=$(dirname "$(find target -name 'crypto.dll' -path '*/aws-lc-fips-sys*' | head -1)")
303-
echo "CRYPTO_DIR=${CRYPTO_DIR}"
304-
ls -la "${CRYPTO_DIR}/crypto.dll" "${CRYPTO_DIR}/fips_empty_main.exe"
305-
export WINEPATH="$(winepath -w "${CRYPTO_DIR}")"
306-
# fips_empty_main.exe always exits with code 1 (by design), so we
307-
# capture its output without letting the non-zero exit fail the step.
308-
OUTPUT=$(wine "${CRYPTO_DIR}/fips_empty_main.exe" 2>&1 || true)
309-
echo "${OUTPUT}"
310-
echo "${OUTPUT}" | grep -q "FIPS mode"
298+
# The FIPS crypto DLL has a version prefix (e.g.,
299+
# aws_lc_fips_0_13_13_crypto.dll) and lives in the cargo build
300+
# artifacts directory — not next to the test binary. Set WINEPATH
301+
# so Wine can find it at runtime.
302+
CRYPTO_DLL=$(find target -name '*crypto.dll' -path '*/aws-lc-fips-sys*' | head -1)
303+
if [ -z "${CRYPTO_DLL}" ]; then
304+
echo "ERROR: Could not find FIPS crypto DLL"
305+
exit 1
306+
fi
307+
echo "Found FIPS DLL: ${CRYPTO_DLL}"
308+
export WINEPATH="$(winepath -w "$(dirname "${CRYPTO_DLL}")")"
309+
# Any test that loads the FIPS crypto library triggers the power-on
310+
# self-test; if the integrity hash is wrong the process aborts.
311+
cargo xwin test -p aws-lc-rs --target ${{ matrix.target }} --features fips
311312
312313
# CMake Rust bindings generation tests
313314
cmake-rust-bindings:

crypto/CMakeLists.txt

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -629,36 +629,34 @@ if(FIPS_SHARED)
629629
# Rewrite libcrypto.so, libcrypto.dylib, or crypto.dll to inject the correct module
630630
# hash value. For now we support the FIPS build only on Linux, macOS, iOS, and Windows.
631631
if(MSVC)
632-
# On Windows we use a single-DLL capture-and-patch approach: build
633-
# crypto.dll once with a placeholder hash, run fips_empty_main.exe (which
634-
# triggers the integrity check, computes the real hash, and prints it),
635-
# then binary-patch the placeholder in crypto.dll with the captured hash.
636-
# This avoids building two separate DLLs (precrypto.dll vs crypto.dll)
637-
# whose linker output may differ — e.g. mandatory ASLR on ARM64 causes
638-
# different ADRP immediates, and lld-link (clang-cl) is not guaranteed to
639-
# produce byte-identical output across two independent link operations.
632+
# On Windows we use capture_hash.go: build crypto.dll with a placeholder
633+
# hash, then run fips_empty_main.exe (which triggers the integrity check
634+
# and prints the correct hash), then patch the placeholder in crypto.dll.
635+
#
636+
# The fips_integrity target (marked ALL) ensures the hash injection runs
637+
# before 'install' copies crypto.dll. Without this, Cargo builds (which
638+
# run 'cmake --build --target install') would skip the hash injection
639+
# because fips_empty_main is not in crypto's dependency chain.
640640
build_libcrypto(NAME crypto MODULE_SOURCE $<TARGET_OBJECTS:fipsmodule> SET_OUTPUT_NAME)
641641

642642
add_executable(fips_empty_main fipsmodule/fips_empty_main.c)
643643
target_link_libraries(fips_empty_main PUBLIC crypto)
644644
target_add_awslc_include_paths(TARGET fips_empty_main SCOPE PRIVATE)
645645

646646
add_custom_command(
647-
TARGET fips_empty_main POST_BUILD
647+
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/fips_hash_injected.stamp
648648
COMMAND ${GO_EXECUTABLE} run
649649
${AWSLC_SOURCE_DIR}/util/fipstools/capture_hash/capture_hash.go
650650
-in-executable $<TARGET_FILE:fips_empty_main>
651651
-patch-dll $<TARGET_FILE:crypto>
652+
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/fips_hash_injected.stamp
653+
DEPENDS fips_empty_main crypto
654+
${AWSLC_SOURCE_DIR}/util/fipstools/capture_hash/capture_hash.go
652655
WORKING_DIRECTORY ${AWSLC_SOURCE_DIR}
653656
)
654-
655-
# Adding an install rule for fips_empty_main makes it a dependency of the
656-
# 'install' target. This is needed because Cargo's cmake crate builds via
657-
# 'cmake --build --target install', which only builds targets that have
658-
# install() rules. Without this, fips_empty_main is never built during a
659-
# Cargo build, so the POST_BUILD hash-patching step above never runs and
660-
# crypto.dll is left with the placeholder integrity hash.
661-
install(TARGETS fips_empty_main RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
657+
add_custom_target(fips_integrity ALL
658+
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/fips_hash_injected.stamp
659+
)
662660
else()
663661
# On Apple and Linux platforms inject_hash.go can parse libcrypto and inject
664662
# the hash directly into the final library.

0 commit comments

Comments
 (0)