Binary repository database dropped and XML variation introduced

This commit is contained in:
Simon Rozman 2016-03-17 14:04:33 +01:00
parent 79fd6c5348
commit eeab3a1ec1
15 changed files with 259 additions and 477 deletions

View File

@ -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;
}

View File

@ -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>

View File

@ -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;
}

View File

@ -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" />

View File

@ -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>

View File

@ -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
View 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;
}

View File

@ -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>

View File

@ -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

View File

@ -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)