diff --git a/include/WinStd/BCrypt.h b/include/WinStd/BCrypt.h new file mode 100644 index 00000000..03b15394 --- /dev/null +++ b/include/WinStd/BCrypt.h @@ -0,0 +1,309 @@ +/* + SPDX-License-Identifier: MIT + Copyright © 1991-2025 Amebis + Copyright © 2016 GÉANT +*/ + +/// \defgroup WinStdBCryptAPI Cryptography API + +#pragma once + +#include "Common.h" +#include "Win.h" +#include +#include +#include +#include + +/// \addtogroup WinStdBCryptAPI +/// @{ + +/// +/// Implements default constructors and operators to prevent their auto-generation by compiler. +/// +#define WINSTD_BCRYPT_HWO_IMPL(C, T) \ +public: \ + C ( ) noexcept {} \ + C (_In_ const C &h) { if (h.m_h != invalid) duplicate_internal(h); } \ + C (_Inout_ C &&h) noexcept : bcrypt_handle_with_object(std::move(h)) {} \ + C& operator=(_In_ const C &h) { bcrypt_handle_with_object::operator=( h ); return *this; } \ + C& operator=(_Inout_ C &&h) noexcept { bcrypt_handle_with_object::operator=(std::move(h)); return *this; } \ +private: + +template +static _Must_inspect_result_ NTSTATUS BCryptSignHash(_In_ BCRYPT_KEY_HANDLE hKey, _In_opt_ VOID *pPaddingInfo, _In_reads_bytes_(cbInput) PUCHAR pbInput, _In_ ULONG cbInput, _Out_ std::vector<_Ty, _Ax> &aOutput, _In_ ULONG dwFlags) +{ + ULONG cbSignature = 0; + NTSTATUS status = BCryptSignHash(hKey, pPaddingInfo, pbInput, cbInput, NULL, 0, &cbSignature, dwFlags); + if (!status) { + aOutput.resize((cbSignature + sizeof(_Ty) - 1) / sizeof(_Ty)); + status = BCryptSignHash(hKey, NULL, pbInput, cbInput, aOutput.data(), cbSignature, &cbSignature, dwFlags); + if (!status) + aOutput.resize((cbSignature + sizeof(_Ty) - 1) / sizeof(_Ty)); + } + return status; +} + +template +static _Must_inspect_result_ NTSTATUS BCryptExportKey(_In_ BCRYPT_KEY_HANDLE hKey, _In_opt_ BCRYPT_KEY_HANDLE hExportKey, _In_z_ LPCWSTR pszBlobType, _Out_ std::vector<_Ty, _Ax> &aOutput, _In_ ULONG dwFlags) +{ + DWORD cbBlob = 0; + NTSTATUS status = BCryptExportKey(hKey, hExportKey, pszBlobType, NULL, 0, &cbBlob, dwFlags); + if (!status) { + aOutput.resize((cbBlob + sizeof(_Ty) - 1) / sizeof(_Ty)); + status = BCryptExportKey(hKey, hExportKey, pszBlobType, aOutput.data(), cbBlob, &cbBlob, dwFlags); + if (!status) + aOutput.resize((cbBlob + sizeof(_Ty) - 1) / sizeof(_Ty)); + } + return status; +} + +/// @} + +namespace winstd +{ + /// \addtogroup WinStdBCryptAPI + /// @{ + + /// + /// BCRYPT_ALG_HANDLE wrapper class + /// + /// \sa [BCryptOpenAlgorithmProvider function](https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptopenalgorithmprovider) + /// + class bcrypt_algorithm_provider : public handle + { + WINSTD_HANDLE_IMPL(bcrypt_algorithm_provider, BCRYPT_ALG_HANDLE, NULL) + + public: + /// + /// Releases the algorithm provider. + /// + /// \sa [BCryptCloseAlgorithmProvider function](https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptclosealgorithmprovider) + /// + virtual ~bcrypt_algorithm_provider() + { + if (m_h != invalid) + free_internal(); + } + + protected: + /// + /// Releases the algorithm provider. + /// + /// \sa [BCryptCloseAlgorithmProvider function](https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptclosealgorithmprovider) + /// + void free_internal() noexcept override + { + BCryptCloseAlgorithmProvider(m_h, 0); + } + }; + + template + class bcrypt_handle_with_object : public handle + { + public: + bcrypt_handle_with_object() noexcept {} + + /// + /// Move constructor + /// + /// \param[inout] h A rvalue reference of another object + /// + bcrypt_handle_with_object(_Inout_ bcrypt_handle_with_object&& h) noexcept : + handle(std::move(h)), + m_hash_object(std::move(h.m_hash_object)) + { + } + + /// + /// Duplicates the object. + /// + /// \param[in] h Object + /// + bcrypt_handle_with_object& operator=(_In_ const bcrypt_handle_with_object& h) + { + if (this != std::addressof(h)) { + if (m_h != invalid) + free_internal(); + if (h) + duplicate_internal(h); + else + m_h = invalid; + } + return *this; + } + + /// + /// Move assignment + /// + /// \param[inout] h A rvalue reference of another object + /// + bcrypt_handle_with_object& operator=(_Inout_ bcrypt_handle_with_object&& h) noexcept + { + if (this != std::addressof(h)) { + handle::operator=(std::move(h)); + m_hash_object = std::move(h.m_hash_object); + } + return *this; + } + + protected: + /// + /// Abstract member function that must be implemented by child classes to do the actual object duplication. + /// + /// \param[inout] h A reference of another object + /// + virtual void duplicate_internal(_In_ const bcrypt_handle_with_object& h) = 0; + + protected: + std::vector m_hash_object; + }; + + /// + /// BCRYPT_HASH_HANDLE wrapper class + /// + class bcrypt_hash : public bcrypt_handle_with_object + { + WINSTD_BCRYPT_HWO_IMPL(bcrypt_hash, BCRYPT_HASH_HANDLE) + + public: + /// + /// Creates a hash. + /// + /// \sa [BCryptCreateHash function](https://msdn.microsoft.com/en-us/library/windows/desktop/aa379908.aspx) + /// + bcrypt_hash(_In_ BCRYPT_ALG_HANDLE hAlgorithm, _In_reads_bytes_opt_(cbSecret) PUCHAR pbSecret, _In_ ULONG cbSecret, _In_ ULONG dwFlags) + { + ULONG hashObjectSize, bytesRead; + NTSTATUS status = BCryptGetProperty(hAlgorithm, BCRYPT_OBJECT_LENGTH, reinterpret_cast(&hashObjectSize), sizeof(hashObjectSize), &bytesRead, 0); + if (status || bytesRead != sizeof(hashObjectSize)) + throw ntstatus_error(status, "Failed to get hash object size"); + m_hash_object.resize(hashObjectSize); + status = BCryptCreateHash(hAlgorithm, &m_h, m_hash_object.data(), static_cast(m_hash_object.size()), pbSecret, cbSecret, dwFlags); + if (status) + throw ntstatus_error(status, "Failed to create hash"); + } + + /// + /// Destroys the hash. + /// + /// \sa [BCryptDestroyHash function](https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptdestroyhash) + /// + virtual ~bcrypt_hash() + { + if (m_h != invalid) + free_internal(); + } + + protected: + /// + /// Destroys the hash. + /// + /// \sa [BCryptDestroyHash function](https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptdestroyhash) + /// + void free_internal() noexcept override + { + BCryptDestroyHash(m_h); + } + + /// + /// Duplicates an existing hash or Message Authentication Code (MAC) object. + /// + /// \sa [BCryptDuplicateHash function](https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptduplicatehash) + /// + void duplicate_internal(_In_ const bcrypt_handle_with_object& h) override + { + auto h2 = reinterpret_cast(&h); + m_hash_object.resize(h2->m_hash_object.size()); + assert(m_hash_object.size() < ULONG_MAX); + NTSTATUS status = BCryptDuplicateHash(h2->m_h, &m_h, m_hash_object.data(), static_cast(m_hash_object.size()), 0); + if (status) + throw ntstatus_error(status, "Failed to duplicate hash"); + } + }; + + /// + /// BCRYPT_KEY_HANDLE wrapper class for symetrical keys + /// + class bcrypt_key : public bcrypt_handle_with_object + { + WINSTD_BCRYPT_HWO_IMPL(bcrypt_key, BCRYPT_KEY_HANDLE) + + public: + /// + /// Destroys the key. + /// + /// \sa [BCryptDestroyKey function](https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptdestroykey) + /// + virtual ~bcrypt_key() + { + if (m_h != invalid) + free_internal(); + } + + protected: + /// + /// Destroys the key. + /// + /// \sa [BCryptDestroyKey function](https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptdestroykey) + /// + void free_internal() noexcept override + { + BCryptDestroyKey(m_h); + } + + /// + /// Creates a duplicate of a symmetric key. + /// + /// \sa [BCryptDuplicateKey function](https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptduplicatekey) + /// + void duplicate_internal(_In_ const bcrypt_handle_with_object& h) override + { + auto h2 = reinterpret_cast(&h); + m_key_object.resize(h2->m_key_object.size()); + assert(m_key_object.size() < ULONG_MAX); + NTSTATUS status = BCryptDuplicateKey(h2->m_h, &m_h, m_key_object.data(), static_cast(m_key_object.size()), 0); + if (status) + throw ntstatus_error(status, "Failed to duplicate key"); + } + + protected: + std::vector m_key_object; + }; + + /// + /// BCRYPT_KEY_HANDLE wrapper class for keypairs + /// + /// \sa [BCryptImportKeyPair function](https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptimportkeypair) + /// \sa [BCryptGenerateKeyPair function](https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgeneratekeypair) + /// + class bcrypt_keypair : public handle + { + WINSTD_HANDLE_IMPL(bcrypt_keypair, BCRYPT_KEY_HANDLE, NULL) + + public: + /// + /// Releases the algorithm provider. + /// + /// \sa [BCryptCloseAlgorithmProvider function](https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptclosealgorithmprovider) + /// + virtual ~bcrypt_keypair() + { + if (m_h != invalid) + free_internal(); + } + + protected: + /// + /// Releases the algorithm provider. + /// + /// \sa [BCryptCloseAlgorithmProvider function](https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptclosealgorithmprovider) + /// + void free_internal() noexcept override + { + BCryptCloseAlgorithmProvider(m_h, 0); + } + }; + + /// @} +}