diff --git a/.github/workflows/test-cpp-inject-hash.yml b/.github/workflows/test-cpp-inject-hash.yml new file mode 100644 index 0000000000..ee931e17e5 --- /dev/null +++ b/.github/workflows/test-cpp-inject-hash.yml @@ -0,0 +1,44 @@ +name: Test C++ inject_hash Implementation + +on: + push: + branches: [ hmac_sha_implementation ] + pull_request: + branches: [ inject-hash-cpp-experiment ] + +jobs: + test-cpp-inject-hash: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest] + # Don't cancel all jobs if one fails + fail-fast: false + + steps: + - uses: actions/checkout@v2 + with: + submodules: 'recursive' + + - name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: '1.20' + + - name: Install Dependencies (Ubuntu) + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y ninja-build cmake perl golang + + - name: Install Dependencies (macOS) + if: runner.os == 'macOS' + run: | + brew install ninja cmake perl go + + - name: Build and Test + run: | + chmod +x tests/ci/run_inject_hash_cpp.sh + ./tests/ci/run_inject_hash_cpp.sh + + diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..ce4702d2c8 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "third_party/lief"] + path = third_party/lief + url = https://github.com/lief-project/LIEF.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b27c5c7ae..943c5bba41 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,31 @@ cmake_minimum_required(VERSION 3.5..3.31) + +#################### +if(USE_CPP_INJECT_HASH) + # Configure LIEF build options first + set(LIEF_PYTHON_API OFF CACHE BOOL "") + set(LIEF_EXAMPLES OFF CACHE BOOL "") + set(LIEF_TESTS OFF CACHE BOOL "") + set(LIEF_DOC OFF CACHE BOOL "") + set(LIEF_INSTALL ON CACHE BOOL "") + set(LIEF_STATIC ON CACHE BOOL "") + set(LIEF_ELF ON CACHE BOOL "Enable ELF format") + set(LIEF_PE ON CACHE BOOL "Enable PE format") + set(LIEF_MACHO ON CACHE BOOL "Enable MACHO format") + set(LIEF_DEX OFF CACHE BOOL "Disable DEX format") + set(LIEF_VDEX OFF CACHE BOOL "Disable VDEX format") + set(LIEF_ART OFF CACHE BOOL "Disable ART format") + set(LIEF_OAT OFF CACHE BOOL "Disable OAT format") + + # Add LIEF subdirectory + add_subdirectory(third_party/lief) + + # Make LIEF headers available + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/third_party/lief/include) +endif() +################### + if(POLICY CMP0091) cmake_policy(SET CMP0091 NEW) endif() diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index a2866c7702..2450f7fbfe 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -324,6 +324,12 @@ else() file(COPY ${GENERATE_CODE_ROOT}/err_data.c DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) endif() +if(FIPS) + add_library(generated_err_data OBJECT err_data.c) + target_include_directories(generated_err_data PRIVATE ${PROJECT_SOURCE_DIR}/include) +endif() + + set(CRYPTO_ARCH_OBJECTS "") if (ARCH STREQUAL "aarch64" AND CMAKE_GENERATOR MATCHES "Visual Studio") msbuild_aarch64_asm(TARGET crypto_objects ASM_FILES ${CRYPTO_ARCH_SOURCES} OUTPUT_OBJECTS CRYPTO_ARCH_OBJECTS) @@ -616,6 +622,7 @@ endfunction() 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. + add_subdirectory(fips_hashing) if(MSVC) # On Windows we use capture_hash.go to capture the computed integrity value that bcm.o prints to generate the # correct value in generated_fips_shared_support.c. See FIPS.md for a full explanation of the process @@ -652,6 +659,22 @@ if(FIPS_SHARED) set(INJECT_HASH_APPLE_FLAG "-apple") endif() + if(USE_CPP_INJECT_HASH) + # add_subdirectory(${PROJECT_SOURCE_DIR}/util/fipstools/inject_hash_cpp + # ${CMAKE_BINARY_DIR}/util/fipstools/inject_hash_cpp) + # Add dependency on C++ implementation + add_dependencies(crypto inject_hash_cpp) + + # C++ implementation for hash injection + add_custom_command( + TARGET crypto POST_BUILD + COMMAND ${CMAKE_BINARY_DIR}/util/fipstools/inject_hash_cpp/inject_hash_cpp + -o $ + -in-object $ + ${INJECT_HASH_APPLE_FLAG} + WORKING_DIRECTORY ${AWSLC_SOURCE_DIR} + ) + else() add_custom_command( TARGET crypto POST_BUILD COMMAND ${GO_EXECUTABLE} run @@ -659,7 +682,7 @@ if(FIPS_SHARED) -o $ -in-object $ ${INJECT_HASH_APPLE_FLAG} WORKING_DIRECTORY ${AWSLC_SOURCE_DIR} ) - + endif() # On macOS 11 and higher on Apple Silicon, codesigning is mandatory for # binaries to run. This applies to both executables and dylibs. An ad-hoc # signature is sufficient, and the linker will automatically apply one when diff --git a/crypto/fips_hashing/CMakeLists.txt b/crypto/fips_hashing/CMakeLists.txt new file mode 100644 index 0000000000..147912d74f --- /dev/null +++ b/crypto/fips_hashing/CMakeLists.txt @@ -0,0 +1,24 @@ +if(FIPS) + add_definitions(-DOPENSSL_NO_ASM=1) + remove_definitions(-DBORINGSSL_FIPS -DFIPS_ENTROPY_SOURCE_JITTER_CPU -DFIPS_ENTROPY_SOURCE_PASSIVE) + + add_library( + fips_hashing + + STATIC + + fips_hashing.c + + ../mem.c + ../thread_none.c + ../thread_pthread.c + + ../err/err.c + $ + ../decrepit/ripemd/ripemd.c + ) + + SET_TARGET_PROPERTIES(fips_hashing PROPERTIES LINKER_LANGUAGE C) + target_include_directories(fips_hashing PUBLIC $) +endif() + diff --git a/crypto/fips_hashing/fips_hashing.c b/crypto/fips_hashing/fips_hashing.c new file mode 100644 index 0000000000..7a6c6f1445 --- /dev/null +++ b/crypto/fips_hashing/fips_hashing.c @@ -0,0 +1,14 @@ +#include "../fipsmodule/delocate.h" + + +#include "../fipsmodule/digest/digest.c" +#include "../fipsmodule/digest/digests.c" +#include "../fipsmodule/hmac/hmac.c" +#include "../fipsmodule/md4/md4.c" +#include "../fipsmodule/md5/md5.c" +#include "../fipsmodule/sha/keccak1600.c" +#include "../fipsmodule/sha/sha1.c" +#include "../fipsmodule/sha/sha256.c" +#include "../fipsmodule/sha/sha3.c" +#include "../fipsmodule/sha/sha512.c" + diff --git a/crypto/fipsmodule/CMakeLists.txt b/crypto/fipsmodule/CMakeLists.txt index 01bf42c4f6..6ba58e758d 100644 --- a/crypto/fipsmodule/CMakeLists.txt +++ b/crypto/fipsmodule/CMakeLists.txt @@ -390,14 +390,25 @@ if(FIPS_DELOCATE) set_target_properties(bcm_hashunset PROPERTIES POSITION_INDEPENDENT_CODE ON) set_target_properties(bcm_hashunset PROPERTIES LINKER_LANGUAGE C) - go_executable(inject_hash - boringssl.googlesource.com/boringssl/util/fipstools/inject_hash) - add_custom_command( - OUTPUT bcm.o - COMMAND ./inject_hash -o bcm.o -in-archive $ - DEPENDS bcm_hashunset inject_hash - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - ) + if(USE_CPP_INJECT_HASH) + add_custom_command( + OUTPUT bcm.o + COMMAND ./inject_hash_cpp + -o bcm.o + -in-archive $ + DEPENDS bcm_hashunset inject_hash_cpp + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) +else() + go_executable(inject_hash + boringssl.googlesource.com/boringssl/util/fipstools/inject_hash) + add_custom_command( + OUTPUT bcm.o + COMMAND ./inject_hash -o bcm.o -in-archive $ + DEPENDS bcm_hashunset inject_hash + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) +endif() # The outputs of add_custom_command cannot be referenced outside of the # CMakeLists.txt that defines it. Thus we have to wrap bcm.o in a custom target diff --git a/crypto/fipsmodule/evp/p_hmac.c b/crypto/fipsmodule/evp/p_hmac.c index 6be4583ba3..7aa8cc2ae5 100644 --- a/crypto/fipsmodule/evp/p_hmac.c +++ b/crypto/fipsmodule/evp/p_hmac.c @@ -183,10 +183,6 @@ DEFINE_METHOD_FUNCTION(EVP_PKEY_METHOD, EVP_PKEY_hmac_pkey_meth) { out->ctrl_str = hmac_ctrl_str; } -int used_for_hmac(EVP_MD_CTX *ctx) { - return ctx->flags == EVP_MD_CTX_HMAC && ctx->pctx != NULL; -} - HMAC_KEY *HMAC_KEY_new(void) { HMAC_KEY *key = OPENSSL_zalloc(sizeof(HMAC_KEY)); if (key == NULL) { diff --git a/crypto/fipsmodule/hmac/hmac.c b/crypto/fipsmodule/hmac/hmac.c index 3d6b3626e9..9139fe1f80 100644 --- a/crypto/fipsmodule/hmac/hmac.c +++ b/crypto/fipsmodule/hmac/hmac.c @@ -681,3 +681,7 @@ int HMAC_CTX_copy(HMAC_CTX *dest, const HMAC_CTX *src) { HMAC_CTX_init(dest); return HMAC_CTX_copy_ex(dest, src); } + +int used_for_hmac(EVP_MD_CTX *ctx) { + return ctx->flags == EVP_MD_CTX_HMAC && ctx->pctx != NULL; +} diff --git a/tests/ci/run_inject_hash_cpp.sh b/tests/ci/run_inject_hash_cpp.sh new file mode 100755 index 0000000000..f0fd8dcc2d --- /dev/null +++ b/tests/ci/run_inject_hash_cpp.sh @@ -0,0 +1,71 @@ +#!/bin/bash +set -exo pipefail + +# Get directory containing this script +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +cd "${DIR}/../.." + +# Print environment info +echo "Environment:" +go version +cmake --version +ninja --version + +# Build AWS-LC with C++ inject_hash +mkdir -p build +cd build + +# Configure +if [[ "$OSTYPE" == "darwin"* ]]; then + cmake -GNinja \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DFIPS=1 \ + -DFIPS_SHARED=1 \ + -DBUILD_SHARED_LIBS=1 \ + -DUSE_CPP_INJECT_HASH=ON \ + -DCMAKE_OSX_SYSROOT=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk \ + .. +else + cmake -GNinja \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DFIPS=1 \ + -DFIPS_SHARED=1 \ + -DBUILD_SHARED_LIBS=1 \ + -DUSE_CPP_INJECT_HASH=ON \ + .. +fi + +# First try to build just inject_hash_cpp +echo "Building inject_hash_cpp..." +ninja inject_hash_cpp + +# Test 1: Check if executable runs +echo "Test 1: Basic execution" +if ./util/fipstools/inject_hash_cpp/inject_hash_cpp; then + echo "Should have failed without arguments" +else + echo "Correctly failed without arguments" +fi + +# Test 2: Check -in-object parameter +echo "Test 2: Testing with test file" +touch test.bin +if ./util/fipstools/inject_hash_cpp/inject_hash_cpp -in-object test.bin; then + echo "Successfully processed test file" +else + echo "Failed to process test file" +fi + +echo "=== Tests Complete ===" +# Build everything +echo "Building full project..." +ninja + +# Test actual FIPS injection +echo "Testing FIPS injection..." +if [[ "$OSTYPE" == "darwin"* ]]; then + ./util/fipstools/inject_hash_cpp/inject_hash_cpp -o ./crypto/libcrypto.dylib -in-object ./crypto/libcrypto.dylib -apple +else + ./util/fipstools/inject_hash_cpp/inject_hash_cpp -o ./crypto/libcrypto.so -in-object ./crypto/libcrypto.so +fi + diff --git a/third_party/lief b/third_party/lief new file mode 160000 index 0000000000..966a66c78a --- /dev/null +++ b/third_party/lief @@ -0,0 +1 @@ +Subproject commit 966a66c78a498ed694f28a25b350853852612c4d diff --git a/util/fipstools/CMakeLists.txt b/util/fipstools/CMakeLists.txt index 6b714bd4b4..f92c51b2a9 100644 --- a/util/fipstools/CMakeLists.txt +++ b/util/fipstools/CMakeLists.txt @@ -8,4 +8,9 @@ if(FIPS AND BUILD_TESTING) target_include_directories(test_fips BEFORE PRIVATE ${AWSLC_BINARY_DIR}/symbol_prefix_include) add_subdirectory(inject_hash/macho_parser/tests) + + endif() + if(USE_CPP_INJECT_HASH) + add_subdirectory(inject_hash_cpp) + endif() \ No newline at end of file diff --git a/util/fipstools/inject_hash_cpp/CMakeLists.txt b/util/fipstools/inject_hash_cpp/CMakeLists.txt new file mode 100644 index 0000000000..0155371739 --- /dev/null +++ b/util/fipstools/inject_hash_cpp/CMakeLists.txt @@ -0,0 +1,28 @@ +if(USE_CPP_INJECT_HASH) + add_executable(inject_hash_cpp + inject_hash.cpp + ) + + target_include_directories(inject_hash_cpp PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/include + ) + + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + target_compile_options(inject_hash_cpp PRIVATE + -Wno-overloaded-virtual + -Wno-unused-parameter + ) + endif() + + target_link_libraries(inject_hash_cpp PRIVATE + LIEF::LIEF + fips_hashing + ) + + set_target_properties(inject_hash_cpp PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED ON + ) +endif() \ No newline at end of file diff --git a/util/fipstools/inject_hash_cpp/inject_hash.cpp b/util/fipstools/inject_hash_cpp/inject_hash.cpp new file mode 100644 index 0000000000..341d977ebc --- /dev/null +++ b/util/fipstools/inject_hash_cpp/inject_hash.cpp @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv) { + std::cout << "\n=== C++ inject_hash starting ===" << std::endl; + std::cout << "Testing LIEF integration..." << std::endl; + + const char* binary_path = nullptr; + for (int i = 1; i < argc - 1; i++) { + if (strcmp(argv[i], "-in-object") == 0) { + binary_path = argv[i + 1]; + break; + } + } + if (!binary_path) { + std::cerr << "Error: -in-object argument not provided" << std::endl; + return 1; + } + + + + std::cout << "Binary Path Loaded" << binary_path << std::endl; + if (auto binary = LIEF::Parser::parse(binary_path)) { + std::cout << "LIEF parser successfully loaded: " << binary_path << std::endl; + } + else { + std::cerr << "LIEF parser failed to load: " << binary_path << std::endl; + return 1; + } + std::cout << "LIEF parser loaded successfully" << std::endl; + + uint8_t zero_key[64] = {0}; + HMAC_CTX ctx; + + if (!HMAC_Init(&ctx, &zero_key, sizeof(zero_key), EVP_sha256())) { + std::cerr << "HMAC_Init failed" << std::endl; + return 1; + } + if (!HMAC_Update(&ctx, (const uint8_t*)binary_path, strlen(binary_path))) { + std::cerr << "HMAC_Update failed" << std::endl; + return 1; + } + std::vector calculate_hash(HMAC_size(&ctx)); + + unsigned int calculate_hash_len; + if (!HMAC_Final(&ctx, calculate_hash.data(), &calculate_hash_len)) { + std::cerr << "HMAC_Final failed" << std::endl; + } + std::cout << "HMAC hash calculated successfully, the hash is: " << std::endl; + for (unsigned int i = 0; i < calculate_hash_len; i++) { + printf("%02x", calculate_hash[i]); + } + std::cout << std::endl; + + std::cout << "=== C++ inject_hash completed ===" << std::endl; + return 0; +} +