Binary repository database dropped and XML variation introduced
This commit is contained in:
parent
79fd6c5348
commit
eeab3a1ec1
@ -21,72 +21,6 @@
|
||||
#pragma comment(lib, "Crypt32.lib")
|
||||
|
||||
|
||||
///
|
||||
/// Reads package information from a stream
|
||||
///
|
||||
/// \param[in] stream Input stream
|
||||
/// \param[out] pkg Package information
|
||||
///
|
||||
/// \returns The stream \p stream
|
||||
///
|
||||
inline std::istream& operator >>(_In_ std::istream& stream, _Out_ Updater::package_info &pkg)
|
||||
{
|
||||
// Read binary version.
|
||||
stream.read((char*)&pkg.ver, sizeof(pkg.ver));
|
||||
if (!stream.good()) return stream;
|
||||
|
||||
// Read human readable description (length prefixed).
|
||||
unsigned __int32 count;
|
||||
stream.read((char*)&count, sizeof(count));
|
||||
if (!stream.good()) return stream;
|
||||
pkg.desc.resize(count);
|
||||
stream.read((char*)pkg.desc.data(), sizeof(wchar_t)*count);
|
||||
|
||||
// Read package language (length prefixed).
|
||||
stream.read((char*)&count, sizeof(count));
|
||||
if (!stream.good()) return stream;
|
||||
pkg.lang.resize(count);
|
||||
stream.read((char*)pkg.lang.data(), sizeof(char)*count);
|
||||
|
||||
// Read package download URL (length prefixed).
|
||||
stream.read((char*)&count, sizeof(count));
|
||||
if (!stream.good()) return stream;
|
||||
pkg.url.resize(count);
|
||||
stream.read((char*)pkg.url.data(), sizeof(char)*count);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// Reads signature from a stream
|
||||
///
|
||||
/// \param[in] stream Input stream
|
||||
/// \param[out] sig Signature
|
||||
///
|
||||
/// \returns The stream \p stream
|
||||
///
|
||||
inline std::istream& operator >>(_In_ std::istream& stream, _Out_ Updater::signature &sig)
|
||||
{
|
||||
// Read signature (length prefixed).
|
||||
unsigned __int32 count;
|
||||
stream.read((char*)&count, sizeof(count));
|
||||
if (!stream.good()) return stream;
|
||||
|
||||
// Read, and reverse signature byte order (to be OpenSSL compatible).
|
||||
std::vector<unsigned char> sig_swap(count);
|
||||
stream.read((char*)sig_swap.data(), sizeof(unsigned char)*count);
|
||||
if (!stream.good()) return stream;
|
||||
sig.resize(count);
|
||||
for (unsigned __int32 i = 0, j = count - 1; i < count; i++, j--)
|
||||
sig[i] = sig_swap[j];
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
///
|
||||
/// Main function
|
||||
///
|
||||
@ -99,96 +33,91 @@ int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_ HINSTANCE hPrevInstance, _In
|
||||
if (!initializer)
|
||||
return -1;
|
||||
|
||||
// Open data file.
|
||||
std::ifstream dat(_T("..\\..\\output\\test.bin"), std::ios_base::in | std::ios_base::binary);
|
||||
if (!dat.good()) {
|
||||
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;
|
||||
}
|
||||
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 signature first.
|
||||
if (stdex::idrec::find<Updater::recordid_t, Updater::recordsize_t, UPDATER_RECORD_ALIGN>(dat, Updater::signature_rec::id)) {
|
||||
// Signature found. Remember file position.
|
||||
Updater::recordsize_t
|
||||
sig_pos = (Updater::recordsize_t)dat.tellg(), // Position to return to to read the signature.
|
||||
sig_start = sig_pos - sizeof(Updater::recordid_t); // Beginning of the signature block
|
||||
wxXmlNode *document = doc.GetDocumentNode();
|
||||
std::vector<BYTE> sig;
|
||||
|
||||
// Create cryptographic context.
|
||||
HCRYPTPROV cp = NULL;
|
||||
wxVERIFY(::CryptAcquireContext(&cp, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT));
|
||||
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)
|
||||
{
|
||||
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);
|
||||
else
|
||||
sig.clear();
|
||||
|
||||
// Hash the file up to signature block start.
|
||||
HCRYPTHASH ch = NULL;
|
||||
wxVERIFY(::CryptCreateHash(cp, CALG_SHA1, 0, 0, &ch));
|
||||
{
|
||||
dat.seekg(0);
|
||||
std::vector<BYTE> buf(8192);
|
||||
for (Updater::recordsize_t data_left = sig_start; dat.good() && data_left;) {
|
||||
dat.read((char*)buf.data(), std::min<Updater::recordsize_t>(buf.size(), data_left));
|
||||
Updater::recordsize_t count = dat.gcount();
|
||||
wxVERIFY(::CryptHashData(ch, buf.data(), count, 0));
|
||||
data_left -= count;
|
||||
// Remove signature for check.
|
||||
document->RemoveChild(prolog);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read the signature.
|
||||
dat.seekg(sig_pos);
|
||||
Updater::signature sig;
|
||||
dat >> Updater::signature_rec(sig);
|
||||
if (dat.good()) {
|
||||
// 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.
|
||||
if (::CryptVerifySignature(ch, (BYTE*)sig.data(), sig.size(), ck, NULL, 0)) {
|
||||
// The signature is correct. Now parse the file.
|
||||
dat.seekg(0);
|
||||
if (stdex::idrec::find<Updater::recordid_t, Updater::recordsize_t, UPDATER_RECORD_ALIGN>(dat, UPDATER_DB_ID, sizeof(Updater::recordid_t))) {
|
||||
Updater::recordsize_t size;
|
||||
dat.read((char*)&size, sizeof(Updater::recordsize_t));
|
||||
if (dat.good()) {
|
||||
if (size > sig_start) {
|
||||
// Limit the size up to the file signature. Should have not get here.
|
||||
wxFAIL_MSG(wxT("Updater file wrong record size."));
|
||||
size = sig_start;
|
||||
}
|
||||
if (stdex::idrec::find<Updater::recordid_t, Updater::recordsize_t, UPDATER_RECORD_ALIGN>(dat, Updater::package_info_rec::id, size)) {
|
||||
// Read package information.
|
||||
Updater::package_info pi;
|
||||
dat >> Updater::package_info_rec(pi);
|
||||
if (dat.good()) {
|
||||
|
||||
} else
|
||||
wxFAIL_MSG(wxT("Error reading package information from Updater file."));
|
||||
} else
|
||||
wxFAIL_MSG(wxT("Updater file has no package information."));
|
||||
} else
|
||||
wxFAIL_MSG(wxT("Updater file read error."));
|
||||
}
|
||||
} else
|
||||
wxFAIL_MSG(wxT("Updater file signature does not match file content."));
|
||||
|
||||
wxVERIFY(::CryptDestroyKey(ck));
|
||||
} else
|
||||
wxFAIL_MSG(wxT("Error reading signature from the Updater file."));
|
||||
|
||||
wxVERIFY(::CryptDestroyHash(ch));
|
||||
wxVERIFY(::CryptReleaseContext(cp, 0));
|
||||
} else
|
||||
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;
|
||||
{
|
||||
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.
|
||||
if (::CryptVerifySignature(ch, sig.data(), sig.size(), ck, NULL, 0)) {
|
||||
// The signature is correct. Now parse the file.
|
||||
} else
|
||||
wxFAIL_MSG(wxT("Updater file signature does not match file content."));
|
||||
|
||||
wxVERIFY(::CryptDestroyKey(ck));
|
||||
wxVERIFY(::CryptDestroyHash(ch));
|
||||
wxVERIFY(::CryptReleaseContext(cp, 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -19,13 +19,19 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../include/UpdaterCfg.h"
|
||||
|
||||
#include "UpdCheck.h"
|
||||
#include "../include/Updater.h"
|
||||
|
||||
#include <wx/app.h>
|
||||
#include <wx/base64.h>
|
||||
#include <wx/datetime.h>
|
||||
#include <wx/protocol/http.h>
|
||||
#include <wx/xml/xml.h>
|
||||
|
||||
#include <wxex/common.h>
|
||||
#include <wxex/xml.h>
|
||||
|
||||
#include <stdex/idrec.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
@ -1,254 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 Amebis
|
||||
|
||||
This file is part of Updater.
|
||||
|
||||
Updater 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.
|
||||
|
||||
Updater 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 Updater. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#pragma comment(lib, "Crypt32.lib")
|
||||
|
||||
|
||||
///
|
||||
/// Writes package information to a stream
|
||||
///
|
||||
/// \param[in] stream Output stream
|
||||
/// \param[in] pkg Package information
|
||||
///
|
||||
/// \returns The stream \p stream
|
||||
///
|
||||
inline std::ostream& operator <<(_In_ std::ostream& stream, _In_ const Updater::package_info &pkg)
|
||||
{
|
||||
// Write binary version.
|
||||
if (stream.fail()) return stream;
|
||||
stream.write((const char*)&pkg.ver, sizeof(pkg.ver));
|
||||
|
||||
// Write human readable description (length prefixed).
|
||||
std::wstring::size_type char_count = pkg.desc.length();
|
||||
#if defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__)
|
||||
// 4G check
|
||||
if (char_count > 0xffffffff) {
|
||||
stream.setstate(std::ios_base::failbit);
|
||||
return stream;
|
||||
}
|
||||
#endif
|
||||
if (stream.fail()) return stream;
|
||||
unsigned __int32 count = (unsigned __int32)char_count;
|
||||
stream.write((const char*)&count, sizeof(count));
|
||||
if (stream.fail()) return stream;
|
||||
stream.write((const char*)pkg.desc.c_str(), sizeof(wchar_t)*count);
|
||||
|
||||
// Write package language (length prefixed)
|
||||
char_count = pkg.lang.length();
|
||||
#if defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__)
|
||||
// 4G check
|
||||
if (char_count > 0xffffffff) {
|
||||
stream.setstate(std::ios_base::failbit);
|
||||
return stream;
|
||||
}
|
||||
#endif
|
||||
if (stream.fail()) return stream;
|
||||
count = (unsigned __int32)char_count;
|
||||
stream.write((const char*)&count, sizeof(count));
|
||||
if (stream.fail()) return stream;
|
||||
stream.write(pkg.lang.c_str(), sizeof(char)*count);
|
||||
|
||||
// Write package download URL (length prefixed)
|
||||
char_count = pkg.url.length();
|
||||
#if defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__)
|
||||
// 4G check
|
||||
if (char_count > 0xffffffff) {
|
||||
stream.setstate(std::ios_base::failbit);
|
||||
return stream;
|
||||
}
|
||||
#endif
|
||||
if (stream.fail()) return stream;
|
||||
count = (unsigned __int32)char_count;
|
||||
stream.write((const char*)&count, sizeof(count));
|
||||
if (stream.fail()) return stream;
|
||||
stream.write(pkg.url.c_str(), sizeof(char)*count);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// Writes signature to a stream
|
||||
///
|
||||
/// \param[in] stream Output stream
|
||||
/// \param[in] sig Signature
|
||||
///
|
||||
/// \returns The stream \p stream
|
||||
///
|
||||
inline std::ostream& operator <<(_In_ std::ostream& stream, _In_ const Updater::signature &sig)
|
||||
{
|
||||
// Write signature (length prefixed).
|
||||
Updater::signature::size_type sig_count = sig.size();
|
||||
#if defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__)
|
||||
// 4G check
|
||||
if (sig_count > 0xffffffff) {
|
||||
stream.setstate(std::ios_base::failbit);
|
||||
return stream;
|
||||
}
|
||||
#endif
|
||||
if (stream.fail()) return stream;
|
||||
unsigned __int32 count = (unsigned __int32)sig_count;
|
||||
stream.write((const char*)&count, sizeof(count));
|
||||
|
||||
// Reverse signature byte order (to make OpenSSL compatible), and write.
|
||||
if (stream.fail()) return stream;
|
||||
std::vector<unsigned char> sig_swap(count);
|
||||
for (unsigned __int32 i = 0, j = count - 1; i < count; i++, j--)
|
||||
sig_swap[j] = sig[i];
|
||||
stream.write((const char*)sig_swap.data(), sizeof(unsigned char)*count);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// Main function
|
||||
///
|
||||
int _tmain(int argc, _TCHAR *argv[])
|
||||
{
|
||||
wxApp::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE, "program");
|
||||
|
||||
// Inizialize wxWidgets.
|
||||
wxInitializer initializer;
|
||||
if (!initializer) {
|
||||
_ftprintf(stderr, wxT("Failed to initialize the wxWidgets library, aborting.\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Parse command line.
|
||||
static const wxCmdLineEntryDesc cmdLineDesc[] =
|
||||
{
|
||||
{ wxCMD_LINE_SWITCH, "h" , "help" , _("Show this help message" ), wxCMD_LINE_VAL_NONE , wxCMD_LINE_OPTION_HELP },
|
||||
{ wxCMD_LINE_OPTION, "x" , "ver-hex", _("Hexadecimal version of the product"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_OPTION_MANDATORY },
|
||||
{ wxCMD_LINE_OPTION, "s" , "ver-str", _("String version of the product" ), wxCMD_LINE_VAL_STRING, wxCMD_LINE_OPTION_MANDATORY },
|
||||
{ wxCMD_LINE_OPTION, "l" , "lang" , _("Package language" ), wxCMD_LINE_VAL_STRING, wxCMD_LINE_OPTION_MANDATORY },
|
||||
{ wxCMD_LINE_OPTION, "u" , "url" , _("Package download URL" ), wxCMD_LINE_VAL_STRING, wxCMD_LINE_OPTION_MANDATORY },
|
||||
{ wxCMD_LINE_PARAM , NULL, NULL , _("output file" ), wxCMD_LINE_VAL_STRING, wxCMD_LINE_OPTION_MANDATORY },
|
||||
|
||||
{ wxCMD_LINE_NONE }
|
||||
};
|
||||
wxCmdLineParser parser(cmdLineDesc, argc, argv);
|
||||
switch (parser.Parse()) {
|
||||
case -1:
|
||||
// Help was given, terminating.
|
||||
return 0;
|
||||
|
||||
case 0:
|
||||
// everything is ok; proceed
|
||||
break;
|
||||
|
||||
default:
|
||||
wxLogMessage(wxT("Syntax error detected, aborting."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
const wxString& filenameOut = parser.GetParam(0);
|
||||
std::fstream dst((LPCTSTR)filenameOut, std::ios_base::out | std::ios_base::in | std::ios_base::trunc | std::ios_base::binary);
|
||||
if (dst.fail()) {
|
||||
_ftprintf(stderr, wxT("%s: error UMD0001: Error opening output file.\n"), filenameOut.fn_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool has_errors = false;
|
||||
|
||||
// Open file ID.
|
||||
std::streamoff dst_start = stdex::idrec::open<Updater::recordid_t, Updater::recordsize_t>(dst, UPDATER_DB_ID);
|
||||
|
||||
Updater::package_info pi;
|
||||
wxString val;
|
||||
|
||||
pi.ver = parser.Found(wxT("x"), &val) ? _tcstoul(val, NULL, 16) : 0;
|
||||
|
||||
if (parser.Found(wxT("s"), &val))
|
||||
pi.desc = val;
|
||||
|
||||
if (parser.Found(wxT("l"), &val))
|
||||
pi.lang = val.ToAscii();
|
||||
|
||||
if (parser.Found(wxT("u"), &val))
|
||||
pi.url = val.ToUTF8();
|
||||
|
||||
dst << Updater::package_info_rec(pi);
|
||||
if (dst.fail()) {
|
||||
_ftprintf(stderr, wxT("%s: error UMD0002: Writing to output file failed.\n"), filenameOut.fn_str());
|
||||
has_errors = true;
|
||||
}
|
||||
|
||||
stdex::idrec::close<Updater::recordid_t, Updater::recordsize_t, UPDATER_RECORD_ALIGN>(dst, dst_start);
|
||||
|
||||
// Create cryptographic context.
|
||||
HCRYPTPROV cp = NULL;
|
||||
wxVERIFY(::CryptAcquireContext(&cp, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT));
|
||||
|
||||
// Import private key.
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
// Hash the file.
|
||||
HCRYPTHASH ch = NULL;
|
||||
wxVERIFY(::CryptCreateHash(cp, CALG_SHA1, 0, 0, &ch));
|
||||
{
|
||||
std::vector<BYTE> buf(8192);
|
||||
for (dst.seekg(0); !dst.eof();) {
|
||||
dst.read((char*)buf.data(), buf.size());
|
||||
wxVERIFY(::CryptHashData(ch, buf.data(), dst.gcount(), 0));
|
||||
}
|
||||
dst.clear();
|
||||
}
|
||||
|
||||
// Sign the hash.
|
||||
DWORD sig_size = 0;
|
||||
if (!::CryptSignHash(ch, AT_SIGNATURE, NULL, 0, NULL, &sig_size)) {
|
||||
_ftprintf(stderr, wxT("%s: error UMD0003: Signing output file failed (%i).\n"), filenameOut.fn_str(), ::GetLastError());
|
||||
has_errors = true;
|
||||
}
|
||||
Updater::signature sig(sig_size);
|
||||
if (!::CryptSignHash(ch, AT_SIGNATURE, NULL, 0, sig.data(), &sig_size)) {
|
||||
_ftprintf(stderr, wxT("%s: error UMD0003: Signing output file failed (%i).\n"), filenameOut.fn_str(), ::GetLastError());
|
||||
has_errors = true;
|
||||
}
|
||||
wxVERIFY(::CryptDestroyHash(ch));
|
||||
wxVERIFY(::CryptReleaseContext(cp, 0));
|
||||
|
||||
// Append to the end of the file.
|
||||
dst << Updater::signature_rec(sig);
|
||||
|
||||
if (has_errors) {
|
||||
dst.close();
|
||||
wxRemoveFile(filenameOut);
|
||||
return 1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
Binary file not shown.
@ -19,8 +19,8 @@
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{516AFFF6-F1E7-4806-B2E2-5CD9911ED2FB}</ProjectGuid>
|
||||
<RootNamespace>UpdMkDesc</RootNamespace>
|
||||
<ProjectGuid>{2A403460-7834-41B8-9823-199E8FE36FA8}</ProjectGuid>
|
||||
<RootNamespace>UpdSignXML</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
@ -56,25 +56,25 @@
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\..\include\Win32.props" />
|
||||
<Import Project="..\..\include\Debug.props" />
|
||||
<Import Project="UpdMkDesc.props" />
|
||||
<Import Project="UpdSignXML.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\..\include\x64.props" />
|
||||
<Import Project="..\..\include\Debug.props" />
|
||||
<Import Project="UpdMkDesc.props" />
|
||||
<Import Project="UpdSignXML.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\..\include\Win32.props" />
|
||||
<Import Project="..\..\include\Release.props" />
|
||||
<Import Project="UpdMkDesc.props" />
|
||||
<Import Project="UpdSignXML.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\..\include\x64.props" />
|
||||
<Import Project="..\..\include\Release.props" />
|
||||
<Import Project="UpdMkDesc.props" />
|
||||
<Import Project="UpdSignXML.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemGroup>
|
||||
@ -88,7 +88,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="stdafx.h" />
|
||||
<ClInclude Include="UpdMkDesc.h" />
|
||||
<ClInclude Include="UpdSignXML.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\lib\wxExtend\build\wxExtend.vcxproj">
|
||||
@ -96,7 +96,7 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="UpdMkDesc.rc" />
|
||||
<ResourceCompile Include="UpdSignXML.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<POCompile Include="locale\sl_SI.po" />
|
@ -30,12 +30,12 @@
|
||||
<ClInclude Include="stdafx.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="UpdMkDesc.h">
|
||||
<ClInclude Include="UpdSignXML.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="UpdMkDesc.rc">
|
||||
<ResourceCompile Include="UpdSignXML.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
@ -1,6 +1,6 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: UpdMkDesc\n"
|
||||
"Project-Id-Version: UpdSignXML\n"
|
||||
"POT-Creation-Date: 2016-03-15 20:09+0100\n"
|
||||
"PO-Revision-Date: 2016-03-15 20:10+0100\n"
|
||||
"Last-Translator: Simon Rozman <simon.rozman@amebis.si>\n"
|
149
UpdSignXML/main.cpp
Normal file
149
UpdSignXML/main.cpp
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
Copyright 2016 Amebis
|
||||
|
||||
This file is part of Updater.
|
||||
|
||||
Updater 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.
|
||||
|
||||
Updater 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 Updater. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#pragma comment(lib, "Crypt32.lib")
|
||||
|
||||
|
||||
///
|
||||
/// Main function
|
||||
///
|
||||
int _tmain(int argc, _TCHAR *argv[])
|
||||
{
|
||||
wxApp::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE, "program");
|
||||
|
||||
// Inizialize wxWidgets.
|
||||
wxInitializer initializer;
|
||||
if (!initializer) {
|
||||
_ftprintf(stderr, wxT("Failed to initialize the wxWidgets library, aborting.\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Parse command line.
|
||||
static const wxCmdLineEntryDesc cmdLineDesc[] =
|
||||
{
|
||||
{ wxCMD_LINE_SWITCH, "h" , "help", _("Show this help message"), wxCMD_LINE_VAL_NONE , wxCMD_LINE_OPTION_HELP },
|
||||
{ wxCMD_LINE_PARAM , NULL, NULL , _("input file" ), wxCMD_LINE_VAL_STRING, wxCMD_LINE_OPTION_MANDATORY },
|
||||
{ wxCMD_LINE_PARAM , NULL, NULL , _("output file" ), wxCMD_LINE_VAL_STRING, wxCMD_LINE_OPTION_MANDATORY },
|
||||
|
||||
{ wxCMD_LINE_NONE }
|
||||
};
|
||||
wxCmdLineParser parser(cmdLineDesc, argc, argv);
|
||||
switch (parser.Parse()) {
|
||||
case -1:
|
||||
// Help was given, terminating.
|
||||
return 0;
|
||||
|
||||
case 0:
|
||||
// everything is ok; proceed
|
||||
break;
|
||||
|
||||
default:
|
||||
wxLogMessage(wxT("Syntax error detected, aborting."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
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());
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool has_errors = false;
|
||||
|
||||
wxXmlNode *document = doc.GetDocumentNode();
|
||||
|
||||
// Examine prologue if the document is already signed.
|
||||
for (wxXmlNode *prolog = document->GetChildren(); prolog; prolog = prolog->GetNext()) {
|
||||
if (prolog->GetType() == wxXML_COMMENT_NODE) {
|
||||
wxString content = prolog->GetContent();
|
||||
if (content.length() >= _countof(wxS(UPDATER_SIGNATURE_MARK)) - 1 &&
|
||||
memcmp((const wxStringCharType*)content, wxS(UPDATER_SIGNATURE_MARK), sizeof(wxStringCharType)*(_countof(wxS(UPDATER_SIGNATURE_MARK)) - 1)) == 0)
|
||||
{
|
||||
// Previous signature found. Remove it.
|
||||
document->RemoveChild(prolog);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create cryptographic context.
|
||||
HCRYPTPROV cp = NULL;
|
||||
wxVERIFY(::CryptAcquireContext(&cp, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT));
|
||||
|
||||
// Import private key.
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
// Hash the content.
|
||||
HCRYPTHASH ch = NULL;
|
||||
wxVERIFY(::CryptCreateHash(cp, CALG_SHA1, 0, 0, &ch));
|
||||
::wxXmlHashNode(ch, document);
|
||||
|
||||
// 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;
|
||||
}
|
||||
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());
|
||||
document->AddChild(new wxXmlNode(wxXML_COMMENT_NODE, wxS(""), signature));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (has_errors) {
|
||||
wxRemoveFile(filenameOut);
|
||||
return 1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
@ -19,18 +19,16 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "UpdMkDesc.h"
|
||||
#include "UpdSignXML.h"
|
||||
#include "../include/Updater.h"
|
||||
|
||||
#include <wx/app.h>
|
||||
#include <wx/base64.h>
|
||||
#include <wx/cmdline.h>
|
||||
#include <wx/xml/xml.h>
|
||||
|
||||
#include <wxex/common.h>
|
||||
|
||||
#include <stdex/idrec.h>
|
||||
#include <wxex/xml.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#include <tchar.h>
|
20
Updater.sln
20
Updater.sln
@ -8,14 +8,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
include\Updater.h = include\Updater.h
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UpdMkDesc", "UpdMkDesc\UpdMkDesc.vcxproj", "{516AFFF6-F1E7-4806-B2E2-5CD9911ED2FB}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UpdCheck", "UpdCheck\UpdCheck.vcxproj", "{0B2B7A10-3FA0-46CD-AD89-6D02DA2C5FC3}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxExtend", "..\lib\wxExtend\build\wxExtend.vcxproj", "{A3A36689-AC35-4026-93DA-A3BA0C0E767C}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stdex", "..\lib\stdex\build\stdex.vcxproj", "{518777CC-0A59-4415-A12A-82751ED75343}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UpdSignXML", "UpdSignXML\UpdSignXML.vcxproj", "{2A403460-7834-41B8-9823-199E8FE36FA8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
@ -24,14 +24,6 @@ Global
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{516AFFF6-F1E7-4806-B2E2-5CD9911ED2FB}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{516AFFF6-F1E7-4806-B2E2-5CD9911ED2FB}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{516AFFF6-F1E7-4806-B2E2-5CD9911ED2FB}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{516AFFF6-F1E7-4806-B2E2-5CD9911ED2FB}.Debug|x64.Build.0 = Debug|x64
|
||||
{516AFFF6-F1E7-4806-B2E2-5CD9911ED2FB}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{516AFFF6-F1E7-4806-B2E2-5CD9911ED2FB}.Release|Win32.Build.0 = Release|Win32
|
||||
{516AFFF6-F1E7-4806-B2E2-5CD9911ED2FB}.Release|x64.ActiveCfg = Release|x64
|
||||
{516AFFF6-F1E7-4806-B2E2-5CD9911ED2FB}.Release|x64.Build.0 = Release|x64
|
||||
{0B2B7A10-3FA0-46CD-AD89-6D02DA2C5FC3}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{0B2B7A10-3FA0-46CD-AD89-6D02DA2C5FC3}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{0B2B7A10-3FA0-46CD-AD89-6D02DA2C5FC3}.Debug|x64.ActiveCfg = Debug|x64
|
||||
@ -56,6 +48,14 @@ Global
|
||||
{518777CC-0A59-4415-A12A-82751ED75343}.Release|Win32.Build.0 = Release|Win32
|
||||
{518777CC-0A59-4415-A12A-82751ED75343}.Release|x64.ActiveCfg = Release|x64
|
||||
{518777CC-0A59-4415-A12A-82751ED75343}.Release|x64.Build.0 = Release|x64
|
||||
{2A403460-7834-41B8-9823-199E8FE36FA8}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{2A403460-7834-41B8-9823-199E8FE36FA8}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{2A403460-7834-41B8-9823-199E8FE36FA8}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{2A403460-7834-41B8-9823-199E8FE36FA8}.Debug|x64.Build.0 = Debug|x64
|
||||
{2A403460-7834-41B8-9823-199E8FE36FA8}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{2A403460-7834-41B8-9823-199E8FE36FA8}.Release|Win32.Build.0 = Release|Win32
|
||||
{2A403460-7834-41B8-9823-199E8FE36FA8}.Release|x64.ActiveCfg = Release|x64
|
||||
{2A403460-7834-41B8-9823-199E8FE36FA8}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -39,58 +39,12 @@
|
||||
//
|
||||
// Human readable product version and build year for UI
|
||||
//
|
||||
#define UPDATER_VERSION_STR "2.0-alpha"
|
||||
#define UPDATER_VERSION_STR "1.0"
|
||||
#define UPDATER_BUILD_YEAR_STR "2016"
|
||||
|
||||
|
||||
#if !defined(RC_INVOKED) && !defined(MIDL_PASS)
|
||||
|
||||
#include <stdex/idrec.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
///
|
||||
/// Data records alignment
|
||||
///
|
||||
#define UPDATER_RECORD_ALIGN 1
|
||||
|
||||
|
||||
///
|
||||
/// Database IDs
|
||||
///
|
||||
#define UPDATER_DB_ID (*(Updater::recordid_t*)"UPD")
|
||||
|
||||
|
||||
namespace Updater {
|
||||
typedef unsigned __int32 recordid_t;
|
||||
typedef unsigned __int32 recordsize_t;
|
||||
|
||||
|
||||
///
|
||||
/// Package information
|
||||
///
|
||||
struct package_info {
|
||||
unsigned __int32 ver; ///< Binary Version
|
||||
std::wstring desc; ///< Human Readable Description (i. e. "1.2")
|
||||
std::string lang; ///< Package Language (ISO 639-1 language code followed by underscore and ISO 3166 country code)
|
||||
std::string url; ///< Package download URL (UTF-8 encoded)
|
||||
};
|
||||
|
||||
typedef stdex::idrec::record<package_info, recordid_t, recordsize_t, UPDATER_RECORD_ALIGN> package_info_rec;
|
||||
|
||||
|
||||
///
|
||||
/// Signature
|
||||
///
|
||||
typedef std::vector<unsigned char> signature;
|
||||
|
||||
typedef stdex::idrec::record<signature, recordid_t, recordsize_t, UPDATER_RECORD_ALIGN> signature_rec;
|
||||
};
|
||||
|
||||
|
||||
const Updater::recordid_t stdex::idrec::record<Updater::package_info, Updater::recordid_t, Updater::recordsize_t, UPDATER_RECORD_ALIGN>::id = *(Updater::recordid_t*)"PKG";
|
||||
const Updater::recordid_t stdex::idrec::record<Updater::signature , Updater::recordid_t, Updater::recordsize_t, UPDATER_RECORD_ALIGN>::id = *(Updater::recordid_t*)"SGN";
|
||||
#define UPDATER_SIGNATURE_MARK "SHA1SIGN:"
|
||||
|
||||
#endif // !defined(RC_INVOKED) && !defined(MIDL_PASS)
|
||||
|
Loading…
x
Reference in New Issue
Block a user