#include <string>
#include <cstdlib>
#include <cstdio>
#include "../Crypto/Base64/Base64.h"
#include "../Crypto/Hash/SHA256.h"
#include "../Crypto/Integer/Integer.h"
#include "LicenseCheck.h"
#include "Util.h"
#include "PublicKey.h"
/*
#ifdef GUI_APP
#include <QString>
#include <QNetworkInterface>
#endif
*/
// static
bool LicenseChecker::ValidateData(const std::vector<uint8_t>& data, const std::vector<uint8_t>& signature)
{
// Calculate what the hash should be
uint32_t hash[8];
sha256((uint8_t*)data.data(), data.size(), hash);
uint2048_t calculatedHash(static_cast<uint64_t>(0ULL));
for (int i = 0; i < 32; i++)
{
uint32_t i1 = i / 4;
uint32_t i2 = i % 4;
calculatedHash <<= 8;
calculatedHash |= hash[i1] >> ((3-i2)*8) & 0xFF;
}
#ifndef _NDEBUG
printf("Calculated Hash: ");
calculatedHash.Print();
#endif
// Get the signed hash (that the server signed using the private key)
uint2048_t signedHash(static_cast<uint64_t>(0ULL));
for (int i = 0; i < signature.size(); i++)
{
signedHash <<= 8;
signedHash |= uint2048_t(signature[i]);
}
#ifndef _NDEBUG
printf("\nSigned Hash: ");
signedHash.Print();
#endif
// Decrypt the signed hash using the public key to get the original hash the server calculated
uint2048_t exponent(publicExponent);
uint2048_t modulus(publicModulus);
uint2048_t decryptedHash = uint2048_t::ExpMod(signedHash, exponent, modulus);
// Finally check that the hashes match (for the last 256 bits)
decryptedHash <<= 2048-256+8;
decryptedHash >>= 2048-256+8;
#ifndef _NDEBUG
printf("\nDecrypted Hash: ");
decryptedHash.Print();
#endif
bool okay = calculatedHash == decryptedHash;
#ifndef _NDEBUG
printf("\nVerified %s\n", (okay) ? "OK" : "Failed");
#endif
return okay;
}
// static
bool LicenseChecker::ValidateLicense(const std::string& licenseText)
{
int lineStart = 0;
int lineEnd = 0;
std::string lic;
std::string sig;
int foundMarker = 0;
while (lineEnd < licenseText.size())
{
if (licenseText[lineEnd] != '\n')
{
lineEnd++;
continue;
}
std::string line = licenseText.substr(lineStart, lineEnd - lineStart + 1);
std::string start = line.substr(0, 9);
if (start == "---------")
{
foundMarker++;
if (foundMarker == 2)
{
break;
}
}
else if (start == "Signature")
{
sig += trim(line.substr(13));
}
else
{
lic += line;
}
lineEnd++;
lineStart = lineEnd;
}
printf(" sig = -%s-\n", sig.c_str());
std::vector<uint8_t> data(lic.c_str(), lic.c_str() + lic.size());
std::vector<uint8_t> signature;
Base64::Decode(sig, signature);
printf(" signature = ");
for (int i = 0; i < signature.size(); i++)
printf("%02x", signature[i]);
printf("\n");
return ValidateData(data, signature);
}
// static
std::string LicenseChecker::LicenseFileLocation()
{
#ifdef _WIN32
const char* home = getenv("userprofile");
#else
const char* home = getenv("HOME");
#endif
std::string path = home;
path += "/.config/WickedDocs/license.txt";
return path;
}
bool CheckLicense()
{
std::string path = LicenseChecker::LicenseFileLocation();
#ifndef _NDEBUG
printf("Opening license: -%s-\n", path.c_str());
#endif
FILE* f = fopen(path.c_str(), "rt");
if (!f)
return false;
std::string all;
char buf[1025];
while (fgets(buf, 1024, f))
{
buf[1024] = 0; // nul-terminate
all += buf;
}
fclose(f);
return LicenseChecker::ValidateLicense(all);
}
LicenseChecker::LicenseChecker()
{
licenseValid = false;
std::string path = LicenseFileLocation();
std::vector<uint8_t> data;
licenseFound = slurp(path.c_str(), data);
if (!licenseFound)
return;
data.push_back(0);
std::string all = (char*)data.data();
/*
FILE* f = fopen(path.c_str(), "rt");
licenseFound = (!!f);
if (!licenseFound)
return;
std::string all;
char buf[1025];
while (fgets(buf, 1024, f))
{
buf[1024] = 0; // nul-terminate
all += buf;
}
fclose(f);
*/
licenseValid = ValidateLicense(all);
if (licenseValid)
{
auto strs = split(all, '\n');
for (auto s : strs)
{
auto kvp = split(s, '=');
if (kvp.size() == 2)
{
keyValuePairs[trim(kvp[0])] = trim(kvp[1]);
}
}
}
if (keyValuePairs.count("Trial"))
trial = true;
if (keyValuePairs.count("Version"))
version = keyValuePairs["Version"];
//int trialExecutionsRemaining;
}
bool LicenseChecker::InstallLicense(std::string licenseText)
{
// TODO: If this is *not* a trial license:
// phone-home to associate the MAC address with the license (and count the times this has been done)
// - perhaps re-generate the license and then write to the standard location
// write this to the standard location
// perhaps use a different public-private key pair for trail and license activations - the paid license that is emailed
// is then essentially a token which can be renewed to do an activation - the activation signed license is then what
// is checked and only that public key can unlock the application. The private-public key for the token doesn't need to
// be know by the application - the server signs it, the application sends it back to the server which validates it, then
// regenerates an activated license.
std::string path = LicenseChecker::LicenseFileLocation();
FILE* f = fopen(path.c_str(), "wt");
if (!f)
return false;
fwrite(licenseText.c_str(), licenseText.size(), 1, f);
fclose(f);
return true;
}