Implement CNG implementation bar pem reading

This commit is contained in:
Ted John 2018-05-26 00:11:01 +01:00
parent 9e214258c3
commit 3b2b15c0f3
4 changed files with 134 additions and 28 deletions

View File

@ -61,7 +61,7 @@
<AdditionalOptions>/utf-8 /std:c++latest /permissive- /Zc:externConstexpr</AdditionalOptions>
</ClCompile>
<Link>
<AdditionalDependencies>imm32.lib;version.lib;winmm.lib;crypt32.lib;wldap32.lib;bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>imm32.lib;version.lib;winmm.lib;crypt32.lib;wldap32.lib;bcrypt.lib;ncrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalOptions>/OPT:NOLBR /ignore:4099 %(AdditionalOptions)</AdditionalOptions>
</Link>
</ItemDefinitionGroup>

View File

@ -18,22 +18,38 @@
#define __USE_CNG__
#endif
#undef __USE_CNG__
#ifdef __USE_CNG__
#include "Crypt.h"
#include "../platform/Platform2.h"
#include <stdexcept>
#include <string>
#include <tuple>
// CNG: Cryptography API: Next Generation (CNG)
// available in Windows Vista onwards.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <bcrypt.h>
#include <ncrypt.h>
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
static void CngThrowOnBadStatus(const std::string_view& name, NTSTATUS status)
{
if (!NT_SUCCESS(status))
{
throw std::runtime_error(std::string(name) + " failed: " + std::to_string(status));
}
}
static void ThrowBadAllocOnNull(const void * ptr)
{
if (ptr == nullptr)
{
throw std::bad_alloc();
}
}
template<typename TBase>
class CngHashAlgorithm final : public TBase
{
@ -76,10 +92,7 @@ public:
TBase * Update(const void * data, size_t dataLen) override
{
auto status = BCryptHashData(_hHash, (PBYTE)data, (ULONG)dataLen, 0);
if (!NT_SUCCESS(status))
{
throw std::runtime_error("BCryptHashData failed: " + std::to_string(status));
}
CngThrowOnBadStatus("BCryptHashData", status);
return this;
}
@ -87,10 +100,7 @@ public:
{
typename TBase::Result 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));
}
CngThrowOnBadStatus("BCryptFinishHash", status);
return result;
}
@ -99,31 +109,19 @@ private:
{
auto flags = _reusable ? BCRYPT_HASH_REUSABLE_FLAG : 0;
auto status = BCryptOpenAlgorithmProvider(&_hAlg, _algName, nullptr, flags);
if (!NT_SUCCESS(status))
{
throw std::runtime_error("BCryptOpenAlgorithmProvider failed: " + std::to_string(status));
}
CngThrowOnBadStatus("BCryptOpenAlgorithmProvider", 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));
}
CngThrowOnBadStatus("BCryptGetProperty", status);
// Create a hash
_pbHashObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbHashObject);
if (_pbHashObject == nullptr)
{
throw std::bad_alloc();
}
ThrowBadAllocOnNull(_pbHashObject);
status = BCryptCreateHash(_hAlg, &_hHash, _pbHashObject, cbHashObject, nullptr, 0, 0);
if (!NT_SUCCESS(status))
{
throw std::runtime_error("BCryptCreateHash failed: " + std::to_string(status));
}
CngThrowOnBadStatus("BCryptCreateHash", status);
}
void Dispose()
@ -138,6 +136,98 @@ private:
}
};
class CngRsaKey final : public RsaKey
{
public:
NCRYPT_KEY_HANDLE GetKeyHandle() const { return _hKey; }
void SetPrivate(const std::string_view& pem) override
{
SetKey(pem, true);
}
void SetPublic(const std::string_view& pem) override
{
SetKey(pem, false);
}
std::string GetPrivate() override { return GetKey(true); }
std::string GetPublic() override { return GetKey(false); }
private:
NCRYPT_KEY_HANDLE _hKey{};
void SetKey(const std::string_view& pem, bool isPrivate)
{
throw std::runtime_error("Not implemented");
}
std::string GetKey(bool isPrivate)
{
throw std::runtime_error("Not implemented");
}
};
class CngRsaAlgorithm final : public RsaAlgorithm
{
public:
std::vector<uint8_t> SignData(const RsaKey& key, const void * data, size_t dataLen) override
{
auto hKey = static_cast<const CngRsaKey&>(key).GetKeyHandle();
auto [cbHash, pbHash] = HashData(data, dataLen);
auto [cbSignature, pbSignature] = std::tuple<DWORD, PBYTE>();
try
{
BCRYPT_PKCS1_PADDING_INFO paddingInfo{ BCRYPT_SHA256_ALGORITHM };
auto status = NCryptSignHash(hKey, &paddingInfo, pbHash, cbHash, NULL, 0, &cbSignature, BCRYPT_PAD_PKCS1);
CngThrowOnBadStatus("NCryptSignHash", status);
pbSignature = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbSignature);
ThrowBadAllocOnNull(pbSignature);
status = NCryptSignHash(hKey, &paddingInfo, pbHash, cbHash, pbSignature, cbSignature, &cbSignature, BCRYPT_PAD_PKCS1);
CngThrowOnBadStatus("NCryptSignHash", status);
auto result = std::vector<uint8_t>(pbSignature, pbSignature + cbSignature);
HeapFree(GetProcessHeap(), 0, pbSignature);
return result;
}
catch (std::exception&)
{
HeapFree(GetProcessHeap(), 0, pbHash);
HeapFree(GetProcessHeap(), 0, pbSignature);
throw;
}
}
bool VerifyData(const RsaKey& key, const void * data, size_t dataLen, const void * sig, size_t sigLen) override
{
auto hKey = static_cast<const CngRsaKey&>(key).GetKeyHandle();
auto [cbHash, pbHash] = HashData(data, dataLen);
auto [cbSignature, pbSignature] = ToHeap(sig, sigLen);
BCRYPT_PKCS1_PADDING_INFO paddingInfo { BCRYPT_SHA256_ALGORITHM };
auto status = NCryptVerifySignature(hKey, &paddingInfo, pbHash, cbHash, pbSignature, cbSignature, BCRYPT_PAD_PKCS1);
HeapFree(GetProcessHeap(), 0, pbSignature);
return status == ERROR_SUCCESS;
}
private:
static std::tuple<DWORD, PBYTE> HashData(const void * data, size_t dataLen)
{
auto hash = Hash::SHA256(data, dataLen);
return ToHeap(hash.data(), hash.size());
}
static std::tuple<DWORD, PBYTE> ToHeap(const void * data, size_t dataLen)
{
auto cbHash = (DWORD)dataLen;
auto pbHash = (PBYTE)HeapAlloc(GetProcessHeap(), 0, dataLen);
ThrowBadAllocOnNull(pbHash);
std::memcpy(pbHash, data, dataLen);
return std::make_tuple(cbHash, pbHash);
}
};
namespace Hash
{
std::unique_ptr<Sha1Algorithm> CreateSHA1()
@ -149,6 +239,16 @@ namespace Hash
{
return std::make_unique<CngHashAlgorithm<Sha256Algorithm>>(BCRYPT_SHA256_ALGORITHM);
}
std::unique_ptr<RsaAlgorithm> CreateRSA()
{
return std::make_unique<CngRsaAlgorithm>();
}
std::unique_ptr<RsaKey> CreateRSAKey()
{
return std::make_unique<CngRsaKey>();
}
}
#endif

View File

@ -18,7 +18,6 @@
#define __USE_CNG__
#endif
#undef __USE_CNG__
#ifndef __USE_CNG__
#include "Crypt.h"

View File

@ -66,4 +66,11 @@ namespace Hash
->Update(data, dataLen)
->Finish();
}
inline Sha256Algorithm::Result SHA256(const void * data, size_t dataLen)
{
return CreateSHA256()
->Update(data, dataLen)
->Finish();
}
}