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"
#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,97 +66,113 @@ int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_ HINSTANCE hPrevInstance, _In
{
wxApp::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE, "program");
// Inizialize wxWidgets.
wxInitializer initializer;
if (!initializer)
// Initialize wxWidgets.
wxUpdCheckInitializer initializer;
if (!initializer.IsOk())
return -1;
wxXmlDocument doc;
// Load repository database.
wxHTTP http;
//wxDateTime timestamp_last_checked;
//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)) {
wxFAIL_MSG(wxT("Error resolving server name."));
return 1;
// Create RSA AES cryptographic context.
wxCryptoSessionRSAAES cs;
if (!cs.IsOk()) {
wxLogError(wxT("Failed to create cryptographics session."));
return -1;
}
wxInputStream *httpStream = http.GetInputStream(wxS(UPDATER_HTTP_PATH));
if (!doc.Load(*httpStream, "UTF-8", wxXMLDOC_KEEP_WHITESPACE_NODES)) {
wxFAIL_MSG(wxT("Error reading data file."));
return 1;
}
wxDELETE(httpStream);
http.Close();
// Find the (first) signature.
wxXmlNode *document = doc.GetDocumentNode();
std::vector<BYTE> sig;
for (wxXmlNode *prolog = document->GetChildren(); prolog; prolog = prolog->GetNext()) {
if (prolog->GetType() == wxXML_COMMENT_NODE) {
wxString content = prolog->GetContent();
size_t content_len = content.length();
if (content_len >= _countof(wxS(UPDATER_SIGNATURE_MARK)) - 1 &&
memcmp((const wxStringCharType*)content, wxS(UPDATER_SIGNATURE_MARK), sizeof(wxStringCharType)*(_countof(wxS(UPDATER_SIGNATURE_MARK)) - 1)) == 0)
{
// Read the signature.
size_t signature_len = content_len - (_countof(wxS(UPDATER_SIGNATURE_MARK)) - 1);
sig.resize(wxBase64DecodedSize(signature_len));
size_t res = wxBase64Decode(sig.data(), sig.capacity(), content.Right(signature_len), wxBase64DecodeMode_SkipWS);
if (res != wxCONV_FAILED) {
sig.resize(res);
// Remove the signature as it is not a part of the validation check.
document->RemoveChild(prolog);
wxDELETE(prolog);
break;
} else
sig.clear();
}
}
}
if (sig.empty())
wxFAIL_MSG(wxT("Signature not found in the Updater file."));
// 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.
HCRYPTHASH ch = NULL;
wxVERIFY(::CryptCreateHash(cp, CALG_SHA1, 0, 0, &ch));
::wxXmlHashNode(ch, document);
// Import public key.
HCRYPTKEY ck = NULL;
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"));
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);
if (!ck.ImportPublic(cs, ::LockResource(res_handle), ::SizeofResource(NULL, res))) {
wxLogError(wxT("Failed to import public key."));
return -1;
}
}
// 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)) {
for (const wxChar *server = wxT(UPDATER_HTTP_SERVER); server[0]; server += wcslen(server) + 1) {
wxXmlDocument doc;
wxLogStatus(wxT("Contacting repository at %s..."), server);
// Load repository database.
wxHTTP http;
//wxDateTime timestamp_last_checked;
//http.SetHeader(wxS("If-Modified-Since"), timestamp_last_checked.Format(wxS("%a, %d %b %y %T %z")));
if (!http.Connect(server, UPDATER_HTTP_PORT)) {
wxLogWarning(wxT("Error resolving %s server name."), server);
continue;
}
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)) {
wxLogWarning(wxT("Error loading repository catalogue."));
wxDELETE(httpStream);
http.Close();
continue;
}
wxDELETE(httpStream);
http.Close();
wxLogStatus(wxT("Verifying repository catalogue signature..."));
// Find the (first) signature.
wxXmlNode *document = doc.GetDocumentNode();
std::vector<BYTE> sig;
for (wxXmlNode *prolog = document->GetChildren(); prolog; prolog = prolog->GetNext()) {
if (prolog->GetType() == wxXML_COMMENT_NODE) {
wxString content = prolog->GetContent();
size_t content_len = content.length();
if (content_len >= _countof(wxS(UPDATER_SIGNATURE_MARK)) - 1 &&
memcmp((const wxStringCharType*)content, wxS(UPDATER_SIGNATURE_MARK), sizeof(wxStringCharType)*(_countof(wxS(UPDATER_SIGNATURE_MARK)) - 1)) == 0)
{
// Read the signature.
size_t signature_len = content_len - (_countof(wxS(UPDATER_SIGNATURE_MARK)) - 1);
sig.resize(wxBase64DecodedSize(signature_len));
size_t res = wxBase64Decode(sig.data(), sig.capacity(), content.Right(signature_len), wxBase64DecodeMode_SkipWS);
if (res != wxCONV_FAILED) {
sig.resize(res);
// Remove the signature as it is not a part of the validation check.
document->RemoveChild(prolog);
wxDELETE(prolog);
break;
} else
sig.clear();
}
}
}
if (sig.empty()) {
wxLogWarning(wxT("Signature not found in the repository catalogue."));
continue;
}
// Hash the content.
wxCryptoHashSHA1 ch(cs);
if (!wxXmlHashNode(ch, document)) {
wxLogWarning(wxT("Failed to hash the repository catalogue."));
continue;
}
// We have the hash, we have the signature, we have the public key. Now verify.
if (!wxCryptoVerifySignature(ch, sig.data(), sig.size(), ck)) {
wxLogWarning(wxT("Repository catalogue signature does not match its content, or signature verification failed."));
continue;
}
// The signature is correct. Now parse the file.
} else
wxFAIL_MSG(wxT("Updater file signature does not match file content."));
wxLogStatus(wxT("Parsing repository catalogue..."));
wxVERIFY(::CryptDestroyKey(ck));
wxVERIFY(::CryptDestroyHash(ch));
wxVERIFY(::CryptReleaseContext(cp, 0));
return 0;
}
return 0;
// No success.
return 1;
}

View File

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

View File

@ -18,7 +18,6 @@
*/
#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");
// Inizialize wxWidgets.
// Initialize wxWidgets.
wxInitializer initializer;
if (!initializer) {
if (!initializer.IsOk()) {
_ftprintf(stderr, wxT("Failed to initialize the wxWidgets library, aborting.\n"));
return -1;
}
@ -55,22 +54,20 @@ int _tmain(int argc, _TCHAR *argv[])
break;
default:
wxLogMessage(wxT("Syntax error detected, aborting."));
wxLogWarning(wxT("Syntax error detected, aborting."));
return -1;
}
// Load input XML document.
const wxString& filenameIn = parser.GetParam(0);
wxXmlDocument doc;
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;
}
bool has_errors = false;
// Examine prologue if the document is already signed and remove all signatures found.
wxXmlNode *document = doc.GetDocumentNode();
// Examine prologue if the document is already signed and remove all signatures.
for (wxXmlNode *prolog = document->GetChildren(); prolog;) {
if (prolog->GetType() == wxXML_COMMENT_NODE) {
wxString content = prolog->GetContent();
@ -89,67 +86,57 @@ int _tmain(int argc, _TCHAR *argv[])
prolog = prolog->GetNext();
}
// Create cryptographic context.
HCRYPTPROV cp = NULL;
wxVERIFY(::CryptAcquireContext(&cp, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT));
// Create RSA AES cryptographic session.
wxCryptoSessionRSAAES cs;
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);
wxASSERT_MSG(res, wxT("private key not found"));
HGLOBAL res_handle = ::LoadResource(NULL, res);
wxASSERT_MSG(res_handle, wxT("loading resource failed"));
PUBLICKEYSTRUC *key_data = NULL;
DWORD key_size = 0;
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));
// 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;
HCRYPTKEY ck = NULL;
wxVERIFY(::CryptImportKey(cp, (const BYTE*)key_data, key_size, NULL, 0, &ck));
wxVERIFY(::CryptDestroyKey(ck));
::LocalFree(key_data);
wxCryptoKey ck;
if (!ck.ImportPrivate(cs, ::LockResource(res_handle), ::SizeofResource(NULL, res))) {
_ftprintf(stderr, wxT("Failed to import private key.\n"));
return -1;
}
}
// Hash the content.
HCRYPTHASH ch = NULL;
wxVERIFY(::CryptCreateHash(cp, CALG_SHA1, 0, 0, &ch));
::wxXmlHashNode(ch, document);
// Hash the XML content.
wxCryptoHashSHA1 ch(cs);
if (!wxXmlHashNode(ch, document)) {
_ftprintf(stderr, wxT("%s: error USX0002: Error hashing input file.\n"), filenameIn.fn_str());
return 2;
}
// Sign the hash.
DWORD sig_size = 0;
if (!::CryptSignHash(ch, AT_SIGNATURE, NULL, 0, NULL, &sig_size)) {
_ftprintf(stderr, wxT("%s: error UMD0003: Signing input file failed (%i).\n"), filenameIn.fn_str(), ::GetLastError());
has_errors = true;
wxMemoryBuffer sig;
if (!ch.Sign(sig)) {
_ftprintf(stderr, wxT("%s: error USX0003: Error signing hash.\n"), filenameIn.fn_str());
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.
wxString signature;
signature += wxS(UPDATER_SIGNATURE_MARK);
signature += wxBase64Encode(sig.data(), sig.size());
signature += wxBase64Encode(sig);
document->AddChild(new wxXmlNode(wxXML_COMMENT_NODE, wxS(""), signature));
// Write output XML document.
const wxString& filenameOut = parser.GetParam(1);
if (!doc.Save(filenameOut, wxXML_NO_INDENTATION)) {
_ftprintf(stderr, wxT("%s: error UMD0001: Error writing output file.\n"), filenameOut.fn_str());
has_errors = true;
_ftprintf(stderr, wxT("%s: error USX0004: Error writing output file.\n"), filenameOut.fn_str());
// 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 <wxex/common.h>
#include <wxex/crypto.h>
#include <wxex/xml.h>
#include <algorithm>