diff --git a/openrct2.common.props b/openrct2.common.props
index 4102691a30..3b41540661 100644
--- a/openrct2.common.props
+++ b/openrct2.common.props
@@ -61,7 +61,7 @@
/utf-8 /std:c++latest /permissive- /Zc:externConstexpr
- imm32.lib;version.lib;winmm.lib;crypt32.lib;wldap32.lib;%(AdditionalDependencies)
+ imm32.lib;version.lib;winmm.lib;crypt32.lib;wldap32.lib;bcrypt.lib;%(AdditionalDependencies)
/OPT:NOLBR /ignore:4099 %(AdditionalOptions)
diff --git a/src/openrct2/core/Hash.cpp b/src/openrct2/core/Hash.cpp
index dd8b23f50d..adefba0d61 100644
--- a/src/openrct2/core/Hash.cpp
+++ b/src/openrct2/core/Hash.cpp
@@ -18,12 +18,17 @@
#include
#include
-// #define __USE_WINSSL__
+#if defined(_WIN32) && !defined(__USE_OPENSSL__)
+#define __USE_CNG__
+#endif
-#ifdef __USE_WINSSL__
+#ifdef __USE_CNG__
+// CNG: Cryptography API: Next Generation (CNG)
+// available in Windows Vista onwards.
#define WIN32_LEAN_AND_MEAN
#include
-#include
+#include
+#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#else
#include
#endif
@@ -31,9 +36,10 @@
class Sha1Algorithm final : public HashAlgorithm<20>
{
private:
-#ifdef __USE_WINSSL__
- HCRYPTPROV _hProv{};
- HCRYPTHASH _hHash{};
+#ifdef __USE_CNG__
+ BCRYPT_ALG_HANDLE _hAlg{};
+ BCRYPT_HASH_HANDLE _hHash{};
+ PBYTE _pbHashObject{};
#else
EVP_MD_CTX * _ctx{};
#endif
@@ -41,18 +47,33 @@ private:
public:
Sha1Algorithm()
{
-#ifdef __USE_WINSSL__
- if (!CryptAcquireContextW(&_hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
+#ifdef __USE_CNG__
+ // TODO BCRYPT_HASH_REUSABLE_FLAG only available from Windows 8
+ auto status = BCryptOpenAlgorithmProvider(&_hAlg, BCRYPT_SHA1_ALGORITHM, nullptr, BCRYPT_HASH_REUSABLE_FLAG);
+ if (!NT_SUCCESS(status))
{
- auto dwStatus = GetLastError();
- throw std::runtime_error("CryptAcquireContext failed : " + std::to_string(dwStatus));
+ throw std::runtime_error("BCryptOpenAlgorithmProvider failed: " + std::to_string(status));
}
- if (!CryptCreateHash(_hProv, CALG_SHA1, 0, 0, &_hHash))
+ // Calculate the size of the buffer to hold the hash object
+ DWORD cbHashObject{};
+ DWORD cbData{};
+ status = BCryptGetProperty(_hAlg, BCRYPT_OBJECT_LENGTH, (PBYTE)&cbHashObject, sizeof(DWORD), &cbData, 0);
+ if (!NT_SUCCESS(status))
{
- auto dwStatus = GetLastError();
- CryptReleaseContext(_hProv, 0);
- throw std::runtime_error("CryptCreateHash failed : " + std::to_string(dwStatus));
+ throw std::runtime_error("BCryptGetProperty failed: " + std::to_string(status));
+ }
+
+ // Create a hash
+ _pbHashObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbHashObject);
+ if (_pbHashObject == nullptr)
+ {
+ throw std::bad_alloc();
+ }
+ status = BCryptCreateHash(_hAlg, &_hHash, _pbHashObject, cbHashObject, nullptr, 0, 0);
+ if (!NT_SUCCESS(status))
+ {
+ throw std::runtime_error("BCryptCreateHash failed: " + std::to_string(status));
}
#else
_ctx = EVP_MD_CTX_create();
@@ -70,9 +91,10 @@ public:
~Sha1Algorithm()
{
-#ifdef __USE_WINSSL__
- CryptDestroyHash(_hHash);
- CryptReleaseContext(_hProv, 0);
+#ifdef __USE_CNG__
+ BCryptCloseAlgorithmProvider(_hAlg, 0);
+ BCryptDestroyHash(_hHash);
+ HeapFree(GetProcessHeap(), 0, _pbHashObject);
#else
EVP_MD_CTX_destroy(_ctx);
#endif
@@ -80,13 +102,9 @@ public:
void Clear() override
{
-#ifdef __USE_WINSSL__
- CryptDestroyHash(_hHash);
- if (!CryptCreateHash(_hProv, CALG_SHA1, 0, 0, &_hHash))
- {
- auto dwStatus = GetLastError();
- throw std::runtime_error("CryptCreateHash failed : " + std::to_string(dwStatus));
- }
+#ifdef __USE_CNG__
+ // Finishing the current digest clears the state ready for a new digest
+ Finish();
#else
if (EVP_DigestInit_ex(_ctx, EVP_sha1(), nullptr) <= 0)
{
@@ -97,11 +115,11 @@ public:
void Update(const void * data, size_t dataLen) override
{
-#ifdef __USE_WINSSL__
- if (!CryptHashData(_hHash, (const BYTE *)data, (DWORD)dataLen, 0))
+#ifdef __USE_CNG__
+ auto status = BCryptHashData(_hHash, (PBYTE)data, (ULONG)dataLen, 0);
+ if (!NT_SUCCESS(status))
{
- auto dwStatus = GetLastError();
- throw std::runtime_error("CryptHashData failed: " + std::to_string(dwStatus));
+ throw std::runtime_error("BCryptHashData failed: " + std::to_string(status));
}
#else
if (EVP_DigestUpdate(_ctx, data, dataLen) <= 0)
@@ -113,13 +131,12 @@ public:
std::array Finish() override
{
-#ifdef __USE_WINSSL__
+#ifdef __USE_CNG__
std::array result;
- auto cbHash = (DWORD)result.size();
- if (!CryptGetHashParam(_hHash, HP_HASHVAL, result.data(), &cbHash, 0))
+ auto status = BCryptFinishHash(_hHash, result.data(), (ULONG)result.size(), 0);
+ if (!NT_SUCCESS(status))
{
- auto dwStatus = GetLastError();
- throw std::runtime_error("CryptGetHashParam failed: " + std::to_string(dwStatus));
+ throw std::runtime_error("BCryptFinishHash failed: " + std::to_string(status));
}
return result;
#else