From ce57eb44a829badf0642dd5aa61941d0945aa3d0 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Wed, 8 Oct 2025 18:10:27 -0600 Subject: [PATCH] Replace hand-rolled MD5/SHA-1 with faster versions --- CMakeLists.txt | 1 + dll/advapi32/md5.c | 291 +++++++++++++++++++++++ dll/advapi32/md5.h | 53 +++++ dll/advapi32/sha1.h | 479 ++++++++++++++++++++++++++++++++++++++ dll/advapi32/wincrypt.cpp | 269 +++++---------------- 5 files changed, 886 insertions(+), 207 deletions(-) create mode 100644 dll/advapi32/md5.c create mode 100644 dll/advapi32/md5.h create mode 100644 dll/advapi32/sha1.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f41690b..d00d8e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,6 +125,7 @@ add_executable(wibo dll/advapi32/winbase.cpp dll/advapi32/wincrypt.cpp dll/advapi32/winreg.cpp + dll/advapi32/md5.c dll/bcrypt.cpp dll/crt.cpp dll/kernel32.cpp diff --git a/dll/advapi32/md5.c b/dll/advapi32/md5.c new file mode 100644 index 0000000..b235e17 --- /dev/null +++ b/dll/advapi32/md5.c @@ -0,0 +1,291 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * (This is a heavily cut-down "BSD license".) + * + * This differs from Colin Plumb's older public domain implementation in that + * no exactly 32-bit integer data type is required (any 32-bit or wider + * unsigned integer data type will do), there's no compile-time endianness + * configuration, and the function prototypes match OpenSSL's. No code from + * Colin Plumb's implementation has been reused; this comment merely compares + * the properties of the two independent implementations. + * + * The primary goals of this implementation are portability and ease of use. + * It is meant to be fast, but not as fast as possible. Some known + * optimizations are not included to reduce source code size and avoid + * compile-time configuration. + */ + +#ifndef HAVE_OPENSSL + +#include + +#include "md5.h" + +/* + * The basic MD5 functions. + * + * F and G are optimized compared to their RFC 1321 definitions for + * architectures that lack an AND-NOT instruction, just like in Colin Plumb's + * implementation. + */ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) +#define H(x, y, z) (((x) ^ (y)) ^ (z)) +#define H2(x, y, z) ((x) ^ ((y) ^ (z))) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) + +/* + * The MD5 transformation for all four rounds. + */ +#define STEP(f, a, b, c, d, x, t, s) \ + (a) += f((b), (c), (d)) + (x) + (t); \ + (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ + (a) += (b); + +/* + * SET reads 4 input bytes in little-endian byte order and stores them in a + * properly aligned word in host byte order. + * + * The check for little-endian architectures that tolerate unaligned memory + * accesses is just an optimization. Nothing will break if it fails to detect + * a suitable architecture. + * + * Unfortunately, this optimization may be a C strict aliasing rules violation + * if the caller's data buffer has effective type that cannot be aliased by + * MD5_u32plus. In practice, this problem may occur if these MD5 routines are + * inlined into a calling function, or with future and dangerously advanced + * link-time optimizations. For the time being, keeping these MD5 routines in + * their own translation unit avoids the problem. + */ +#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) +#define SET(n) \ + (*(MD5_u32plus *)&ptr[(n) * 4]) +#define GET(n) \ + SET(n) +#else +#define SET(n) \ + (ctx->block[(n)] = \ + (MD5_u32plus)ptr[(n) * 4] | \ + ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ + ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ + ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) +#define GET(n) \ + (ctx->block[(n)]) +#endif + +/* + * This processes one or more 64-byte data blocks, but does NOT update the bit + * counters. There are no alignment requirements. + */ +static const void *body(MD5_CTX *ctx, const void *data, unsigned long size) +{ + const unsigned char *ptr; + MD5_u32plus a, b, c, d; + MD5_u32plus saved_a, saved_b, saved_c, saved_d; + + ptr = (const unsigned char *)data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + +/* Round 1 */ + STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) + STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) + STEP(F, c, d, a, b, SET(2), 0x242070db, 17) + STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) + STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) + STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) + STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) + STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) + STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) + STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) + STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) + STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) + STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) + STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) + STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) + STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) + +/* Round 2 */ + STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) + STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) + STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) + STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) + STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) + STEP(G, d, a, b, c, GET(10), 0x02441453, 9) + STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) + STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) + STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) + STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) + STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) + STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) + STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) + STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) + STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) + STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) + +/* Round 3 */ + STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) + STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11) + STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) + STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23) + STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) + STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11) + STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) + STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23) + STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) + STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11) + STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) + STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23) + STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) + STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11) + STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) + STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23) + +/* Round 4 */ + STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) + STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) + STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) + STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) + STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) + STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) + STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) + STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) + STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) + STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) + STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) + STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) + STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) + STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) + STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) + STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while (size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; +} + +void MD5_Init(MD5_CTX *ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + + ctx->lo = 0; + ctx->hi = 0; +} + +void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) +{ + MD5_u32plus saved_lo; + unsigned long used, available; + + saved_lo = ctx->lo; + if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) + ctx->hi++; + ctx->hi += size >> 29; + + used = saved_lo & 0x3f; + + if (used) { + available = 64 - used; + + if (size < available) { + memcpy(&ctx->buffer[used], data, size); + return; + } + + memcpy(&ctx->buffer[used], data, available); + data = (const unsigned char *)data + available; + size -= available; + body(ctx, ctx->buffer, 64); + } + + if (size >= 64) { + data = body(ctx, data, size & ~(unsigned long)0x3f); + size &= 0x3f; + } + + memcpy(ctx->buffer, data, size); +} + +#define OUT(dst, src) \ + (dst)[0] = (unsigned char)(src); \ + (dst)[1] = (unsigned char)((src) >> 8); \ + (dst)[2] = (unsigned char)((src) >> 16); \ + (dst)[3] = (unsigned char)((src) >> 24); + +void MD5_Final(unsigned char *result, MD5_CTX *ctx) +{ + unsigned long used, available; + + used = ctx->lo & 0x3f; + + ctx->buffer[used++] = 0x80; + + available = 64 - used; + + if (available < 8) { + memset(&ctx->buffer[used], 0, available); + body(ctx, ctx->buffer, 64); + used = 0; + available = 64; + } + + memset(&ctx->buffer[used], 0, available - 8); + + ctx->lo <<= 3; + OUT(&ctx->buffer[56], ctx->lo) + OUT(&ctx->buffer[60], ctx->hi) + + body(ctx, ctx->buffer, 64); + + OUT(&result[0], ctx->a) + OUT(&result[4], ctx->b) + OUT(&result[8], ctx->c) + OUT(&result[12], ctx->d) + + memset(ctx, 0, sizeof(*ctx)); +} + +#endif diff --git a/dll/advapi32/md5.h b/dll/advapi32/md5.h new file mode 100644 index 0000000..85302e4 --- /dev/null +++ b/dll/advapi32/md5.h @@ -0,0 +1,53 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * See md5.c for more information. + */ + +#ifdef HAVE_OPENSSL +#include +#elif !defined(_MD5_H) +#define _MD5_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Any 32-bit or wider unsigned integer data type will do */ +typedef unsigned int MD5_u32plus; + +typedef struct { + MD5_u32plus lo, hi; + MD5_u32plus a, b, c, d; + unsigned char buffer[64]; + MD5_u32plus block[16]; +} MD5_CTX; + +extern void MD5_Init(MD5_CTX *ctx); +extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); +extern void MD5_Final(unsigned char *result, MD5_CTX *ctx); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/dll/advapi32/sha1.h b/dll/advapi32/sha1.h new file mode 100644 index 0000000..ff63932 --- /dev/null +++ b/dll/advapi32/sha1.h @@ -0,0 +1,479 @@ +/* +SHA-1 hashing. Choice of public domain or MIT-0. See license statements at the end of this file. + +David Reid - mackron@gmail.com +*/ + +/* +A simple SHA-1 hashing implementation. Usage: + + unsigned char digest[SHA1_SIZE]; + sha1_context ctx; + sha1_init(&ctx); + { + sha1_update(&ctx, src, sz); + } + sha1_finalize(&ctx, digest); + +The above code is the literal implementation of `sha1()` which is a high level helper for hashing +data of a known size: + + unsigned char hash[SHA1_SIZE]; + sha1(hash, data, dataSize); + +Use `sha1_format()` to format the digest as a hex string. The capacity of the output buffer needs to +be at least `SHA1_SIZE_FORMATTED` bytes. + +This library does not perform any memory allocations and does not use anything from the standard +library except for `size_t` and `NULL`, both of which are drawn in from stddef.h. No other standard +headers are included. + +There is no need to link to anything with this library. You can use SHA1_IMPLEMENTATION to define +the implementation section, or you can use sha1.c if you prefer a traditional header/source pair. + +This implements both methods defined in RFC 3174. Method 1 will be used by default. If you want to +use Method 2, define `SHA1_USE_RFC_METHOD_2` at compile time. + + #define SHA1_USE_RFC_METHOD_2 + #define SHA1_IMPLEMENTATION + #include "sha1.h" + +No effort has been made to optimize this beyond the algorithms described in RGC 3174. If you're +looking for the fastest SHA-1 implementation you'll need to look elsewhere. An optimized +implementation may come later. +*/ +#ifndef sha1_h +#define sha1_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include /* For size_t and NULL. */ + +#if defined(_MSC_VER) + typedef unsigned __int64 sha1_uint64; +#else + typedef unsigned long long sha1_uint64; +#endif + +#if !defined(SHA1_API) + #define SHA1_API +#endif + +#define SHA1_SIZE 20 +#define SHA1_SIZE_FORMATTED 41 + +typedef struct +{ + unsigned int h[5]; + sha1_uint64 sz; + unsigned char cache[64]; + unsigned int cacheLen; +} sha1_context; + +SHA1_API void sha1_init(sha1_context* ctx); +SHA1_API void sha1_update(sha1_context* ctx, const void* src, size_t sz); +SHA1_API void sha1_finalize(sha1_context* ctx, unsigned char* digest); +SHA1_API void sha1(unsigned char* digest, const void* src, size_t sz); +SHA1_API void sha1_format(char* dst, size_t dstCap, const unsigned char* hash); + +#ifdef __cplusplus +} +#endif +#endif /* sha1_h */ + +#if defined(SHA1_IMPLEMENTATION) +#ifndef sha1_c +#define sha1_c + +#define SHA1_ALGORITHM_RFC_METHOD_1 1 +#define SHA1_ALGORITHM_RFC_METHOD_2 2 +#define SHA1_ALGORITHM_DEFAULT SHA1_ALGORITHM_RFC_METHOD_1 + +# if defined(SHA1_USE_RFC_METHOD_1) + #define SHA1_ALGORITHM SHA1_ALGORITHM_RFC_METHOD_1 +#elif defined(SHA1_USE_RFC_METHOD_2) + #define SHA1_ALGORITHM SHA1_ALGORITHM_RFC_METHOD_2 +#else + #define SHA1_ALGORITHM SHA1_ALGORITHM_DEFAULT +#endif + + +static void sha1_zero_memory(void* p, size_t sz) +{ + size_t i; + for (i = 0; i < sz; i += 1) { + ((unsigned char*)p)[i] = 0; + } +} + +static void sha1_copy_memory(void* dst, const void* src, size_t sz) +{ + size_t i; + for (i = 0; i < sz; i += 1) { + ((unsigned char*)dst)[i] = ((unsigned char*)src)[i]; + } +} + + +#define SHA1_ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + +#define SHA1_F00(b, c, d) (((b) & (c)) | ((~(b)) & (d))) /* (B AND C) OR ((NOT B) AND D) */ +#define SHA1_F20(b, c, d) ((b) ^ (c) ^ (d)) /* B XOR C XOR D */ +#define SHA1_F40(b, c, d) (((b) & (c)) | ((b) & (d)) | ((c) & (d))) /* (B AND C) OR (B AND D) OR (C AND D) */ +#define SHA1_F60(b, c, d) ((b) ^ (c) ^ (d)) /* B XOR C XOR D */ + +/* +This is the main SHA-1 function. Everything is processed in blocks of 64 bytes. +*/ +static void sha1_update_block(sha1_context* ctx, const unsigned char* src) +{ + size_t i; + unsigned int w[80]; + unsigned int a, b, c, d, e; + unsigned int temp; + + /* assert(ctx != NULL); */ + /* assert(src != NULL); */ + + for (i = 0; i < 16; i += 1) { + w[i] = (src[i*4 + 0] << 24); + w[i] |= (src[i*4 + 1] << 16); + w[i] |= (src[i*4 + 2] << 8); + w[i] |= (src[i*4 + 3] << 0); + } + + a = ctx->h[0]; + b = ctx->h[1]; + c = ctx->h[2]; + d = ctx->h[3]; + e = ctx->h[4]; + +#if SHA1_ALGORITHM == SHA1_ALGORITHM_RFC_METHOD_1 + { + for (i = 16; i < 80; i += 1) { + w[i] = SHA1_ROTATE_LEFT((w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]), 1); + } + + for (i = 0; i < 20; i += 1) { + temp = SHA1_ROTATE_LEFT(a, 5) + SHA1_F00(b, c, d) + e + w[i] + 0x5A827999; + e = d; + d = c; + c = SHA1_ROTATE_LEFT(b, 30); + b = a; + a = temp; + } + + for (i = 20; i < 40; i += 1) { + temp = SHA1_ROTATE_LEFT(a, 5) + SHA1_F20(b, c, d) + e + w[i] + 0x6ED9EBA1; + e = d; + d = c; + c = SHA1_ROTATE_LEFT(b, 30); + b = a; + a = temp; + } + + for (i = 40; i < 60; i += 1) { + temp = SHA1_ROTATE_LEFT(a, 5) + SHA1_F40(b, c, d) + e + w[i] + 0x8F1BBCDC; + e = d; + d = c; + c = SHA1_ROTATE_LEFT(b, 30); + b = a; + a = temp; + } + + for (i = 60; i < 80; i += 1) { + temp = SHA1_ROTATE_LEFT(a, 5) + SHA1_F60(b, c, d) + e + w[i] + 0xCA62C1D6; + e = d; + d = c; + c = SHA1_ROTATE_LEFT(b, 30); + b = a; + a = temp; + } + } +#endif + +#if SHA1_ALGORITHM == SHA1_ALGORITHM_RFC_METHOD_2 + { + unsigned int mask = 0x0000000F; + unsigned int s; + + for (i = 0; i < 16; i += 1) { + s = i & mask; + temp = SHA1_ROTATE_LEFT(a, 5) + SHA1_F00(b, c, d) + e + w[s] + 0x5A827999; + e = d; + d = c; + c = SHA1_ROTATE_LEFT(b, 30); + b = a; + a = temp; + } + + for (i = 16; i < 20; i += 1) { + s = i & mask; + w[s] = SHA1_ROTATE_LEFT(w[(s + 13) & mask] ^ w[(s + 8) & mask] ^ w[(s + 2) & mask] ^ w[s], 1); + temp = SHA1_ROTATE_LEFT(a, 5) + SHA1_F00(b, c, d) + e + w[s] + 0x5A827999; + e = d; + d = c; + c = SHA1_ROTATE_LEFT(b, 30); + b = a; + a = temp; + } + + for (i = 20; i < 40; i += 1) { + s = i & mask; + w[s] = SHA1_ROTATE_LEFT(w[(s + 13) & mask] ^ w[(s + 8) & mask] ^ w[(s + 2) & mask] ^ w[s], 1); + temp = SHA1_ROTATE_LEFT(a, 5) + SHA1_F20(b, c, d) + e + w[s] + 0x6ED9EBA1; + e = d; + d = c; + c = SHA1_ROTATE_LEFT(b, 30); + b = a; + a = temp; + } + + for (i = 40; i < 60; i += 1) { + s = i & mask; + w[s] = SHA1_ROTATE_LEFT(w[(s + 13) & mask] ^ w[(s + 8) & mask] ^ w[(s + 2) & mask] ^ w[s], 1); + temp = SHA1_ROTATE_LEFT(a, 5) + SHA1_F40(b, c, d) + e + w[s] + 0x8F1BBCDC; + e = d; + d = c; + c = SHA1_ROTATE_LEFT(b, 30); + b = a; + a = temp; + } + + for (i = 60; i < 80; i += 1) { + s = i & mask; + w[s] = SHA1_ROTATE_LEFT(w[(s + 13) & mask] ^ w[(s + 8) & mask] ^ w[(s + 2) & mask] ^ w[s], 1); + temp = SHA1_ROTATE_LEFT(a, 5) + SHA1_F60(b, c, d) + e + w[s] + 0xCA62C1D6; + e = d; + d = c; + c = SHA1_ROTATE_LEFT(b, 30); + b = a; + a = temp; + } + } +#endif + + ctx->h[0] += a; + ctx->h[1] += b; + ctx->h[2] += c; + ctx->h[3] += d; + ctx->h[4] += e; + + /* We'll only ever be calling this if the context's cache is full. At this point the cache will also be empty. */ + ctx->cacheLen = 0; +} + +SHA1_API void sha1_init(sha1_context* ctx) +{ + if (ctx == NULL) { + return; + } + + sha1_zero_memory(ctx, sizeof(*ctx)); + + ctx->h[0] = 0x67452301; + ctx->h[1] = 0xEFCDAB89; + ctx->h[2] = 0x98BADCFE; + ctx->h[3] = 0x10325476; + ctx->h[4] = 0xC3D2E1F0; +} + +SHA1_API void sha1_update(sha1_context* ctx, const void* src, size_t sz) +{ + const unsigned char* bytes = (const unsigned char*)src; + size_t totalBytesProcessed = 0; + + if (ctx == NULL || (src == NULL && sz > 0)) { + return; + } + + /* Keep processing until all data has been exhausted. */ + while (totalBytesProcessed < sz) { + /* Optimization. Bypass the cache if there's nothing in it and the number of bytes remaining to process is larger than 64. */ + size_t bytesRemainingToProcess = sz - totalBytesProcessed; + if (ctx->cacheLen == 0 && bytesRemainingToProcess > sizeof(ctx->cache)) { + /* Fast path. Bypass the cache and just process directly. */ + sha1_update_block(ctx, bytes + totalBytesProcessed); + totalBytesProcessed += sizeof(ctx->cache); + } else { + /* Slow path. Need to store in the cache. */ + size_t cacheRemaining = sizeof(ctx->cache) - ctx->cacheLen; + if (cacheRemaining > 0) { + /* There's still some room left in the cache. Write as much data to it as we can. */ + size_t bytesToProcess = bytesRemainingToProcess; + if (bytesToProcess > cacheRemaining) { + bytesToProcess = cacheRemaining; + } + + sha1_copy_memory(ctx->cache + ctx->cacheLen, bytes + totalBytesProcessed, bytesToProcess); + ctx->cacheLen += (unsigned int)bytesToProcess; /* Safe cast. bytesToProcess will always be <= sizeof(ctx->cache) which is 64. */ + totalBytesProcessed += bytesToProcess; + + /* Update the number of bytes remaining in the cache so we can use it later. */ + cacheRemaining = sizeof(ctx->cache) - ctx->cacheLen; + } + + /* If the cache is full, get it processed. */ + if (cacheRemaining == 0) { + sha1_update_block(ctx, ctx->cache); + } + } + } + + ctx->sz += sz; +} + +SHA1_API void sha1_finalize(sha1_context* ctx, unsigned char* digest) +{ + size_t cacheRemaining; + unsigned int szLo; + unsigned int szHi; + + if (digest == NULL) { + return; + } + + if (ctx == NULL) { + sha1_zero_memory(digest, SHA1_SIZE); + return; + } + + /* + Padding must be applied. First thing to do is clear the cache if there's no room for at least + one byte. This should never happen, but leaving this logic here for safety. + */ + cacheRemaining = sizeof(ctx->cache) - ctx->cacheLen; + if (cacheRemaining == 0) { + sha1_update_block(ctx, ctx->cache); + } + + /* Now we need to write a byte with the most significant bit set (0x80). */ + ctx->cache[ctx->cacheLen] = 0x80; + ctx->cacheLen += 1; + + /* If there isn't enough room for 8 bytes we need to padd with zeroes and get the block processed. */ + cacheRemaining = sizeof(ctx->cache) - ctx->cacheLen; + if (cacheRemaining < 8) { + sha1_zero_memory(ctx->cache + ctx->cacheLen, cacheRemaining); + sha1_update_block(ctx, ctx->cache); + cacheRemaining = sizeof(ctx->cache); + } + + /* Now we need to fill the buffer with zeros until we've filled 56 bytes (8 bytes left over for the length). */ + sha1_zero_memory(ctx->cache + ctx->cacheLen, cacheRemaining - 8); + + szLo = (unsigned int)(((ctx->sz >> 0) & 0xFFFFFFFF) << 3); + szHi = (unsigned int)(((ctx->sz >> 32) & 0xFFFFFFFF) << 3); + ctx->cache[56] = (unsigned char)((szHi >> 24) & 0xFF); + ctx->cache[57] = (unsigned char)((szHi >> 16) & 0xFF); + ctx->cache[58] = (unsigned char)((szHi >> 8) & 0xFF); + ctx->cache[59] = (unsigned char)((szHi >> 0) & 0xFF); + ctx->cache[60] = (unsigned char)((szLo >> 24) & 0xFF); + ctx->cache[61] = (unsigned char)((szLo >> 16) & 0xFF); + ctx->cache[62] = (unsigned char)((szLo >> 8) & 0xFF); + ctx->cache[63] = (unsigned char)((szLo >> 0) & 0xFF); + sha1_update_block(ctx, ctx->cache); + + /* Now write out the digest. */ + digest[ 0] = (unsigned char)(ctx->h[0] >> 24); digest[ 1] = (unsigned char)(ctx->h[0] >> 16); digest[ 2] = (unsigned char)(ctx->h[0] >> 8); digest[ 3] = (unsigned char)(ctx->h[0] >> 0); + digest[ 4] = (unsigned char)(ctx->h[1] >> 24); digest[ 5] = (unsigned char)(ctx->h[1] >> 16); digest[ 6] = (unsigned char)(ctx->h[1] >> 8); digest[ 7] = (unsigned char)(ctx->h[1] >> 0); + digest[ 8] = (unsigned char)(ctx->h[2] >> 24); digest[ 9] = (unsigned char)(ctx->h[2] >> 16); digest[10] = (unsigned char)(ctx->h[2] >> 8); digest[11] = (unsigned char)(ctx->h[2] >> 0); + digest[12] = (unsigned char)(ctx->h[3] >> 24); digest[13] = (unsigned char)(ctx->h[3] >> 16); digest[14] = (unsigned char)(ctx->h[3] >> 8); digest[15] = (unsigned char)(ctx->h[3] >> 0); + digest[16] = (unsigned char)(ctx->h[4] >> 24); digest[17] = (unsigned char)(ctx->h[4] >> 16); digest[18] = (unsigned char)(ctx->h[4] >> 8); digest[19] = (unsigned char)(ctx->h[4] >> 0); +} + +SHA1_API void sha1(unsigned char* digest, const void* src, size_t sz) +{ + sha1_context ctx; + sha1_init(&ctx); + { + sha1_update(&ctx, src, sz); + } + sha1_finalize(&ctx, digest); +} + + +static void sha1_format_byte(char* dst, unsigned char byte) +{ + const char* hex = "0123456789abcdef"; + dst[0] = hex[(byte & 0xF0) >> 4]; + dst[1] = hex[(byte & 0x0F) ]; +} + +SHA1_API void sha1_format(char* dst, size_t dstCap, const unsigned char* hash) +{ + size_t i; + + if (dst == NULL) { + return; + } + + if (dstCap < SHA1_SIZE_FORMATTED) { + if (dstCap > 0) { + dst[0] = '\0'; + } + + return; + } + + for (i = 0; i < SHA1_SIZE; i += 1) { + sha1_format_byte(dst + (i*2), hash[i]); + } + + /* Always null terminate. */ + dst[SHA1_SIZE_FORMATTED-1] = '\0'; +} +#endif /* sha1_c */ +#endif /* SHA1_IMPLEMENTATION */ + +/* +This software is available as a choice of the following licenses. Choose +whichever you prefer. + +=============================================================================== +ALTERNATIVE 1 - Public Domain (www.unlicense.org) +=============================================================================== +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + +=============================================================================== +ALTERNATIVE 2 - MIT No Attribution +=============================================================================== +Copyright 2022 David Reid + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ diff --git a/dll/advapi32/wincrypt.cpp b/dll/advapi32/wincrypt.cpp index eb30a4b..516121e 100644 --- a/dll/advapi32/wincrypt.cpp +++ b/dll/advapi32/wincrypt.cpp @@ -4,189 +4,31 @@ #include "context.h" #include "errors.h" +#include "md5.h" +#define SHA1_IMPLEMENTATION +#include "sha1.h" + #include #include -#include namespace { struct HashObject { ALG_ID algid = 0; - std::vector data; - std::vector digest; - bool digestComputed = false; + unsigned char digest[20]{}; + union { + MD5_CTX md5{}; + sha1_context sha1; + }; }; -uint32_t leftRotate(uint32_t value, uint32_t bits) { return (value << bits) | (value >> (32 - bits)); } - -std::vector computeMD5(const std::vector &input) { - static const uint32_t s[64] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, - 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, - 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, - 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21}; - static const uint32_t K[64] = { - 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, - 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, - 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, - 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, - 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, - 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, - 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, - 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391}; - - std::vector data = input; - uint64_t bitLen = static_cast(data.size()) * 8ULL; - data.push_back(0x80); - while ((data.size() % 64) != 56) { - data.push_back(0); - } - for (int i = 0; i < 8; ++i) { - data.push_back(static_cast((bitLen >> (8 * i)) & 0xFF)); - } - - uint32_t A = 0x67452301; - uint32_t B = 0xEFCDAB89; - uint32_t C = 0x98BADCFE; - uint32_t D = 0x10325476; - - for (size_t offset = 0; offset < data.size(); offset += 64) { - uint32_t M[16]; - for (int i = 0; i < 16; ++i) { - M[i] = static_cast(data[offset + i * 4]) | - (static_cast(data[offset + i * 4 + 1]) << 8) | - (static_cast(data[offset + i * 4 + 2]) << 16) | - (static_cast(data[offset + i * 4 + 3]) << 24); - } - uint32_t a = A; - uint32_t b = B; - uint32_t c = C; - uint32_t d = D; - for (int i = 0; i < 64; ++i) { - uint32_t F; - int g; - if (i < 16) { - F = (b & c) | ((~b) & d); - g = i; - } else if (i < 32) { - F = (d & b) | ((~d) & c); - g = (5 * i + 1) % 16; - } else if (i < 48) { - F = b ^ c ^ d; - g = (3 * i + 5) % 16; - } else { - F = c ^ (b | (~d)); - g = (7 * i) % 16; - } - uint32_t temp = d; - d = c; - c = b; - uint32_t rotateVal = a + F + K[i] + M[g]; - b = b + leftRotate(rotateVal, s[i]); - a = temp; - } - A += a; - B += b; - C += c; - D += d; - } - - std::vector digest(16); - uint32_t output[4] = {A, B, C, D}; - for (int i = 0; i < 4; ++i) { - digest[i * 4] = static_cast(output[i] & 0xFF); - digest[i * 4 + 1] = static_cast((output[i] >> 8) & 0xFF); - digest[i * 4 + 2] = static_cast((output[i] >> 16) & 0xFF); - digest[i * 4 + 3] = static_cast((output[i] >> 24) & 0xFF); - } - return digest; -} - -std::vector computeSHA1(const std::vector &input) { - std::vector data = input; - uint64_t bitLen = static_cast(data.size()) * 8ULL; - data.push_back(0x80); - while ((data.size() % 64) != 56) { - data.push_back(0); - } - for (int i = 7; i >= 0; --i) { - data.push_back(static_cast((bitLen >> (8 * i)) & 0xFF)); - } - - uint32_t h0 = 0x67452301; - uint32_t h1 = 0xEFCDAB89; - uint32_t h2 = 0x98BADCFE; - uint32_t h3 = 0x10325476; - uint32_t h4 = 0xC3D2E1F0; - - for (size_t offset = 0; offset < data.size(); offset += 64) { - uint32_t w[80]; - for (int i = 0; i < 16; ++i) { - w[i] = (static_cast(data[offset + i * 4]) << 24) | - (static_cast(data[offset + i * 4 + 1]) << 16) | - (static_cast(data[offset + i * 4 + 2]) << 8) | - static_cast(data[offset + i * 4 + 3]); - } - for (int i = 16; i < 80; ++i) { - w[i] = leftRotate(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1); - } - uint32_t a = h0; - uint32_t b = h1; - uint32_t c = h2; - uint32_t d = h3; - uint32_t e = h4; - for (int i = 0; i < 80; ++i) { - uint32_t f; - uint32_t k; - if (i < 20) { - f = (b & c) | ((~b) & d); - k = 0x5A827999; - } else if (i < 40) { - f = b ^ c ^ d; - k = 0x6ED9EBA1; - } else if (i < 60) { - f = (b & c) | (b & d) | (c & d); - k = 0x8F1BBCDC; - } else { - f = b ^ c ^ d; - k = 0xCA62C1D6; - } - uint32_t temp = leftRotate(a, 5) + f + e + k + w[i]; - e = d; - d = c; - c = leftRotate(b, 30); - b = a; - a = temp; - } - h0 += a; - h1 += b; - h2 += c; - h3 += d; - h4 += e; - } - - std::vector digest(20); - uint32_t output[5] = {h0, h1, h2, h3, h4}; - for (int i = 0; i < 5; ++i) { - digest[i * 4] = static_cast((output[i] >> 24) & 0xFF); - digest[i * 4 + 1] = static_cast((output[i] >> 16) & 0xFF); - digest[i * 4 + 2] = static_cast((output[i] >> 8) & 0xFF); - digest[i * 4 + 3] = static_cast(output[i] & 0xFF); - } - return digest; -} - bool computeDigest(HashObject &hash) { - if (hash.digestComputed) { - return true; - } switch (hash.algid) { case CALG_MD5: - hash.digest = computeMD5(hash.data); - hash.digestComputed = true; + MD5_Final(hash.digest, &hash.md5); return true; case CALG_SHA1: - hash.digest = computeSHA1(hash.data); - hash.digestComputed = true; + sha1_finalize(&hash.sha1, hash.digest); return true; default: return false; @@ -202,6 +44,17 @@ HashObject *hashObjectFromHandle(HCRYPTHASH hHash) { HCRYPTHASH hashHandleFromObject(HashObject *hash) { return static_cast(reinterpret_cast(hash)); } +DWORD hashSizeForAlgid(ALG_ID algid) { + switch (algid) { + case CALG_MD5: + return 16; + case CALG_SHA1: + return 20; + default: + return 0; + } +} + } // namespace namespace advapi32 { @@ -270,9 +123,11 @@ BOOL WIN_FUNC CryptCreateHash(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey, DW } auto *hash = new HashObject; hash->algid = Algid; - hash->digestComputed = false; - hash->data.clear(); - hash->digest.clear(); + if (Algid == CALG_MD5) { + MD5_Init(&hash->md5); + } else if (Algid == CALG_SHA1) { + sha1_init(&hash->sha1); + } *phHash = hashHandleFromObject(hash); return TRUE; } @@ -291,9 +146,11 @@ BOOL WIN_FUNC CryptHashData(HCRYPTHASH hHash, const BYTE *pbData, DWORD dwDataLe return FALSE; } if (pbData && dwDataLen) { - hash->data.insert(hash->data.end(), pbData, pbData + dwDataLen); - hash->digestComputed = false; - hash->digest.clear(); + if (hash->algid == CALG_MD5) { + MD5_Update(&hash->md5, pbData, dwDataLen); + } else if (hash->algid == CALG_SHA1) { + sha1_update(&hash->sha1, pbData, dwDataLen); + } } return TRUE; } @@ -328,37 +185,7 @@ BOOL WIN_FUNC CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam, BYTE *pbData, D return TRUE; } case HP_HASHSIZE: { - DWORD size = 0; - switch (hash->algid) { - case CALG_MD5: - size = 16; - break; - case CALG_SHA1: - size = 20; - break; - default: - wibo::lastError = ERROR_NOT_SUPPORTED; - return FALSE; - } - if (!pbData) { - *pdwDataLen = sizeof(DWORD); - return TRUE; - } - if (*pdwDataLen < sizeof(DWORD)) { - *pdwDataLen = sizeof(DWORD); - wibo::lastError = ERROR_INSUFFICIENT_BUFFER; - return FALSE; - } - memcpy(pbData, &size, sizeof(DWORD)); - *pdwDataLen = sizeof(DWORD); - return TRUE; - } - case HP_HASHVAL: { - if (!computeDigest(*hash)) { - wibo::lastError = ERROR_NOT_SUPPORTED; - return FALSE; - } - DWORD required = static_cast(hash->digest.size()); + DWORD required = sizeof(DWORD); if (!pbData) { *pdwDataLen = required; return TRUE; @@ -368,10 +195,38 @@ BOOL WIN_FUNC CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam, BYTE *pbData, D wibo::lastError = ERROR_INSUFFICIENT_BUFFER; return FALSE; } - memcpy(pbData, hash->digest.data(), required); + DWORD size = hashSizeForAlgid(hash->algid); + if (size == 0) { + wibo::lastError = ERROR_NOT_SUPPORTED; + return FALSE; + } + memcpy(pbData, &size, required); *pdwDataLen = required; return TRUE; } + case HP_HASHVAL: { + if (!computeDigest(*hash)) { + wibo::lastError = ERROR_NOT_SUPPORTED; + return FALSE; + } + DWORD size = hashSizeForAlgid(hash->algid); + if (size == 0) { + wibo::lastError = ERROR_NOT_SUPPORTED; + return FALSE; + } + if (!pbData) { + *pdwDataLen = size; + return TRUE; + } + if (*pdwDataLen < size) { + *pdwDataLen = size; + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return FALSE; + } + memcpy(pbData, hash->digest, size); + *pdwDataLen = size; + return TRUE; + } default: wibo::lastError = ERROR_NOT_SUPPORTED; return FALSE;