From 320b149b58b185e44b2d3519c95b4ac2ab574746 Mon Sep 17 00:00:00 2001 From: Ted John Date: Wed, 23 May 2018 13:18:11 +0100 Subject: [PATCH] Change Windows builds to use CNG API --- openrct2.common.props | 2 +- src/openrct2/core/Hash.cpp | 83 +++++++++++++++++++++++--------------- 2 files changed, 51 insertions(+), 34 deletions(-) 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