Newer
Older
Import / applications / MakePDF / LicenseCheck.cpp
#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(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(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;
}