CryptoAPI functions replaced with wxExtend classes, logging improved

This commit is contained in:
Simon Rozman 2016-03-18 12:19:28 +01:00
parent ca8d110de5
commit bd1652bd5f
4 changed files with 172 additions and 128 deletions

View File

@ -18,7 +18,45 @@
*/ */
#include "stdafx.h" #include "stdafx.h"
#pragma comment(lib, "Crypt32.lib")
///
/// Module initializer
///
class wxUpdCheckInitializer : public wxInitializer
{
protected:
FILE *m_pLogFile;
public:
wxUpdCheckInitializer();
virtual ~wxUpdCheckInitializer();
};
wxUpdCheckInitializer::wxUpdCheckInitializer() :
m_pLogFile(NULL),
wxInitializer()
{
if (wxInitializer::IsOk()) {
wxString str(wxFileName::GetTempDir());
str += wxFILE_SEP_PATH;
str += wxT(UPDATER_LOG_FILE);
m_pLogFile = fopen(str, "w+");
if (m_pLogFile)
delete wxLog::SetActiveTarget(new wxLogStderr(m_pLogFile));
} else
m_pLogFile = NULL;
}
wxUpdCheckInitializer::~wxUpdCheckInitializer()
{
if (m_pLogFile != NULL) {
delete wxLog::SetActiveTarget(NULL);
fclose(m_pLogFile);
}
}
/// ///
@ -28,29 +66,62 @@ int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_ HINSTANCE hPrevInstance, _In
{ {
wxApp::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE, "program"); wxApp::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE, "program");
// Inizialize wxWidgets. // Initialize wxWidgets.
wxInitializer initializer; wxUpdCheckInitializer initializer;
if (!initializer) if (!initializer.IsOk())
return -1; return -1;
// Create RSA AES cryptographic context.
wxCryptoSessionRSAAES cs;
if (!cs.IsOk()) {
wxLogError(wxT("Failed to create cryptographics session."));
return -1;
}
// Import public key.
wxCryptoKey ck;
{
HRSRC res = ::FindResource(NULL, MAKEINTRESOURCE(IDR_KEY_PUBLIC), RT_RCDATA);
wxASSERT_MSG(res, wxT("public key not found"));
HGLOBAL res_handle = ::LoadResource(NULL, res);
wxASSERT_MSG(res_handle, wxT("loading resource failed"));
if (!ck.ImportPublic(cs, ::LockResource(res_handle), ::SizeofResource(NULL, res))) {
wxLogError(wxT("Failed to import public key."));
return -1;
}
}
for (const wxChar *server = wxT(UPDATER_HTTP_SERVER); server[0]; server += wcslen(server) + 1) {
wxXmlDocument doc; wxXmlDocument doc;
wxLogStatus(wxT("Contacting repository at %s..."), server);
// Load repository database. // Load repository database.
wxHTTP http; wxHTTP http;
//wxDateTime timestamp_last_checked; //wxDateTime timestamp_last_checked;
//http.SetHeader(wxS("If-Modified-Since"), timestamp_last_checked.Format(wxS("%a, %d %b %y %T %z"))); //http.SetHeader(wxS("If-Modified-Since"), timestamp_last_checked.Format(wxS("%a, %d %b %y %T %z")));
if (!http.Connect(wxS(UPDATER_HTTP_SERVER), UPDATER_HTTP_PORT)) { if (!http.Connect(server, UPDATER_HTTP_PORT)) {
wxFAIL_MSG(wxT("Error resolving server name.")); wxLogWarning(wxT("Error resolving %s server name."), server);
return 1; continue;
} }
wxInputStream *httpStream = http.GetInputStream(wxS(UPDATER_HTTP_PATH)); wxInputStream *httpStream = http.GetInputStream(wxS(UPDATER_HTTP_PATH));
if (!httpStream) {
wxLogWarning(wxT("Error response received from server %s (port %u) requesting %s."), server, UPDATER_HTTP_PORT, UPDATER_HTTP_PATH);
continue;
}
wxLogStatus(wxT("Loading repository catalogue..."));
if (!doc.Load(*httpStream, "UTF-8", wxXMLDOC_KEEP_WHITESPACE_NODES)) { if (!doc.Load(*httpStream, "UTF-8", wxXMLDOC_KEEP_WHITESPACE_NODES)) {
wxFAIL_MSG(wxT("Error reading data file.")); wxLogWarning(wxT("Error loading repository catalogue."));
return 1; wxDELETE(httpStream);
http.Close();
continue;
} }
wxDELETE(httpStream); wxDELETE(httpStream);
http.Close(); http.Close();
wxLogStatus(wxT("Verifying repository catalogue signature..."));
// Find the (first) signature. // Find the (first) signature.
wxXmlNode *document = doc.GetDocumentNode(); wxXmlNode *document = doc.GetDocumentNode();
std::vector<BYTE> sig; std::vector<BYTE> sig;
@ -78,47 +149,30 @@ int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_ HINSTANCE hPrevInstance, _In
} }
} }
if (sig.empty()) if (sig.empty()) {
wxFAIL_MSG(wxT("Signature not found in the Updater file.")); wxLogWarning(wxT("Signature not found in the repository catalogue."));
continue;
// Reverse byte order, for consistent OpenSSL experience. }
for (std::vector<BYTE>::size_type i = 0, j = sig.size() - 1; i < j; i++, j--)
std::swap(sig[i], sig[j]);
// Create cryptographic context.
HCRYPTPROV cp = NULL;
wxVERIFY(::CryptAcquireContext(&cp, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT));
// Hash the content. // Hash the content.
HCRYPTHASH ch = NULL; wxCryptoHashSHA1 ch(cs);
wxVERIFY(::CryptCreateHash(cp, CALG_SHA1, 0, 0, &ch)); if (!wxXmlHashNode(ch, document)) {
::wxXmlHashNode(ch, document); wxLogWarning(wxT("Failed to hash the repository catalogue."));
continue;
// Import public key.
HCRYPTKEY ck = NULL;
{
HRSRC res = ::FindResource(NULL, MAKEINTRESOURCE(IDR_KEY_PUBLIC), RT_RCDATA);
wxASSERT_MSG(res, wxT("public key not found"));
HGLOBAL res_handle = ::LoadResource(NULL, res);
wxASSERT_MSG(res_handle, wxT("loading resource failed"));
CERT_PUBLIC_KEY_INFO *keyinfo_data = NULL;
DWORD keyinfo_size = 0;
wxVERIFY(::CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, (const BYTE*)::LockResource(res_handle), ::SizeofResource(NULL, res), CRYPT_DECODE_ALLOC_FLAG, NULL, &keyinfo_data, &keyinfo_size));
wxVERIFY(::CryptImportPublicKeyInfo(cp, X509_ASN_ENCODING, keyinfo_data, &ck));
::LocalFree(keyinfo_data);
} }
// We have the hash, we have the signature, we have the public key. Now verify. // We have the hash, we have the signature, we have the public key. Now verify.
if (::CryptVerifySignature(ch, sig.data(), sig.size(), ck, NULL, 0)) { if (!wxCryptoVerifySignature(ch, sig.data(), sig.size(), ck)) {
// The signature is correct. Now parse the file. wxLogWarning(wxT("Repository catalogue signature does not match its content, or signature verification failed."));
} else continue;
wxFAIL_MSG(wxT("Updater file signature does not match file content.")); }
wxVERIFY(::CryptDestroyKey(ck)); // The signature is correct. Now parse the file.
wxVERIFY(::CryptDestroyHash(ch)); wxLogStatus(wxT("Parsing repository catalogue..."));
wxVERIFY(::CryptReleaseContext(cp, 0));
return 0; return 0;
}
// No success.
return 1;
} }

View File

@ -27,10 +27,12 @@
#include <wx/app.h> #include <wx/app.h>
#include <wx/base64.h> #include <wx/base64.h>
#include <wx/datetime.h> #include <wx/datetime.h>
#include <wx/filename.h>
#include <wx/protocol/http.h> #include <wx/protocol/http.h>
#include <wx/xml/xml.h> #include <wx/xml/xml.h>
#include <wxex/common.h> #include <wxex/common.h>
#include <wxex/crypto.h>
#include <wxex/xml.h> #include <wxex/xml.h>
#include <algorithm> #include <algorithm>

View File

@ -18,7 +18,6 @@
*/ */
#include "stdafx.h" #include "stdafx.h"
#pragma comment(lib, "Crypt32.lib")
/// ///
@ -28,9 +27,9 @@ int _tmain(int argc, _TCHAR *argv[])
{ {
wxApp::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE, "program"); wxApp::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE, "program");
// Inizialize wxWidgets. // Initialize wxWidgets.
wxInitializer initializer; wxInitializer initializer;
if (!initializer) { if (!initializer.IsOk()) {
_ftprintf(stderr, wxT("Failed to initialize the wxWidgets library, aborting.\n")); _ftprintf(stderr, wxT("Failed to initialize the wxWidgets library, aborting.\n"));
return -1; return -1;
} }
@ -55,22 +54,20 @@ int _tmain(int argc, _TCHAR *argv[])
break; break;
default: default:
wxLogMessage(wxT("Syntax error detected, aborting.")); wxLogWarning(wxT("Syntax error detected, aborting."));
return -1; return -1;
} }
// Load input XML document.
const wxString& filenameIn = parser.GetParam(0); const wxString& filenameIn = parser.GetParam(0);
wxXmlDocument doc; wxXmlDocument doc;
if (!doc.Load(filenameIn, "UTF-8", wxXMLDOC_KEEP_WHITESPACE_NODES)) { if (!doc.Load(filenameIn, "UTF-8", wxXMLDOC_KEEP_WHITESPACE_NODES)) {
_ftprintf(stderr, wxT("%s: error UMD0001: Error opening input file.\n"), filenameIn.fn_str()); _ftprintf(stderr, wxT("%s: error USX0001: Error opening input file.\n"), filenameIn.fn_str());
return 1; return 1;
} }
bool has_errors = false; // Examine prologue if the document is already signed and remove all signatures found.
wxXmlNode *document = doc.GetDocumentNode(); wxXmlNode *document = doc.GetDocumentNode();
// Examine prologue if the document is already signed and remove all signatures.
for (wxXmlNode *prolog = document->GetChildren(); prolog;) { for (wxXmlNode *prolog = document->GetChildren(); prolog;) {
if (prolog->GetType() == wxXML_COMMENT_NODE) { if (prolog->GetType() == wxXML_COMMENT_NODE) {
wxString content = prolog->GetContent(); wxString content = prolog->GetContent();
@ -89,67 +86,57 @@ int _tmain(int argc, _TCHAR *argv[])
prolog = prolog->GetNext(); prolog = prolog->GetNext();
} }
// Create cryptographic context. // Create RSA AES cryptographic session.
HCRYPTPROV cp = NULL; wxCryptoSessionRSAAES cs;
wxVERIFY(::CryptAcquireContext(&cp, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)); if (!cs.IsOk()) {
_ftprintf(stderr, wxT("Failed to create cryptographics session.\n"));
return -1;
}
// Import private key. // Import the private key into the session from resources.
{ {
HRSRC res = ::FindResource(NULL, MAKEINTRESOURCE(IDR_KEY_PRIVATE), RT_RCDATA); HRSRC res = ::FindResource(NULL, MAKEINTRESOURCE(IDR_KEY_PRIVATE), RT_RCDATA);
wxASSERT_MSG(res, wxT("private key not found")); wxASSERT_MSG(res, wxT("private key not found"));
HGLOBAL res_handle = ::LoadResource(NULL, res); HGLOBAL res_handle = ::LoadResource(NULL, res);
wxASSERT_MSG(res_handle, wxT("loading resource failed")); wxASSERT_MSG(res_handle, wxT("loading resource failed"));
PUBLICKEYSTRUC *key_data = NULL; wxCryptoKey ck;
DWORD key_size = 0; if (!ck.ImportPrivate(cs, ::LockResource(res_handle), ::SizeofResource(NULL, res))) {
wxVERIFY(::CryptDecodeObjectEx(X509_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, (const BYTE*)::LockResource(res_handle), ::SizeofResource(NULL, res), CRYPT_DECODE_ALLOC_FLAG, NULL, &key_data, &key_size)); _ftprintf(stderr, wxT("Failed to import private key.\n"));
// See: http://pumka.net/2009/12/16/rsa-encryption-cplusplus-delphi-cryptoapi-php-openssl-2/comment-page-1/#comment-429 return -1;
key_data->aiKeyAlg = CALG_RSA_SIGN; }
HCRYPTKEY ck = NULL;
wxVERIFY(::CryptImportKey(cp, (const BYTE*)key_data, key_size, NULL, 0, &ck));
wxVERIFY(::CryptDestroyKey(ck));
::LocalFree(key_data);
} }
// Hash the content. // Hash the XML content.
HCRYPTHASH ch = NULL; wxCryptoHashSHA1 ch(cs);
wxVERIFY(::CryptCreateHash(cp, CALG_SHA1, 0, 0, &ch)); if (!wxXmlHashNode(ch, document)) {
::wxXmlHashNode(ch, document); _ftprintf(stderr, wxT("%s: error USX0002: Error hashing input file.\n"), filenameIn.fn_str());
return 2;
}
// Sign the hash. // Sign the hash.
DWORD sig_size = 0; wxMemoryBuffer sig;
if (!::CryptSignHash(ch, AT_SIGNATURE, NULL, 0, NULL, &sig_size)) { if (!ch.Sign(sig)) {
_ftprintf(stderr, wxT("%s: error UMD0003: Signing input file failed (%i).\n"), filenameIn.fn_str(), ::GetLastError()); _ftprintf(stderr, wxT("%s: error USX0003: Error signing hash.\n"), filenameIn.fn_str());
has_errors = true; return 3;
} }
std::vector<BYTE> sig(sig_size);
if (!::CryptSignHash(ch, AT_SIGNATURE, NULL, 0, sig.data(), &sig_size)) {
_ftprintf(stderr, wxT("%s: error UMD0003: Signing input file failed (%i).\n"), filenameIn.fn_str(), ::GetLastError());
has_errors = true;
}
wxVERIFY(::CryptDestroyHash(ch));
wxVERIFY(::CryptReleaseContext(cp, 0));
// Reverse byte order, for consistent OpenSSL experience.
for (std::vector<BYTE>::size_type i = 0, j = sig.size() - 1; i < j; i++, j--)
std::swap(sig[i], sig[j]);
// Encode signature (Base64) and append to the document prolog. // Encode signature (Base64) and append to the document prolog.
wxString signature; wxString signature;
signature += wxS(UPDATER_SIGNATURE_MARK); signature += wxS(UPDATER_SIGNATURE_MARK);
signature += wxBase64Encode(sig.data(), sig.size()); signature += wxBase64Encode(sig);
document->AddChild(new wxXmlNode(wxXML_COMMENT_NODE, wxS(""), signature)); document->AddChild(new wxXmlNode(wxXML_COMMENT_NODE, wxS(""), signature));
// Write output XML document.
const wxString& filenameOut = parser.GetParam(1); const wxString& filenameOut = parser.GetParam(1);
if (!doc.Save(filenameOut, wxXML_NO_INDENTATION)) { if (!doc.Save(filenameOut, wxXML_NO_INDENTATION)) {
_ftprintf(stderr, wxT("%s: error UMD0001: Error writing output file.\n"), filenameOut.fn_str()); _ftprintf(stderr, wxT("%s: error USX0004: Error writing output file.\n"), filenameOut.fn_str());
has_errors = true;
// Remove the output file (if exists).
wxRemoveFile(filenameOut);
return 4;
} }
if (has_errors) {
wxRemoveFile(filenameOut);
return 1;
} else
return 0; return 0;
} }

View File

@ -28,6 +28,7 @@
#include <wx/xml/xml.h> #include <wx/xml/xml.h>
#include <wxex/common.h> #include <wxex/common.h>
#include <wxex/crypto.h>
#include <wxex/xml.h> #include <wxex/xml.h>
#include <algorithm> #include <algorithm>