diff --git a/CMakeLists.txt b/CMakeLists.txt index f377820..de66aeb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,18 @@ if(BUILD_TESTING) ${CMAKE_CURRENT_SOURCE_DIR}/test/test_external_dll.c ${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h) + add_custom_command( + OUTPUT ${WIBO_TEST_BIN_DIR}/test_bcrypt.exe + COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2 + -I${CMAKE_CURRENT_SOURCE_DIR}/test + -o test_bcrypt.exe + ${CMAKE_CURRENT_SOURCE_DIR}/test/test_bcrypt.c + -lbcrypt + WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR} + DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/test/test_bcrypt.c + ${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h) + add_custom_command( OUTPUT ${WIBO_TEST_BIN_DIR}/test_resources_res.o COMMAND ${WIBO_MINGW_WINDRES} @@ -105,6 +117,7 @@ if(BUILD_TESTING) DEPENDS ${WIBO_TEST_BIN_DIR}/external_exports.dll ${WIBO_TEST_BIN_DIR}/test_external_dll.exe + ${WIBO_TEST_BIN_DIR}/test_bcrypt.exe ${WIBO_TEST_BIN_DIR}/test_resources.exe) if(CMAKE_CONFIGURATION_TYPES) @@ -123,6 +136,12 @@ if(BUILD_TESTING) WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR} DEPENDS wibo.build_fixtures) + add_test(NAME wibo.test_bcrypt + COMMAND $ ${WIBO_TEST_BIN_DIR}/test_bcrypt.exe) + set_tests_properties(wibo.test_bcrypt PROPERTIES + WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR} + DEPENDS wibo.build_fixtures) + add_test(NAME wibo.test_resources COMMAND $ ${WIBO_TEST_BIN_DIR}/test_resources.exe) set_tests_properties(wibo.test_resources PROPERTIES diff --git a/common.h b/common.h index 44a7d3a..b132019 100644 --- a/common.h +++ b/common.h @@ -81,6 +81,8 @@ typedef unsigned char BYTE; typedef int NTSTATUS; #define STATUS_SUCCESS ((NTSTATUS)0x00000000) #define STATUS_INVALID_HANDLE ((NTSTATUS)0xC0000008) +#define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000D) +#define STATUS_NOT_IMPLEMENTED ((NTSTATUS)0xC0000002) #define STATUS_END_OF_FILE ((NTSTATUS)0xC0000011) #define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BB) #define STATUS_UNEXPECTED_IO_ERROR ((NTSTATUS)0xC00000E9) diff --git a/dll/bcrypt.cpp b/dll/bcrypt.cpp index a2dc27b..054e30b 100644 --- a/dll/bcrypt.cpp +++ b/dll/bcrypt.cpp @@ -1,21 +1,64 @@ #include "common.h" -#include -#include -#include +#include +#include + +namespace { typedef PVOID BCRYPT_ALG_HANDLE; -namespace bcrypt { +constexpr ULONG BCRYPT_RNG_USE_ENTROPY_IN_BUFFER = 0x00000001; +constexpr ULONG BCRYPT_USE_SYSTEM_PREFERRED_RNG = 0x00000002; -using random_bytes_engine = std::independent_bits_engine; +bool fillWithSystemRandom(PUCHAR buffer, size_t length) { + while (length > 0) { + ssize_t written = getrandom(buffer, length, 0); + if (written < 0) { + if (errno == EINTR) + continue; + return false; + } + if (written == 0) + continue; + buffer += written; + length -= static_cast(written); + } + return true; +} + +} // namespace + +namespace bcrypt { NTSTATUS WIN_FUNC BCryptGenRandom(BCRYPT_ALG_HANDLE hAlgorithm, PUCHAR pbBuffer, ULONG cbBuffer, ULONG dwFlags) { DEBUG_LOG("BCryptGenRandom(%p, %p, %lu, %lu)\n", hAlgorithm, pbBuffer, cbBuffer, dwFlags); - assert(hAlgorithm == nullptr); - assert(dwFlags == 0 || dwFlags == 2 /* BCRYPT_USE_SYSTEM_PREFERRED_RNG */); - random_bytes_engine rbe; - std::generate(pbBuffer, pbBuffer + cbBuffer, std::ref(rbe)); + if (pbBuffer == nullptr && cbBuffer != 0) + return STATUS_INVALID_HANDLE; + + if (hAlgorithm != nullptr) + return STATUS_NOT_IMPLEMENTED; + + if ((dwFlags & BCRYPT_USE_SYSTEM_PREFERRED_RNG) == 0) + return STATUS_INVALID_HANDLE; + + ULONG allowedFlags = BCRYPT_RNG_USE_ENTROPY_IN_BUFFER | BCRYPT_USE_SYSTEM_PREFERRED_RNG; + if ((dwFlags & ~allowedFlags) != 0) + return STATUS_INVALID_PARAMETER; + + if (cbBuffer == 0) + return STATUS_SUCCESS; + + std::vector entropy; + if ((dwFlags & BCRYPT_RNG_USE_ENTROPY_IN_BUFFER) && pbBuffer != nullptr) + entropy.assign(pbBuffer, pbBuffer + cbBuffer); + + if (!fillWithSystemRandom(pbBuffer, cbBuffer)) + return STATUS_UNEXPECTED_IO_ERROR; + + if (!entropy.empty()) { + for (size_t i = 0; i < entropy.size(); ++i) + pbBuffer[i] ^= entropy[i]; + } return STATUS_SUCCESS; } diff --git a/dll/crt.cpp b/dll/crt.cpp index 99b8b3a..ec5dd16 100644 --- a/dll/crt.cpp +++ b/dll/crt.cpp @@ -132,6 +132,12 @@ void WIN_ENTRY free(void *ptr) { ::free(ptr); } void *WIN_ENTRY memcpy(void *dest, const void *src, size_t count) { return std::memcpy(dest, src, count); } +void *WIN_ENTRY memmove(void *dest, const void *src, size_t count) { return std::memmove(dest, src, count); } + +void *WIN_ENTRY memset(void *dest, int ch, size_t count) { return std::memset(dest, ch, count); } + +int WIN_ENTRY memcmp(const void *lhs, const void *rhs, size_t count) { return std::memcmp(lhs, rhs, count); } + int WIN_ENTRY __setusermatherr(void *handler) { DEBUG_LOG("STUB: __setusermatherr(%p)\n", handler); return 0; @@ -266,6 +272,12 @@ static void *resolveByName(const char *name) { return (void *)crt::free; if (strcmp(name, "memcpy") == 0) return (void *)crt::memcpy; + if (strcmp(name, "memmove") == 0) + return (void *)crt::memmove; + if (strcmp(name, "memset") == 0) + return (void *)crt::memset; + if (strcmp(name, "memcmp") == 0) + return (void *)crt::memcmp; if (strcmp(name, "exit") == 0) return (void *)crt::exit; if (strcmp(name, "_cexit") == 0) diff --git a/dll/msvcrt.cpp b/dll/msvcrt.cpp index b8f17d0..6a42000 100644 --- a/dll/msvcrt.cpp +++ b/dll/msvcrt.cpp @@ -570,6 +570,10 @@ char* WIN_ENTRY setlocale(int category, const char *locale){ return std::memmove(dest, src, count); } + int WIN_ENTRY memcmp(const void *lhs, const void *rhs, size_t count) { + return std::memcmp(lhs, rhs, count); + } + int WIN_ENTRY fflush(FILE *stream) { return std::fflush(stream); } @@ -1485,6 +1489,7 @@ static void *resolveByName(const char *name) { if (strcmp(name, "memset") == 0) return (void*)msvcrt::memset; if (strcmp(name, "memcpy") == 0) return (void*)msvcrt::memcpy; if (strcmp(name, "memmove") == 0) return (void*)msvcrt::memmove; + if (strcmp(name, "memcmp") == 0) return (void*)msvcrt::memcmp; if (strcmp(name, "fflush") == 0) return (void*)msvcrt::fflush; if (strcmp(name, "fopen") == 0) return (void*)msvcrt::fopen; if (strcmp(name, "fseek") == 0) return (void*)msvcrt::fseek; diff --git a/test/test_bcrypt.c b/test/test_bcrypt.c new file mode 100644 index 0000000..5e1146a --- /dev/null +++ b/test/test_bcrypt.c @@ -0,0 +1,67 @@ +#define WIN32_NO_STATUS +#include +#undef WIN32_NO_STATUS +#include +#include + +#include "test_assert.h" +#include +#include +#include + +#ifndef NT_SUCCESS +#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) +#endif + +static void expect_success(NTSTATUS status) { + TEST_CHECK_MSG(NT_SUCCESS(status), "Expected success NTSTATUS, got 0x%08lx", (unsigned long)status); +} + +int main(void) { + UCHAR temp[32] = {0}; + const UCHAR zero_block[32] = {0}; + NTSTATUS status = BCryptGenRandom(NULL, temp, sizeof(temp), 0); + TEST_CHECK_EQ(STATUS_INVALID_HANDLE, status); + + UCHAR first[32] = {0}; + status = BCryptGenRandom(NULL, first, sizeof(first), BCRYPT_USE_SYSTEM_PREFERRED_RNG); + expect_success(status); + TEST_CHECK_MSG(memcmp(first, zero_block, sizeof(first)) != 0, + "BCryptGenRandom with system RNG flag left buffer zeroed"); + + UCHAR second[32] = {0}; + status = BCryptGenRandom(NULL, second, sizeof(second), BCRYPT_USE_SYSTEM_PREFERRED_RNG); + expect_success(status); + TEST_CHECK_MSG(memcmp(second, zero_block, sizeof(second)) != 0, + "BCryptGenRandom produced zeroed buffer on repeat call"); + TEST_CHECK_MSG(memcmp(first, second, sizeof(first)) != 0, + "BCryptGenRandom produced identical buffers across calls"); + + UCHAR entropy_buffer[32]; + UCHAR entropy_original[32]; + memset(entropy_buffer, 0x5a, sizeof(entropy_buffer)); + memcpy(entropy_original, entropy_buffer, sizeof(entropy_buffer)); + status = BCryptGenRandom(NULL, entropy_buffer, sizeof(entropy_buffer), + BCRYPT_RNG_USE_ENTROPY_IN_BUFFER | BCRYPT_USE_SYSTEM_PREFERRED_RNG); + expect_success(status); + TEST_CHECK_MSG(memcmp(entropy_buffer, entropy_original, sizeof(entropy_buffer)) != 0, + "Entropy flag did not modify buffer"); + + status = BCryptGenRandom((BCRYPT_ALG_HANDLE)0x1, first, sizeof(first), 0); + TEST_CHECK_EQ(STATUS_NOT_IMPLEMENTED, status); + + status = BCryptGenRandom(NULL, first, sizeof(first), 0x4); + TEST_CHECK_EQ(STATUS_INVALID_HANDLE, status); + + status = BCryptGenRandom((BCRYPT_ALG_HANDLE)0x1, first, sizeof(first), BCRYPT_USE_SYSTEM_PREFERRED_RNG); + TEST_CHECK_EQ(STATUS_NOT_IMPLEMENTED, status); + + status = BCryptGenRandom(NULL, NULL, sizeof(first), 0); + TEST_CHECK_EQ(STATUS_INVALID_HANDLE, status); + + status = BCryptGenRandom(NULL, NULL, 0, 0); + TEST_CHECK_EQ(STATUS_INVALID_HANDLE, status); + + printf("bcrypt_gen_random: passed\n"); + return EXIT_SUCCESS; +}