#pragma region Copyright (c) 2018 OpenRCT2 Developers /***************************************************************************** * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. * * OpenRCT2 is the work of many authors, a full list can be found in contributors.md * For more information, visit https://github.com/OpenRCT2/OpenRCT2 * * OpenRCT2 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * A full copy of the GNU General Public License can be found in licence.txt *****************************************************************************/ #pragma endregion #include "Hash.h" #include "../platform/Platform2.h" #include #include #if defined(_WIN32) && !defined(__USE_OPENSSL__) #define __USE_CNG__ #endif #ifdef __USE_CNG__ // CNG: Cryptography API: Next Generation (CNG) // available in Windows Vista onwards. #define WIN32_LEAN_AND_MEAN #include #include #define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) #else #include #endif class Sha1Algorithm final : public HashAlgorithm<20> { private: #ifdef __USE_CNG__ BCRYPT_ALG_HANDLE _hAlg{}; BCRYPT_HASH_HANDLE _hHash{}; PBYTE _pbHashObject{}; bool _reusable{}; #else EVP_MD_CTX * _ctx{}; #endif public: Sha1Algorithm() { #ifdef __USE_CNG__ // BCRYPT_HASH_REUSABLE_FLAG only available from Windows 8 _reusable = Platform::IsOSVersionAtLeast(6, 2, 0); #endif Initialise(); } ~Sha1Algorithm() { Dispose(); } void Clear() override { #ifdef __USE_CNG__ if (_reusable) { // Finishing the current digest clears the state ready for a new digest Finish(); } else { Dispose(); Initialise(); } #else if (EVP_DigestInit_ex(_ctx, EVP_sha1(), nullptr) <= 0) { throw std::runtime_error("EVP_DigestInit_ex failed"); } #endif } void Update(const void * data, size_t dataLen) override { #ifdef __USE_CNG__ auto status = BCryptHashData(_hHash, (PBYTE)data, (ULONG)dataLen, 0); if (!NT_SUCCESS(status)) { throw std::runtime_error("BCryptHashData failed: " + std::to_string(status)); } #else if (EVP_DigestUpdate(_ctx, data, dataLen) <= 0) { throw std::runtime_error("EVP_DigestUpdate failed"); } #endif } std::array Finish() override { #ifdef __USE_CNG__ std::array result; auto status = BCryptFinishHash(_hHash, result.data(), (ULONG)result.size(), 0); if (!NT_SUCCESS(status)) { throw std::runtime_error("BCryptFinishHash failed: " + std::to_string(status)); } return result; #else std::array result; unsigned int digestSize{}; if (EVP_DigestFinal(_ctx, result.data(), &digestSize) <= 0) { EVP_MD_CTX_destroy(_ctx); throw std::runtime_error("EVP_DigestFinal failed"); } if (digestSize != result.size()) { throw std::runtime_error("Expected digest size to equal " + std::to_string(result.size())); } return result; #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 { std::unique_ptr> CreateSHA1() { return std::make_unique(); } std::array SHA1(const void * data, size_t dataLen) { Sha1Algorithm sha1; sha1.Update(data, dataLen); return sha1.Finish(); } }