diff --git a/build/wxExtend.vcxproj b/build/wxExtend.vcxproj index 8359984..5e70355 100644 --- a/build/wxExtend.vcxproj +++ b/build/wxExtend.vcxproj @@ -21,6 +21,7 @@ + Create Create @@ -33,6 +34,7 @@ + diff --git a/build/wxExtend.vcxproj.filters b/build/wxExtend.vcxproj.filters index 68925a3..6cca51e 100644 --- a/build/wxExtend.vcxproj.filters +++ b/build/wxExtend.vcxproj.filters @@ -31,6 +31,9 @@ Source Files + + Source Files + @@ -48,6 +51,9 @@ Header Files + + Header Files + diff --git a/include/wxex/crypto.h b/include/wxex/crypto.h new file mode 100644 index 0000000..a4f1f12 --- /dev/null +++ b/include/wxex/crypto.h @@ -0,0 +1,260 @@ +/* + Copyright 2016 Amebis + + This file is part of wxExtend. + + wxExtend 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. + + wxExtend is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with wxExtend. If not, see . +*/ + +#pragma once + +#include "common.h" + +#include +#include + +#include + + +/// +/// Cryptographics Session Base Class +/// +class WXEXTEND_API wxCryptoSession +{ +protected: + HCRYPTPROV m_h; ///< Session Handle + +public: + wxCryptoSession(); + virtual ~wxCryptoSession(); + + + /// + /// Has the session creation been successful? + /// + /// \returns + /// - true if creation succeeded + /// - false otherwise + /// + inline bool IsOk() const + { + return m_h != NULL; + } + + + /// + /// \returns Session handle to be used in native API calls. + /// + inline operator HCRYPTPROV() const + { + return m_h; + } +}; + + +/// +/// RSA AES Cryptographics Session +/// +class WXEXTEND_API wxCryptoSessionRSAAES : public wxCryptoSession +{ +public: + wxCryptoSessionRSAAES(); +}; + + +/// +/// Cryptographics Hash Base +/// +class WXEXTEND_API wxCryptoHash +{ +protected: + HCRYPTHASH m_h; ///< Hash Handle + +public: + wxCryptoHash(); + virtual ~wxCryptoHash(); + + + /// + /// Has the hash creation been successful? + /// + /// \returns + /// - true if creation succeeded + /// - false otherwise + /// + inline bool IsOk() const + { + return m_h != NULL; + } + + + /// + /// \returns Hash handle to be used in native API calls. + /// + inline operator HCRYPTHASH() const + { + return m_h; + } + + + /// + /// Hashes given block of data + /// + /// \param[in] data Pointer to memory block + /// \param[in] size Size of memory block in bytes + /// + /// \returns + /// - true if hashing succeeded + /// - false otherwise + /// + bool Hash(const void *data, size_t size); + + + /// + /// Hashes given block of data + /// + /// \param[in] data Memory block + /// + /// \returns + /// - true if hashing succeeded + /// - false otherwise + /// + inline bool Hash(const wxMemoryBuffer &data) + { + return Hash(data.GetData(), data.GetDataLen()); + } + + + /// + /// Converts string to UTF-8 and hashes it. + /// + /// \param[in] str String to hash + /// + /// \returns + /// - true if hashing succeeded + /// - false otherwise + /// + inline bool HashAsUTF8(const wxString &str) + { + const wxScopedCharBuffer buf(str.ToUTF8()); + return Hash((const char*)buf.data(), buf.length()); + } + + + /// + /// Signs the hash using session key + /// + /// \param[out] signature Digital signature + /// + /// \returns + /// - true if signing succeeded + /// - false otherwise + /// + bool Sign(wxMemoryBuffer &signature); + + + /// + /// Signs the hash using session key + /// + /// \returns Digital Signature + /// + inline wxMemoryBuffer Sign() + { + wxMemoryBuffer signature; + wxVERIFY(Sign(signature)); + return signature; + } +}; + + +/// +/// SHA-1 Cryptographics Hash +/// +class WXEXTEND_API wxCryptoHashSHA1 : public wxCryptoHash +{ +public: + wxCryptoHashSHA1(wxCryptoSession &session); +}; + + +/// +/// Cryptographics Key Base +/// +class WXEXTEND_API wxCryptoKey +{ +public: + wxCryptoKey(); + virtual ~wxCryptoKey(); + + + /// + /// Has the key creation been successful? + /// + /// \returns + /// - true if creation succeeded + /// - false otherwise + /// + inline bool IsOk() const + { + return m_h != NULL; + } + + + /// + /// \returns Key handle to be used in native API calls. + /// + inline operator HCRYPTKEY() const + { + return m_h; + } + + + bool ImportPrivate(wxCryptoSession &session, const void *data, size_t size); + bool ImportPublic(wxCryptoSession &session, const void *data, size_t size); + +protected: + HCRYPTKEY m_h; +}; + + +/// +/// Verifies if the hash matches signature and the public key +/// +/// \param[in] hash Hash +/// \param[in] signature_data Pointer to signature data +/// \param[in] signature_size Signature data size in bytes +/// \param[in] key Public key +/// +/// \returns +/// - true if verification succeeded and the hash matches +/// - false otherwise +/// +bool WXEXTEND_API wxCryptoVerifySignature(const wxCryptoHash &hash, const void *signature_data, size_t signature_size, const wxCryptoKey &key); + + +/// +/// Verifies if the hash matches signature and the public key +/// +/// \param[in] hash Hash +/// \param[in] signature Signature data +/// \param[in] key Public key +/// +/// \returns +/// - true if verification succeeded and the hash matches +/// - false otherwise +/// +inline bool wxCryptoVerifySignature(const wxCryptoHash &hash, const wxMemoryBuffer &signature, const wxCryptoKey &key) +{ + return ::wxCryptoVerifySignature(hash, signature.GetData(), signature.GetDataLen(), key); +} diff --git a/include/wxex/xml.h b/include/wxex/xml.h index 0dba1c4..10d03f4 100644 --- a/include/wxex/xml.h +++ b/include/wxex/xml.h @@ -19,6 +19,7 @@ #pragma once +#include "crypto.h" #include "common.h" #include @@ -113,4 +114,4 @@ inline wxString wxXmlEscapeAttr(_In_ const wxString& str) /// \param[in] node Root node /// /// -void WXEXTEND_API wxXmlHashNode(_In_ HCRYPTHASH hash, const wxXmlNode *node); +bool WXEXTEND_API wxXmlHashNode(_In_ wxCryptoHash &hash, const wxXmlNode *node); diff --git a/src/crypto.cpp b/src/crypto.cpp new file mode 100644 index 0000000..66a78d2 --- /dev/null +++ b/src/crypto.cpp @@ -0,0 +1,188 @@ +/* + Copyright 2015-2016 Amebis + + This file is part of wxExtend. + + wxExtend 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. + + wxExtend is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with wxExtend. If not, see . +*/ + +#include "stdafx.h" +#pragma comment(lib, "Crypt32.lib") + + +////////////////////////////////////////////////////////////////////////// +// wxCryptoSession +////////////////////////////////////////////////////////////////////////// + +wxCryptoSession::wxCryptoSession() : m_h(NULL) +{ +} + + +wxCryptoSession::~wxCryptoSession() +{ + if (m_h) + ::CryptReleaseContext(m_h, 0); +} + + +////////////////////////////////////////////////////////////////////////// +// wxCryptoSessionRSAAES +////////////////////////////////////////////////////////////////////////// + +wxCryptoSessionRSAAES::wxCryptoSessionRSAAES() +{ + ::CryptAcquireContext(&m_h, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT); +} + + +////////////////////////////////////////////////////////////////////////// +// wxCryptoHash +////////////////////////////////////////////////////////////////////////// + +wxCryptoHash::wxCryptoHash() : m_h(NULL) +{ +} + + +wxCryptoHash::~wxCryptoHash() +{ + if (m_h) + ::CryptDestroyHash(m_h); +} + + +bool wxCryptoHash::Hash(const void *data, size_t size) +{ + wxASSERT_MSG(m_h, wxT("object uninitialized")); + wxASSERT_MSG(data || !size, wxT("invalid parameter")); + + return ::CryptHashData(m_h, (const BYTE*)data, size, 0) ? true : false; +} + + +bool wxCryptoHash::Sign(wxMemoryBuffer &signature) +{ + // Try with the current buffer size first. + DWORD size = (DWORD)signature.GetBufSize(); + BYTE *data = (BYTE*)signature.GetWriteBuf(size); + if (::CryptSignHash(m_h, AT_SIGNATURE, NULL, 0, data, &size)) { + // The buffer was large enough. Set the actual size. + signature.SetDataLen(size); + } else if (::GetLastError() == ERROR_MORE_DATA) { + // The buffer was not big enough. Variable size contains the actual required size. Realloc and retry one more time. + wxCHECK(::CryptSignHash(m_h, AT_SIGNATURE, NULL, 0, data = (BYTE*)signature.GetWriteBuf(size), &size), false); + signature.SetDataLen(size); + } else { + signature.Clear(); + return false; + } + + // Reverse byte order, for consistent OpenSSL experience. + for (DWORD i = 0, j = size - 1; i < j; i++, j--) + wxSwap(data[i], data[j]); + + return true; +} + + +////////////////////////////////////////////////////////////////////////// +// wxCryptoHashSHA1 +////////////////////////////////////////////////////////////////////////// + +wxCryptoHashSHA1::wxCryptoHashSHA1(wxCryptoSession &session) +{ + ::CryptCreateHash(session, CALG_SHA1, 0, 0, &m_h); +} + + +////////////////////////////////////////////////////////////////////////// +// wxCryptoKey +////////////////////////////////////////////////////////////////////////// + +wxCryptoKey::wxCryptoKey() : m_h(NULL) +{ +} + + +wxCryptoKey::~wxCryptoKey() +{ + if (m_h) + ::CryptDestroyKey(m_h); +} + + +bool wxCryptoKey::ImportPrivate(wxCryptoSession &session, const void *data, size_t size) +{ + wxASSERT_MSG(!m_h, wxT("object initialized")); + wxASSERT_MSG(session.IsOk(), wxT("invalid session")); + wxASSERT_MSG(data || !size, wxT("invalid parameter")); + + PUBLICKEYSTRUC *key_data = NULL; + DWORD key_size = 0; + if (!::CryptDecodeObjectEx(X509_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, (const BYTE*)data, size, CRYPT_DECODE_ALLOC_FLAG, NULL, &key_data, &key_size)) + return false; + + // See: http://pumka.net/2009/12/16/rsa-encryption-cplusplus-delphi-cryptoapi-php-openssl-2/comment-page-1/#comment-429 + key_data->aiKeyAlg = CALG_RSA_SIGN; + + if (!::CryptImportKey(session, (const BYTE*)key_data, key_size, NULL, 0, &m_h)) { + ::LocalFree(key_data); + return false; + } + ::LocalFree(key_data); + + return true; +} + + +bool wxCryptoKey::ImportPublic(wxCryptoSession &session, const void *data, size_t size) +{ + wxASSERT_MSG(!m_h, wxT("object initialized")); + wxASSERT_MSG(session.IsOk(), wxT("invalid session")); + wxASSERT_MSG(data || !size, wxT("invalid parameter")); + + CERT_PUBLIC_KEY_INFO *keyinfo_data = NULL; + DWORD keyinfo_size = 0; + if (!::CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, (const BYTE*)data, size, CRYPT_DECODE_ALLOC_FLAG, NULL, &keyinfo_data, &keyinfo_size)) + return false; + + if (!::CryptImportPublicKeyInfo(session, X509_ASN_ENCODING, keyinfo_data, &m_h)) { + ::LocalFree(keyinfo_data); + return false; + } + ::LocalFree(keyinfo_data); + + return true; +} + + +////////////////////////////////////////////////////////////////////////// +// wxCryptoVerifySignature +////////////////////////////////////////////////////////////////////////// + +bool WXEXTEND_API wxCryptoVerifySignature(const wxCryptoHash &hash, const void *signature_data, size_t signature_size, const wxCryptoKey &key) +{ + wxASSERT_MSG(hash.IsOk() , wxT("invalid hash")); + wxASSERT_MSG(signature_data || !signature_size, wxT("invalid parameter")); + wxASSERT_MSG(key.IsOk() , wxT("invalid key")); + + // Reverse byte order, for consistent OpenSSL experience. + wxMemoryBuffer signature(signature_size); + BYTE *data = (BYTE*)signature.GetData(); + for (size_t i = 0, j = signature_size - 1; i < signature_size; i++, j--) + data[i] = ((const BYTE*)signature_data)[j]; + + return ::CryptVerifySignature(hash, data, signature_size, key, NULL, 0) ? true : false; +} diff --git a/src/stdafx.h b/src/stdafx.h index 29d9677..97c9249 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -25,6 +25,7 @@ #include "../include/wxex/appbar.h" #include "../include/wxex/comutils.h" +#include "../include/wxex/crypto.h" #include "../include/wxex/xml.h" #include "../include/wxex/common.h" diff --git a/src/xml.cpp b/src/xml.cpp index aed3d2b..26b039f 100644 --- a/src/xml.cpp +++ b/src/xml.cpp @@ -20,14 +20,7 @@ #include "stdafx.h" -static inline BOOL CryptHashData(__in HCRYPTHASH hHash, __in const wxString &str, __in DWORD dwFlags) -{ - const wxScopedCharBuffer buf(str.ToUTF8()); - return ::CryptHashData(hHash, (const BYTE*)buf.data(), buf.length(), dwFlags); -} - - -void WXEXTEND_API wxXmlHashNode(_In_ HCRYPTHASH hash, const wxXmlNode *node) +bool WXEXTEND_API wxXmlHashNode(_In_ wxCryptoHash &hash, const wxXmlNode *node) { wxASSERT_MSG(node, wxT("invalid parameter")); @@ -38,22 +31,22 @@ void WXEXTEND_API wxXmlHashNode(_In_ HCRYPTHASH hash, const wxXmlNode *node) static const BYTE element_in[] = "<"; // Hash the open tag. - wxVERIFY(::CryptHashData(hash, element_in, _countof(element_in) - 1, 0)); - wxVERIFY(::CryptHashData(hash, node->GetName(), 0)); + wxCHECK(hash.Hash(element_in, _countof(element_in) - 1), false); + wxCHECK(hash.HashAsUTF8(node->GetName()), false); for (wxXmlAttribute *attr = node->GetAttributes(); attr; attr = attr->GetNext()) { static const BYTE attrib_sep[] = " "; - wxVERIFY(::CryptHashData(hash, attrib_sep, _countof(attrib_sep) - 1, 0)); - wxVERIFY(::CryptHashData(hash, attr->GetName(), 0)); + wxCHECK(hash.Hash(attrib_sep, _countof(attrib_sep) - 1), false); + wxCHECK(hash.HashAsUTF8(attr->GetName()), false); wxString value = attr->GetValue(); if (!value.IsEmpty()) { static const BYTE attrval_in [] = "=\"", attrval_out[] = "\""; - wxVERIFY(::CryptHashData(hash, attrval_in, _countof(attrval_in) - 1, 0)); - wxVERIFY(::CryptHashData(hash, wxXmlEscapeAttr(value), 0)); - wxVERIFY(::CryptHashData(hash, attrval_out, _countof(attrval_out) - 1, 0)); + wxCHECK(hash.Hash(attrval_in, _countof(attrval_in) - 1), false); + wxCHECK(hash.HashAsUTF8(wxXmlEscapeAttr(value)), false); + wxCHECK(hash.Hash(attrval_out, _countof(attrval_out) - 1), false); } } } @@ -65,21 +58,21 @@ void WXEXTEND_API wxXmlHashNode(_In_ HCRYPTHASH hash, const wxXmlNode *node) elemclose_in[] = "GetNext()) - wxXmlHashNode(hash, child); + wxCHECK(wxXmlHashNode(hash, child), false); // Hash the closing tag. - wxVERIFY(::CryptHashData(hash, elemclose_in, _countof(elemclose_in) - 1, 0)); - wxVERIFY(::CryptHashData(hash, node->GetName(), 0)); - wxVERIFY(::CryptHashData(hash, element_out, _countof(element_out) - 1, 0)); + wxCHECK(hash.Hash(elemclose_in, _countof(elemclose_in) - 1), false); + wxCHECK(hash.HashAsUTF8(node->GetName()), false); + wxCHECK(hash.Hash(element_out, _countof(element_out) - 1), false); } else { static const BYTE element_out [] = "/>"; // Hash the childless element tag closing. - wxVERIFY(::CryptHashData(hash, element_out, _countof(element_out) - 1, 0)); + wxCHECK(hash.Hash(element_out, _countof(element_out) - 1), false); } break; @@ -87,7 +80,7 @@ void WXEXTEND_API wxXmlHashNode(_In_ HCRYPTHASH hash, const wxXmlNode *node) case wxXML_TEXT_NODE: { - wxVERIFY(::CryptHashData(hash, wxXmlEscapeText(node->GetContent()), 0)); + wxCHECK(hash.HashAsUTF8(wxXmlEscapeText(node->GetContent())), false); break; } @@ -97,16 +90,16 @@ void WXEXTEND_API wxXmlHashNode(_In_ HCRYPTHASH hash, const wxXmlNode *node) cdata_in [] = ""; - wxVERIFY(::CryptHashData(hash, cdata_in, _countof(cdata_in) - 1, 0)); - wxVERIFY(::CryptHashData(hash, node->GetContent(), 0)); - wxVERIFY(::CryptHashData(hash, cdata_out, _countof(cdata_out) - 1, 0)); + wxCHECK(hash.Hash(cdata_in, _countof(cdata_in) - 1), false); + wxCHECK(hash.HashAsUTF8(node->GetContent()), false); + wxCHECK(hash.Hash(cdata_out, _countof(cdata_out) - 1), false); break; } case wxXML_COMMENT_NODE: { - wxVERIFY(::CryptHashData(hash, node->GetContent(), 0)); + wxCHECK(hash.HashAsUTF8(node->GetContent()), false); break; } @@ -114,12 +107,15 @@ void WXEXTEND_API wxXmlHashNode(_In_ HCRYPTHASH hash, const wxXmlNode *node) { // Hash the children. for (wxXmlNode *child = node->GetChildren(); child; child = child->GetNext()) - wxXmlHashNode(hash, child); + wxCHECK(wxXmlHashNode(hash, child), false); break; } default: wxFAIL_MSG(wxT("unsupported XML node type")); + return false; } + + return true; }