Skip to content
Open
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
32 changes: 10 additions & 22 deletions crypto/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -629,34 +629,22 @@ if(FIPS_SHARED)
# Rewrite libcrypto.so, libcrypto.dylib, or crypto.dll to inject the correct module
# hash value. For now we support the FIPS build only on Linux, macOS, iOS, and Windows.
if(MSVC)
# On Windows we use capture_hash.go: build crypto.dll with a placeholder
# hash, then run fips_empty_main.exe (which triggers the integrity check
# and prints the correct hash), then patch the placeholder in crypto.dll.
#
# The fips_integrity target (marked ALL) ensures the hash injection runs
# before 'install' copies crypto.dll. Without this, Cargo builds (which
# run 'cmake --build --target install') would skip the hash injection
# because fips_empty_main is not in crypto's dependency chain.
# On Windows, inject_hash.go parses the linker map file and the PE to locate
# module boundaries, computes the integrity hash, and patches it directly
# into the DLL. This matches the Linux/Apple approach and does not require
# running a binary, enabling cross-compilation without Wine.
set(CRYPTO_MAP_FILE "${CMAKE_CURRENT_BINARY_DIR}/fips_crypto.map")
build_libcrypto(NAME crypto MODULE_SOURCE $<TARGET_OBJECTS:fipsmodule> SET_OUTPUT_NAME)

add_executable(fips_empty_main fipsmodule/fips_empty_main.c)
target_link_libraries(fips_empty_main PUBLIC crypto)
target_add_awslc_include_paths(TARGET fips_empty_main SCOPE PRIVATE)
target_link_options(crypto PRIVATE "/MAP:${CRYPTO_MAP_FILE}")

add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/fips_hash_injected.stamp
TARGET crypto POST_BUILD
COMMAND ${GO_EXECUTABLE} run
${AWSLC_SOURCE_DIR}/util/fipstools/capture_hash/capture_hash.go
-in-executable $<TARGET_FILE:fips_empty_main>
-patch-dll $<TARGET_FILE:crypto>
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/fips_hash_injected.stamp
DEPENDS fips_empty_main crypto
${AWSLC_SOURCE_DIR}/util/fipstools/capture_hash/capture_hash.go
${AWSLC_SOURCE_DIR}/util/fipstools/inject_hash/inject_hash.go
-o $<TARGET_FILE:crypto> -in-object $<TARGET_FILE:crypto>
-map ${CRYPTO_MAP_FILE} -windows
WORKING_DIRECTORY ${AWSLC_SOURCE_DIR}
)
add_custom_target(fips_integrity ALL
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/fips_hash_injected.stamp
)
else()
# On Apple and Linux platforms inject_hash.go can parse libcrypto and inject
# the hash directly into the final library.
Expand Down
22 changes: 13 additions & 9 deletions crypto/fipsmodule/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -619,26 +619,30 @@ elseif(FIPS_SHARED)
separate_arguments(FIPS_MARKER_C_FLAGS NATIVE_COMMAND "${CMAKE_C_FLAGS}")
add_custom_command(
OUTPUT fips_msvc_start.obj
COMMAND ${CMAKE_C_COMPILER} ${FIPS_MARKER_C_FLAGS} -w /nologo /c /DAWSLC_FIPS_SHARED_START /Fo:fips_msvc_start.obj ${CMAKE_CURRENT_SOURCE_DIR}/fips_shared_library_marker.c
COMMAND ${CMAKE_C_COMPILER} ${FIPS_MARKER_C_FLAGS} -w /nologo /c /DAWSLC_FIPS_SHARED_START /Fofips_msvc_start.obj ${CMAKE_CURRENT_SOURCE_DIR}/fips_shared_library_marker.c
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/fips_shared_library_marker.c
)
add_custom_command(
OUTPUT fips_msvc_end.obj
COMMAND ${CMAKE_C_COMPILER} ${FIPS_MARKER_C_FLAGS} -w /nologo /c /DAWSLC_FIPS_SHARED_END /Fo:fips_msvc_end.obj ${CMAKE_CURRENT_SOURCE_DIR}/fips_shared_library_marker.c
COMMAND ${CMAKE_C_COMPILER} ${FIPS_MARKER_C_FLAGS} -w /nologo /c /DAWSLC_FIPS_SHARED_END /Fofips_msvc_end.obj ${CMAKE_CURRENT_SOURCE_DIR}/fips_shared_library_marker.c
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/fips_shared_library_marker.c
)
if(CMAKE_AR)
set(MSVC_LIB "${CMAKE_AR}")
else()
get_filename_component(MSVC_BIN ${CMAKE_LINKER} DIRECTORY)
set(MSVC_LIB "${MSVC_BIN}/lib.exe")
get_filename_component(MSVC_BIN ${CMAKE_LINKER} DIRECTORY)
find_program(MSVC_LIB NAMES lib lib.exe llvm-lib llvm-lib.exe
HINTS ${MSVC_BIN} NO_DEFAULT_PATH)
if(NOT MSVC_LIB)
find_program(MSVC_LIB NAMES lib lib.exe llvm-lib llvm-lib.exe)
endif()
if(NOT MSVC_LIB)
message(FATAL_ERROR "Could not find lib.exe or llvm-lib for creating bcm.lib")
endif()
file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/bcm_objects.rsp"
CONTENT "$<JOIN:$<TARGET_OBJECTS:bcm_library>,\n>")
add_custom_command(
OUTPUT ${BCM_NAME}
COMMAND ${MSVC_LIB} /nologo fips_msvc_start.obj $<TARGET_OBJECTS:bcm_library> fips_msvc_end.obj /OUT:${BCM_NAME}
COMMAND_EXPAND_LISTS
COMMAND ${MSVC_LIB} /nologo fips_msvc_start.obj @bcm_objects.rsp fips_msvc_end.obj /OUT:${BCM_NAME}
DEPENDS fips_msvc_start.obj fips_msvc_end.obj bcm_library
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
Expand Down
18 changes: 4 additions & 14 deletions crypto/fipsmodule/FIPS.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,20 +163,10 @@ The Shared Windows FIPS integrity test differs in two key ways:
2. How the correct integrity hash is calculated
Microsoft Visual C compiler (MSVC) does not support linker scripts that add symbols to mark the start and end of the text and rodata sections, as is done on Linux. Instead, `fips_shared_library_marker.c` is compiled twice to generate two object files that contain start/end functions and variables. MSVC `pragma` segment definitions are used to place the markers in specific sections (e.g. `.fipstx$a`). This particular name format uses [Portable Executable Grouped Sections](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#grouped-sections-object-only) to control what section the code is placed in and the order within the section. With the start and end markers placed at `$a` and `$z` respectively, BCM puts everything in the `$b` section. When the final crypto.dll is built, all the code is in the `.fipstx` section, all data is in `.fipsda`, all constants are in `.fipsco`, all uninitialized items in `.fipsbs`, and everything is in the correct order.
The process to generate the expected integrity fingerprint is also different from Linux. We use a single-DLL capture-and-patch approach: build `crypto.dll` once with a placeholder hash, run it to compute the real hash, then binary-patch the placeholder directly in the DLL. This avoids building two separate DLLs whose linker output may differ (e.g. mandatory ASLR on ARM64 causes different ADRP immediates, and `lld-link` used by clang-cl is not guaranteed to produce byte-identical output across two independent link operations).
1. Build the required object files once: `bcm.obj` from `bcm.c` and the start/end object files
1. `bcm.obj` places the power-on self tests in the `.CRT$XCU` section which is run automatically by the Windows Common Runtime library (CRT) startup code
2. Use MSVC's `lib.exe` (or `llvm-lib` for clang-cl) to combine the start/end object files with `bcm.obj` to create the static library `bcm.lib`.
1. MSVC does not support combining multiple object files into another object file like the Apple build.
3. Build `fipsmodule` which contains the placeholder integrity hash
4. Build `crypto.dll` with `bcm.lib` and `fipsmodule`
5. Build the small application `fips_empty_main.exe` and link it with `crypto.dll`
6. `capture_hash.go` runs `fips_empty_main.exe`
1. The CRT runs all functions in the `.CRT$XC*` sections in order starting with `.CRT$XCA`
2. The BCM power-on tests are in `.CRT$XCU` and are run after all other Windows initialization is complete
3. BCM calculates the correct integrity value which will not match the placeholder value. Before aborting the process the correct value is printed
4. `capture_hash.go` reads the correct integrity value and binary-patches the 32-byte placeholder directly in `crypto.dll`
The process to generate the expected integrity fingerprint follows the same approach as Linux and Apple, using `inject_hash.go` to patch the hash directly into the final binary:
1. Build `crypto.dll` from `bcm.c`, the start/end marker objects, and the rest of `fipsmodule`, using the `/MAP:` linker flag to produce a linker map file
2. `inject_hash.go -windows` parses the linker map file and the PE to locate the FIPS module boundaries via the marker symbols, computes the integrity hash over the module text and rodata, and patches the correct value directly into `crypto.dll`
### Linux Static build
Expand Down
20 changes: 0 additions & 20 deletions crypto/fipsmodule/fips_empty_main.c

This file was deleted.

210 changes: 0 additions & 210 deletions util/fipstools/capture_hash/capture_hash.go

This file was deleted.

Loading