Only reuse CNG object if Windows 8+

This commit is contained in:
Ted John 2018-05-23 19:42:42 +01:00
parent 320b149b58
commit ea22c672d0
3 changed files with 97 additions and 59 deletions

View File

@ -15,6 +15,7 @@
#pragma endregion #pragma endregion
#include "Hash.h" #include "Hash.h"
#include "../platform/Platform2.h"
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
@ -40,6 +41,7 @@ private:
BCRYPT_ALG_HANDLE _hAlg{}; BCRYPT_ALG_HANDLE _hAlg{};
BCRYPT_HASH_HANDLE _hHash{}; BCRYPT_HASH_HANDLE _hHash{};
PBYTE _pbHashObject{}; PBYTE _pbHashObject{};
bool _reusable{};
#else #else
EVP_MD_CTX * _ctx{}; EVP_MD_CTX * _ctx{};
#endif #endif
@ -48,63 +50,30 @@ public:
Sha1Algorithm() Sha1Algorithm()
{ {
#ifdef __USE_CNG__ #ifdef __USE_CNG__
// TODO BCRYPT_HASH_REUSABLE_FLAG only available from Windows 8 // BCRYPT_HASH_REUSABLE_FLAG only available from Windows 8
auto status = BCryptOpenAlgorithmProvider(&_hAlg, BCRYPT_SHA1_ALGORITHM, nullptr, BCRYPT_HASH_REUSABLE_FLAG); _reusable = Platform::IsOSVersionAtLeast(6, 2, 0);
if (!NT_SUCCESS(status))
{
throw std::runtime_error("BCryptOpenAlgorithmProvider failed: " + std::to_string(status));
}
// 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))
{
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();
if (_ctx == nullptr)
{
throw std::runtime_error("EVP_MD_CTX_create failed");
}
if (EVP_DigestInit_ex(_ctx, EVP_sha1(), nullptr) <= 0)
{
EVP_MD_CTX_destroy(_ctx);
throw std::runtime_error("EVP_DigestInit_ex failed");
}
#endif #endif
Initialise();
} }
~Sha1Algorithm() ~Sha1Algorithm()
{ {
#ifdef __USE_CNG__ Dispose();
BCryptCloseAlgorithmProvider(_hAlg, 0);
BCryptDestroyHash(_hHash);
HeapFree(GetProcessHeap(), 0, _pbHashObject);
#else
EVP_MD_CTX_destroy(_ctx);
#endif
} }
void Clear() override void Clear() override
{ {
#ifdef __USE_CNG__ #ifdef __USE_CNG__
// Finishing the current digest clears the state ready for a new digest if (_reusable)
Finish(); {
// Finishing the current digest clears the state ready for a new digest
Finish();
}
else
{
Dispose();
Initialise();
}
#else #else
if (EVP_DigestInit_ex(_ctx, EVP_sha1(), nullptr) <= 0) if (EVP_DigestInit_ex(_ctx, EVP_sha1(), nullptr) <= 0)
{ {
@ -154,6 +123,67 @@ public:
return result; return result;
#endif #endif
} }
private:
void Initialise()
{
#ifdef __USE_CNG__
auto flags = _reusable ? BCRYPT_HASH_REUSABLE_FLAG : 0;
auto status = BCryptOpenAlgorithmProvider(&_hAlg, BCRYPT_SHA1_ALGORITHM, nullptr, flags);
if (!NT_SUCCESS(status))
{
throw std::runtime_error("BCryptOpenAlgorithmProvider failed: " + std::to_string(status));
}
// 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))
{
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();
if (_ctx == nullptr)
{
throw std::runtime_error("EVP_MD_CTX_create failed");
}
if (EVP_DigestInit_ex(_ctx, EVP_sha1(), nullptr) <= 0)
{
EVP_MD_CTX_destroy(_ctx);
throw std::runtime_error("EVP_DigestInit_ex failed");
}
#endif
}
void Dispose()
{
#ifdef __USE_CNG__
BCryptCloseAlgorithmProvider(_hAlg, 0);
BCryptDestroyHash(_hHash);
HeapFree(GetProcessHeap(), 0, _pbHashObject);
_hAlg = {};
_hHash = {};
_pbHashObject = {};
#else
EVP_MD_CTX_destroy(_ctx);
_ctx = {};
#endif
}
}; };
namespace Hash namespace Hash

View File

@ -197,15 +197,10 @@ namespace Platform
return result; return result;
} }
/** bool IsOSVersionAtLeast(uint32 major, uint32 minor, uint32 build)
* Checks if the current version of Windows supports ANSI colour codes.
* From Windows 10, build 10586 ANSI escape colour codes can be used on stdout.
*/
static bool HasANSIColourSupport()
{ {
const DWORD MINV_MAJOR = 10, MINV_MINOR = 0, MINV_BUILD = 10586;
bool result = false; bool result = false;
HMODULE hModule = GetModuleHandleA("ntdll.dll"); auto hModule = GetModuleHandleA("ntdll.dll");
if (hModule != nullptr) if (hModule != nullptr)
{ {
using RtlGetVersionPtr = NTSTATUS(WINAPI *)(PRTL_OSVERSIONINFOW); using RtlGetVersionPtr = NTSTATUS(WINAPI *)(PRTL_OSVERSIONINFOW);
@ -216,11 +211,11 @@ namespace Platform
rovi.dwOSVersionInfoSize = sizeof(rovi); rovi.dwOSVersionInfoSize = sizeof(rovi);
if (fn(&rovi) == 0) if (fn(&rovi) == 0)
{ {
if (rovi.dwMajorVersion > MINV_MAJOR || if (rovi.dwMajorVersion > major ||
(rovi.dwMajorVersion == MINV_MAJOR && (rovi.dwMajorVersion == major &&
(rovi.dwMinorVersion > MINV_MINOR || (rovi.dwMinorVersion > minor ||
(rovi.dwMinorVersion == MINV_MINOR && (rovi.dwMinorVersion == minor &&
rovi.dwBuildNumber >= MINV_BUILD)))) rovi.dwBuildNumber >= build))))
{ {
result = true; result = true;
} }
@ -230,6 +225,15 @@ namespace Platform
return result; return result;
} }
/**
* Checks if the current version of Windows supports ANSI colour codes.
* From Windows 10, build 10586 ANSI escape colour codes can be used on stdout.
*/
static bool HasANSIColourSupport()
{
return IsOSVersionAtLeast(10, 0, 10586);
}
static void EnableANSIConsole() static void EnableANSIConsole()
{ {
if (HasANSIColourSupport()) if (HasANSIColourSupport())

View File

@ -45,5 +45,9 @@ namespace Platform
std::string FormatShortDate(std::time_t timestamp); std::string FormatShortDate(std::time_t timestamp);
std::string FormatTime(std::time_t timestamp); std::string FormatTime(std::time_t timestamp);
#ifdef _WIN32
bool IsOSVersionAtLeast(uint32 major, uint32 minor, uint32 build);
#endif
bool IsColourTerminalSupported(); bool IsColourTerminalSupported();
} // namespace Platform } // namespace Platform