/*
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 .
*/
#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 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(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(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 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;
}