From 596c443112d09506c3bf13ac98046a84b912e56c Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Mon, 9 Jun 2025 13:23:49 +0100 Subject: Use Windows library for cryptographic checksums Add a new feature to libbb, FEATURE_USE_CNG_API, which enables the use of the Cryptography API: Next Generation library to calculate checksums. It is disabled by default except in the mingw64u default config, as the API requires Windows 10+ to function. Usage of this API provides a size benefit and delegates hardware optimizations to the operating system cryptography library. Based on GitHub PR #498 by rfl890. Saves 4064 bytes in the mingw64u case. --- Makefile.flags | 2 +- configs/mingw32_defconfig | 7 +++- configs/mingw64_defconfig | 7 +++- configs/mingw64a_defconfig | 7 +++- configs/mingw64u_defconfig | 7 +++- include/libbb.h | 37 +++++++++++++++++++ libbb/Config.src | 10 +++++ libbb/hash_md5_sha.c | 86 +++++++++++++++++++++++++++++++++++++++++++ scripts/mk_mingw64u_defconfig | 1 + 9 files changed, 155 insertions(+), 9 deletions(-) diff --git a/Makefile.flags b/Makefile.flags index bf094ec5e..a26e61e7b 100644 --- a/Makefile.flags +++ b/Makefile.flags @@ -164,7 +164,7 @@ endif endif EXEEXT = .exe -LDLIBS += ws2_32 +LDLIBS += ws2_32 bcrypt endif ifneq ($(CONFIG_PLATFORM_MINGW32),y) diff --git a/configs/mingw32_defconfig b/configs/mingw32_defconfig index d56dd1207..8859b9811 100644 --- a/configs/mingw32_defconfig +++ b/configs/mingw32_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Busybox version: 1.37.0.git -# Fri Jun 14 12:24:50 2024 +# Busybox version: 1.38.0.git +# Mon Jun 9 11:54:15 2025 # CONFIG_HAVE_DOT_CONFIG=y # CONFIG_PLATFORM_POSIX is not set @@ -121,6 +121,7 @@ CONFIG_FEATURE_BUFFERS_USE_MALLOC=y # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set CONFIG_PASSWORD_MINLEN=6 +# CONFIG_FEATURE_USE_CNG_API is not set CONFIG_MD5_SMALL=1 CONFIG_SHA1_SMALL=3 # CONFIG_SHA1_HWACCEL is not set @@ -226,6 +227,7 @@ CONFIG_FEATURE_UNZIP_BZIP2=y CONFIG_FEATURE_UNZIP_LZMA=y CONFIG_FEATURE_UNZIP_XZ=y CONFIG_FEATURE_LZMA_FAST=y +# CONFIG_FEATURE_PATH_TRAVERSAL_PROTECTION is not set # # Coreutils @@ -965,6 +967,7 @@ CONFIG_IFUPDOWN_IFSTATE_PATH="" # CONFIG_IPNEIGH is not set # CONFIG_FEATURE_IP_ADDRESS is not set # CONFIG_FEATURE_IP_LINK is not set +CONFIG_FEATURE_IP_LINK_CAN=y # CONFIG_FEATURE_IP_ROUTE is not set CONFIG_FEATURE_IP_ROUTE_DIR="" # CONFIG_FEATURE_IP_TUNNEL is not set diff --git a/configs/mingw64_defconfig b/configs/mingw64_defconfig index b35157b94..013176794 100644 --- a/configs/mingw64_defconfig +++ b/configs/mingw64_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Busybox version: 1.37.0.git -# Fri Jun 14 12:24:50 2024 +# Busybox version: 1.38.0.git +# Mon Jun 9 11:54:15 2025 # CONFIG_HAVE_DOT_CONFIG=y # CONFIG_PLATFORM_POSIX is not set @@ -121,6 +121,7 @@ CONFIG_FEATURE_BUFFERS_USE_MALLOC=y # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set CONFIG_PASSWORD_MINLEN=6 +# CONFIG_FEATURE_USE_CNG_API is not set CONFIG_MD5_SMALL=1 CONFIG_SHA1_SMALL=3 # CONFIG_SHA1_HWACCEL is not set @@ -226,6 +227,7 @@ CONFIG_FEATURE_UNZIP_BZIP2=y CONFIG_FEATURE_UNZIP_LZMA=y CONFIG_FEATURE_UNZIP_XZ=y CONFIG_FEATURE_LZMA_FAST=y +# CONFIG_FEATURE_PATH_TRAVERSAL_PROTECTION is not set # # Coreutils @@ -965,6 +967,7 @@ CONFIG_IFUPDOWN_IFSTATE_PATH="" # CONFIG_IPNEIGH is not set # CONFIG_FEATURE_IP_ADDRESS is not set # CONFIG_FEATURE_IP_LINK is not set +CONFIG_FEATURE_IP_LINK_CAN=y # CONFIG_FEATURE_IP_ROUTE is not set CONFIG_FEATURE_IP_ROUTE_DIR="" # CONFIG_FEATURE_IP_TUNNEL is not set diff --git a/configs/mingw64a_defconfig b/configs/mingw64a_defconfig index 9c178a620..1a945b2ea 100644 --- a/configs/mingw64a_defconfig +++ b/configs/mingw64a_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Busybox version: 1.37.0.git -# Fri Jun 14 12:24:50 2024 +# Busybox version: 1.38.0.git +# Mon Jun 9 11:54:15 2025 # CONFIG_HAVE_DOT_CONFIG=y # CONFIG_PLATFORM_POSIX is not set @@ -121,6 +121,7 @@ CONFIG_FEATURE_BUFFERS_USE_MALLOC=y # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set CONFIG_PASSWORD_MINLEN=6 +# CONFIG_FEATURE_USE_CNG_API is not set CONFIG_MD5_SMALL=1 CONFIG_SHA1_SMALL=3 # CONFIG_SHA1_HWACCEL is not set @@ -226,6 +227,7 @@ CONFIG_FEATURE_UNZIP_BZIP2=y CONFIG_FEATURE_UNZIP_LZMA=y CONFIG_FEATURE_UNZIP_XZ=y CONFIG_FEATURE_LZMA_FAST=y +# CONFIG_FEATURE_PATH_TRAVERSAL_PROTECTION is not set # # Coreutils @@ -965,6 +967,7 @@ CONFIG_IFUPDOWN_IFSTATE_PATH="" # CONFIG_IPNEIGH is not set # CONFIG_FEATURE_IP_ADDRESS is not set # CONFIG_FEATURE_IP_LINK is not set +CONFIG_FEATURE_IP_LINK_CAN=y # CONFIG_FEATURE_IP_ROUTE is not set CONFIG_FEATURE_IP_ROUTE_DIR="" # CONFIG_FEATURE_IP_TUNNEL is not set diff --git a/configs/mingw64u_defconfig b/configs/mingw64u_defconfig index e5628ea44..d25aabe63 100644 --- a/configs/mingw64u_defconfig +++ b/configs/mingw64u_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Busybox version: 1.37.0.git -# Fri Jun 14 12:24:50 2024 +# Busybox version: 1.38.0.git +# Mon Jun 9 11:54:15 2025 # CONFIG_HAVE_DOT_CONFIG=y # CONFIG_PLATFORM_POSIX is not set @@ -121,6 +121,7 @@ CONFIG_FEATURE_BUFFERS_USE_MALLOC=y # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set CONFIG_PASSWORD_MINLEN=6 +CONFIG_FEATURE_USE_CNG_API=y CONFIG_MD5_SMALL=1 CONFIG_SHA1_SMALL=3 # CONFIG_SHA1_HWACCEL is not set @@ -226,6 +227,7 @@ CONFIG_FEATURE_UNZIP_BZIP2=y CONFIG_FEATURE_UNZIP_LZMA=y CONFIG_FEATURE_UNZIP_XZ=y CONFIG_FEATURE_LZMA_FAST=y +# CONFIG_FEATURE_PATH_TRAVERSAL_PROTECTION is not set # # Coreutils @@ -965,6 +967,7 @@ CONFIG_IFUPDOWN_IFSTATE_PATH="" # CONFIG_IPNEIGH is not set # CONFIG_FEATURE_IP_ADDRESS is not set # CONFIG_FEATURE_IP_LINK is not set +CONFIG_FEATURE_IP_LINK_CAN=y # CONFIG_FEATURE_IP_ROUTE is not set CONFIG_FEATURE_IP_ROUTE_DIR="" # CONFIG_FEATURE_IP_TUNNEL is not set diff --git a/include/libbb.h b/include/libbb.h index 7357d2907..1e345a3ee 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -2294,6 +2294,36 @@ char *decode_base64(char *dst, const char **pp_src) FAST_FUNC; char *decode_base32(char *dst, const char **pp_src) FAST_FUNC; void read_base64(FILE *src_stream, FILE *dst_stream, int flags) FAST_FUNC; +#if defined CONFIG_FEATURE_USE_CNG_API +struct bcrypt_hash_ctx_t { + void *handle; + void *hash_obj; + unsigned int output_size; +}; +typedef struct bcrypt_hash_ctx_t md5_ctx_t; +typedef struct bcrypt_hash_ctx_t sha1_ctx_t; +typedef struct bcrypt_hash_ctx_t sha256_ctx_t; +typedef struct bcrypt_hash_ctx_t sha512_ctx_t; +typedef struct sha3_ctx_t { + uint64_t state[25]; + unsigned bytes_queued; + unsigned input_block_bytes; +} sha3_ctx_t; +void md5_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC; +void sha1_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC; +void sha256_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC; +void sha512_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC; +void generic_hash(struct bcrypt_hash_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; +unsigned generic_end(struct bcrypt_hash_ctx_t *ctx, void *resbuf) FAST_FUNC; +#define md5_hash generic_hash +#define sha1_hash generic_hash +#define sha256_hash generic_hash +#define sha512_hash generic_hash +#define md5_end generic_end +#define sha1_end generic_end +#define sha256_end generic_end +#define sha512_end generic_end +#else typedef struct md5_ctx_t { uint8_t wbuffer[64]; /* always correctly aligned for uint64_t */ void (*process_block)(struct md5_ctx_t*) FAST_FUNC; @@ -2324,13 +2354,20 @@ void sha256_begin(sha256_ctx_t *ctx) FAST_FUNC; void sha512_begin(sha512_ctx_t *ctx) FAST_FUNC; void sha512_hash(sha512_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; unsigned sha512_end(sha512_ctx_t *ctx, void *resbuf) FAST_FUNC; +#endif void sha3_begin(sha3_ctx_t *ctx) FAST_FUNC; void sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; unsigned sha3_end(sha3_ctx_t *ctx, void *resbuf) FAST_FUNC; /* TLS benefits from knowing that sha1 and sha256 share these. Give them "agnostic" names too */ +#if defined CONFIG_FEATURE_USE_CNG_API +typedef struct bcrypt_hash_ctx_t md5sha_ctx_t; +#define md5sha_hash generic_hash +#define sha_end generic_end +#else typedef struct md5_ctx_t md5sha_ctx_t; #define md5sha_hash md5_hash #define sha_end sha1_end +#endif enum { MD5_OUTSIZE = 16, SHA1_OUTSIZE = 20, diff --git a/libbb/Config.src b/libbb/Config.src index 61b4601d6..ed6521c33 100644 --- a/libbb/Config.src +++ b/libbb/Config.src @@ -37,6 +37,14 @@ config PASSWORD_MINLEN help Minimum allowable password length. +config FEATURE_USE_CNG_API + bool "Use the Windows CNG API for checksums (Windows 10+ only)" + default n + depends on PLATFORM_MINGW32 + help + Use the in-built Windows CNG API for checksums. + This reduces code size, but is only supported on Windows 10+. + config MD5_SMALL int "MD5: Trade bytes for speed (0:fast, 3:slow)" default 1 # all "fast or small" options default to small @@ -67,6 +75,7 @@ config SHA1_SMALL config SHA1_HWACCEL bool "SHA1: Use hardware accelerated instructions if possible" default y + depends on !FEATURE_USE_CNG_API help On x86, this adds ~590 bytes of code. Throughput is about twice as fast as fully-unrolled generic code. @@ -74,6 +83,7 @@ config SHA1_HWACCEL config SHA256_HWACCEL bool "SHA256: Use hardware accelerated instructions if possible" default y + depends on !FEATURE_USE_CNG_API help On x86, this adds ~1k bytes of code. diff --git a/libbb/hash_md5_sha.c b/libbb/hash_md5_sha.c index 75a61c32c..abdacf939 100644 --- a/libbb/hash_md5_sha.c +++ b/libbb/hash_md5_sha.c @@ -13,6 +13,90 @@ #define NEED_SHA512 (ENABLE_SHA512SUM || ENABLE_USE_BB_CRYPT_SHA) +#if ENABLE_FEATURE_USE_CNG_API +# include +# include + +// these work on Windows >= 10 +# define BCRYPT_MD5_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000021) +# define BCRYPT_SHA1_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000031) +# define BCRYPT_SHA256_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000041) +# define BCRYPT_SHA512_ALG_HANDLE ((BCRYPT_ALG_HANDLE) 0x00000061) + +# define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0) + +static void die_if_error(NTSTATUS status, const char *function_name) { + if (!NT_SUCCESS(status)) { + bb_error_msg_and_die("call to %s failed: 0x%08lX", + function_name, (unsigned long)status); + } +} + +/* Initialize structure containing state of computation. + * (RFC 1321, 3.3: Step 3) + */ + +static void generic_init(struct bcrypt_hash_ctx_t *ctx, BCRYPT_ALG_HANDLE *alg_handle) { + DWORD hash_object_length = 0; + ULONG _unused; + NTSTATUS status; + + status = BCryptGetProperty(alg_handle, BCRYPT_OBJECT_LENGTH, (PUCHAR)&hash_object_length, sizeof(DWORD), &_unused, 0); + die_if_error(status, "BCryptGetProperty"); + status = BCryptGetProperty(alg_handle, BCRYPT_HASH_LENGTH, (PUCHAR)&ctx->output_size, sizeof(DWORD), &_unused, 0); + die_if_error(status, "BCryptGetProperty"); + + + ctx->hash_obj = xmalloc(hash_object_length); + + status = BCryptCreateHash(alg_handle, &ctx->handle, ctx->hash_obj, hash_object_length, NULL, 0, 0); + die_if_error(status, "BCryptCreateHash"); +} + +void FAST_FUNC md5_begin(md5_ctx_t *ctx) +{ + generic_init(ctx, BCRYPT_MD5_ALG_HANDLE); +} + +void FAST_FUNC sha1_begin(sha1_ctx_t *ctx) +{ + generic_init(ctx, BCRYPT_SHA1_ALG_HANDLE); +} + +/* Initialize structure containing state of computation. + (FIPS 180-2:5.3.2) */ +void FAST_FUNC sha256_begin(sha256_ctx_t *ctx) +{ + generic_init(ctx, BCRYPT_SHA256_ALG_HANDLE); +} + +#if NEED_SHA512 +/* Initialize structure containing state of computation. + (FIPS 180-2:5.3.3) */ +void FAST_FUNC sha512_begin(sha512_ctx_t *ctx) +{ + generic_init(ctx, BCRYPT_SHA512_ALG_HANDLE); +} +#endif /* NEED_SHA512 */ + +void FAST_FUNC generic_hash(struct bcrypt_hash_ctx_t *ctx, const void *buffer, size_t len) +{ + /* + for perf, no error checking here + */ + /*NTSTATUS status = */ BCryptHashData(ctx->handle, (const PUCHAR)buffer, len, 0); + // die_if_error(status, "BCryptHashData"); +} + +unsigned FAST_FUNC generic_end(struct bcrypt_hash_ctx_t *ctx, void *resbuf) +{ + NTSTATUS status = BCryptFinishHash(ctx->handle, resbuf, ctx->output_size, 0); + die_if_error(status, "BCryptFinishHash"); + free(ctx->hash_obj); + return ctx->output_size; +} +#endif /* !ENABLE_FEATURE_USE_CNG_API */ + #if ENABLE_SHA1_HWACCEL || ENABLE_SHA256_HWACCEL # if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) static void cpuid_eax_ebx_ecx(unsigned *eax, unsigned *ebx, unsigned *ecx, unsigned *edx) @@ -80,6 +164,7 @@ static ALWAYS_INLINE uint64_t rotl64(uint64_t x, unsigned n) return (x << n) | (x >> (64 - n)); } +#if !ENABLE_FEATURE_USE_CNG_API /* Process the remaining bytes in the buffer */ static void FAST_FUNC common64_end(md5_ctx_t *ctx, int swap_needed) { @@ -1367,6 +1452,7 @@ unsigned FAST_FUNC sha512_end(sha512_ctx_t *ctx, void *resbuf) return sizeof(ctx->hash); } #endif /* NEED_SHA512 */ +#endif /* !ENABLE_FEATURE_USE_CNG_API */ /* diff --git a/scripts/mk_mingw64u_defconfig b/scripts/mk_mingw64u_defconfig index 19124d735..5561a0900 100755 --- a/scripts/mk_mingw64u_defconfig +++ b/scripts/mk_mingw64u_defconfig @@ -32,6 +32,7 @@ set_build_opts \ CONFIG_LAST_SUPPORTED_WCHAR=1114111 \ CONFIG_UNICODE_COMBINING_WCHARS=y \ CONFIG_UNICODE_WIDE_WCHARS=y \ + CONFIG_FEATURE_USE_CNG_API=y \ < "$configs"/mingw64_defconfig \ > "$configs"/mingw64u_defconfig -- cgit v1.2.3-55-g6feb From 3fac1fbf2c3daf805629f0ecb10b3d268d2a6f5d Mon Sep 17 00:00:00 2001 From: RFL890 Date: Mon, 9 Jun 2025 11:32:07 -0400 Subject: format #defines --- include/libbb.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/libbb.h b/include/libbb.h index 1e345a3ee..19becf428 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -2315,14 +2315,14 @@ void sha256_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC; void sha512_begin(struct bcrypt_hash_ctx_t *ctx) FAST_FUNC; void generic_hash(struct bcrypt_hash_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC; unsigned generic_end(struct bcrypt_hash_ctx_t *ctx, void *resbuf) FAST_FUNC; -#define md5_hash generic_hash -#define sha1_hash generic_hash -#define sha256_hash generic_hash -#define sha512_hash generic_hash -#define md5_end generic_end -#define sha1_end generic_end -#define sha256_end generic_end -#define sha512_end generic_end +# define md5_hash generic_hash +# define sha1_hash generic_hash +# define sha256_hash generic_hash +# define sha512_hash generic_hash +# define md5_end generic_end +# define sha1_end generic_end +# define sha256_end generic_end +# define sha512_end generic_end #else typedef struct md5_ctx_t { uint8_t wbuffer[64]; /* always correctly aligned for uint64_t */ -- cgit v1.2.3-55-g6feb