Newer
Older
Import / applications / MakePDF / FingerPrint.cpp
/*
   WickedDocs
   (C) Copyright 2020
   John Ryland
*/
#include "FingerPrint.h"
#include <stdio.h>
#include "Util.h"
#include "Crypto/Hash/SHA256.h"
#include "Crypto/Base64/Base64.h"
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <iostream>
#include <iomanip>
#include <algorithm>

#if defined(__linux__)
#  include <linux/limits.h>
#endif

#if defined(__APPLE__)
#  define DARWIN
#endif

#ifdef DARWIN

#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/network/IOEthernetInterface.h>
#include <IOKit/network/IONetworkInterface.h>
#include <IOKit/network/IOEthernetController.h>
#include <DiskArbitration/DiskArbitration.h>
#include <mach-o/arch.h>
#include <sys/param.h>
#include <sys/mount.h>

// macOS specific
uint64_t GetMacAddress()
{
  io_iterator_t intfIterator;
  UInt8 MACAddress[kIOEthernetAddressSize] = { 0 };

  // iterator for interfaces
  CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOEthernetInterfaceClass);
  if (matchingDict)
  {
    CFMutableDictionaryRef propertyMatchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    if (propertyMatchDict)
    {
      CFDictionarySetValue(propertyMatchDict, CFSTR(kIOPrimaryInterface), kCFBooleanTrue); 
      CFDictionarySetValue(matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict);
      CFRelease(propertyMatchDict);
    }
  }

  // iterate the interfaces
  if (IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &intfIterator) == KERN_SUCCESS)
  {
    io_object_t intfService;
    io_object_t controllerService;
    while ((intfService = IOIteratorNext(intfIterator)))
    {
      // obtain the mac address
      CFDataRef MACAddressAsCFData;        
      if (IORegistryEntryGetParentEntry(intfService, kIOServicePlane, &controllerService) == KERN_SUCCESS)
      {
        MACAddressAsCFData = (CFDataRef)IORegistryEntryCreateCFProperty(controllerService, CFSTR(kIOMACAddress), kCFAllocatorDefault, 0);
        if (MACAddressAsCFData)
        {
          // copy it
          CFDataGetBytes(MACAddressAsCFData, CFRangeMake(0, kIOEthernetAddressSize), MACAddress);
          CFRelease(MACAddressAsCFData);
        }
        IOObjectRelease(controllerService);
      }
      IOObjectRelease(intfService);
    }
  }

  // release the iterator.  
  IOObjectRelease(intfIterator);

  // convert it to a 64bit number
  uint64_t res = 0;
  for (int i = 0; i < kIOEthernetAddressSize; i++)
  {
    res <<= 8;
    res |= MACAddress[i];
  }
  return res;
}

#endif

/*

// This is not working on MAC
static QString MacAddress()
{
  QNetworkConfiguration nc;
  QNetworkConfigurationManager ncm;
  QList<QNetworkConfiguration> configsForEth,configsForWLAN,allConfigs;
  // getting all the configs we can
  foreach (nc,ncm.allConfigurations(QNetworkConfiguration::Active))
  {
    if(nc.type() == QNetworkConfiguration::InternetAccessPoint)
    {
      // selecting the bearer type here
      if(nc.bearerType() == QNetworkConfiguration::BearerWLAN)
      {
        configsForWLAN.append(nc);
      }
      if(nc.bearerType() == QNetworkConfiguration::BearerEthernet)
      {
        configsForEth.append(nc);
      }
    }
  }
  // further in the code WLAN's and Eth's were treated differently
  allConfigs.append(configsForWLAN);
  allConfigs.append(configsForEth);
  QString MAC;
  foreach(nc,allConfigs)
  {
    QNetworkSession networkSession(nc);
    QNetworkInterface netInterface = networkSession.interface();
    // these last two conditions are for omiting the virtual machines' MAC
    // works pretty good since no one changes their adapter name
    if(!(netInterface.flags() & QNetworkInterface::IsLoopBack)
        && !netInterface.humanReadableName().toLower().contains("vmware")
        && !netInterface.humanReadableName().toLower().contains("virtual"))
    {
      MAC = QString(netInterface.hardwareAddress());
      return MAC;
      //break;
    }
  }

  foreach (QNetworkInterface netInterface, QNetworkInterface::allInterfaces())
  {
    // Return only the first non-loopback MAC Address
    if (!(netInterface.flags() & QNetworkInterface::IsLoopBack))
      return netInterface.hardwareAddress();
  }
  return QString();
}

*/


#if 0

#ifdef _WIN32

#include <windows.h>
#include <intrin.h>
#include <iphlpapi.h>
 
// we just need this for purposes of unique machine id. 
// So any one or two mac's is fine.
u16 hashMacAddress( PIP_ADAPTER_INFO info )
{
   u16 hash = 0;
   for ( u32 i = 0; i < info->AddressLength; i++ )
   {
      hash += ( info->Address[i] << (( i & 1 ) * 8 ));
   }
   return hash;
}
 
void getMacHash( u16& mac1, u16& mac2 )
{
   IP_ADAPTER_INFO AdapterInfo[32];
   DWORD dwBufLen = sizeof( AdapterInfo );
 
   DWORD dwStatus = GetAdaptersInfo( AdapterInfo, &dwBufLen );
   if ( dwStatus != ERROR_SUCCESS )
      return; // no adapters.
 
   PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo;
   mac1 = hashMacAddress( pAdapterInfo );
   if ( pAdapterInfo->Next )
      mac2 = hashMacAddress( pAdapterInfo->Next );
 
   // sort the mac addresses. We don't want to invalidate
   // both macs if they just change order.
   if ( mac1 > mac2 )
   {
      u16 tmp = mac2;
      mac2 = mac1;
      mac1 = tmp;
   }
}
 
u16 getVolumeHash()
{
   DWORD serialNum = 0;
 
   // Determine if this volume uses an NTFS file system.
   GetVolumeInformation( "c:\\", NULL, 0, &serialNum, NULL, NULL, NULL, 0 );
   u16 hash = (u16)(( serialNum + ( serialNum >> 16 )) & 0xFFFF );
    
   return hash;
}
 
u16 getCpuHash()
{
   int cpuinfo[4] = { 0, 0, 0, 0 };
   __cpuid( cpuinfo, 0 );
   u16 hash = 0;
   u16* ptr = (u16*)(&cpuinfo[0]);
   for ( u32 i = 0; i < 8; i++ )
      hash += ptr[i];
 
   return hash;
}
 
const char* getMachineName()
{
   static char computerName[1024];
   DWORD size = 1024;
   GetComputerName( computerName, &size );
   return &(computerName[0]);
}


#else

#include "machine_id.h"
 
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
 
#include <sys/types.h>
#include <sys/ioctl.h>
 
#ifdef DARWIN
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if_dl.h>
#include <ifaddrs.h>
#include <net/if_types.h>
#else //!DARWIN
#include <linux/if.h>
#include <linux/sockios.h>
#endif //!DARWIN
 
#include <sys/resource.h>
#include <sys/utsname.h>
 
//---------------------------------get MAC addresses ---------------------------------
// we just need this for purposes of unique machine id. So any one or two 
// mac's is fine.
u16 hashMacAddress( u8* mac )
{
   u16 hash = 0;
 
   for ( u32 i = 0; i < 6; i++ )
   {
      hash += ( mac[i] << (( i & 1 ) * 8 ));
   }
   return hash;
}
 
void getMacHash( u16& mac1, u16& mac2 )
{
   mac1 = 0;
   mac2 = 0;
 
#ifdef DARWIN
 
   struct ifaddrs* ifaphead;
   if ( getifaddrs( &ifaphead ) != 0 )
      return;
 
   // iterate over the net interfaces
   bool foundMac1 = false;
   struct ifaddrs* ifap;
   for ( ifap = ifaphead; ifap; ifap = ifap->ifa_next )
   {
      struct sockaddr_dl* sdl = (struct sockaddr_dl*)ifap->ifa_addr;
      if ( sdl && ( sdl->sdl_family == AF_LINK ) && ( sdl->sdl_type == IFT_ETHER ))
      {
          if ( !foundMac1 )
          {
             foundMac1 = true;
             mac1 = hashMacAddress( (u8*)(LLADDR(sdl))); //sdl->sdl_data) + 
                                    sdl->sdl_nlen) );
          } else {
             mac2 = hashMacAddress( (u8*)(LLADDR(sdl))); //sdl->sdl_data) + 
                                    sdl->sdl_nlen) );
             break;
          }
      }
   }
   
   freeifaddrs( ifaphead );
 
#else // !DARWIN
 
   int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP );
   if ( sock < 0 ) return;
    
   // enumerate all IP addresses of the system
   struct ifconf conf;
   char ifconfbuf[ 128 * sizeof(struct ifreq)  ];
   memset( ifconfbuf, 0, sizeof( ifconfbuf ));
   conf.ifc_buf = ifconfbuf;
   conf.ifc_len = sizeof( ifconfbuf );
   if ( ioctl( sock, SIOCGIFCONF, &conf ))
   {
      assert(0);
      return;
   }
    
   // get MAC address
   bool foundMac1 = false;
   struct ifreq* ifr;
   for ( ifr = conf.ifc_req; (s8*)ifr < (s8*)conf.ifc_req + conf.ifc_len; ifr++ ) 
   {
      if ( ifr->ifr_addr.sa_data == (ifr+1)->ifr_addr.sa_data ) 
         continue;  // duplicate, skip it
      
      if ( ioctl( sock, SIOCGIFFLAGS, ifr )) 
         continue;  // failed to get flags, skip it
      if ( ioctl( sock, SIOCGIFHWADDR, ifr ) == 0 ) 
      {
         if ( !foundMac1 )
         {
            foundMac1 = true;
            mac1 = hashMacAddress( (u8*)&(ifr->ifr_addr.sa_data));
         } else {
            mac2 = hashMacAddress( (u8*)&(ifr->ifr_addr.sa_data));
            break;
         }
      }
   }
    
   close( sock );
 
#endif // !DARWIN
 
   // sort the mac addresses. We don't want to invalidate
   // both macs if they just change order.
   if ( mac1 > mac2 )
   {
      u16 tmp = mac2;
      mac2 = mac1;
      mac1 = tmp;
   }
}
 
u16 getVolumeHash()
{
   // we don't have a 'volume serial number' like on windows. 
   // Lets hash the system name instead.
   u8* sysname = (u8*)getMachineName();
   u16 hash = 0;
 
   for ( u32 i = 0; sysname[i]; i++ )
      hash += ( sysname[i] << (( i & 1 ) * 8 ));
 
   return hash;
}
 
#ifdef DARWIN   
 #include <mach-o/arch.h>
 u16 getCpuHash()
 { 
     const NXArchInfo* info = NXGetLocalArchInfo();
     u16 val = 0;
     val += (u16)info->cputype;
     val += (u16)info->cpusubtype;
     return val;
 }

u64 getSystemSerialNumberHash()
{
   u64 hash = 0;
   u8* phash = (u8*)&hash;

   io_service_t platformExpert = IOServiceGetMatchingService( kIOMasterPortDefault, IOServiceMatching( "IOPlatformExpertDevice" ));

   if ( !platformExpert ) return 0;

   CFTypeRef t = IORegistryEntryCreateCFProperty( platformExpert, CFSTR( kIOPlatformSerialNumberKey ), kCFAllocatorDefault, 0 );
   CFStringRef serialNumber = (CFStringRef)t;

   if ( serialNumber )
   {
      const char* cs = (const char*) CFStringGetCStringPtr( serialNumber, kCFStringEncodingMacRoman ); 

      u32 i = 0;
      while ( *cs )
         phash[i++%sizeof(u64)] ^= *cs++;
   }

   CFRelease( serialNumber );
   IOObjectRelease( platformExpert );

   return hash;
}

#else // !DARWIN
 
 static void getCpuid( u32* p, u32 ax )
 {
    __asm __volatile
    (   "movl %%ebx, %%esi\n\t"
        "cpuid\n\t"
        "xchgl %%ebx, %%esi"
        : "=a" (p[0]), "=S" (p[1]),
          "=c" (p[2]), "=d" (p[3])
        : "0" (ax)
    );
 }
 
 u16 getCpuHash()
 {  
    u32 cpuinfo[4] = { 0, 0, 0, 0 };
    getCpuid( cpuinfo, 0 );
    u16 hash = 0;
    u32* ptr = (&cpuinfo[0]);
    for ( u32 i = 0; i < 4; i++ )
       hash += (ptr[i] & 0xFFFF) + ( ptr[i] >> 16 ); 
 
    return hash;
 }
#endif // !DARWIN
 
const char* getMachineName()
{
   static struct utsname u;
 
   if ( uname( &u ) < 0 )
   {
      assert(0);
      return "unknown";
   }
 
   return u.nodename;
}

#endif




u16 mask[5] = { 0x4e25, 0xf4a1, 0x5437, 0xab41, 0x0000 };
 
static void smear( u16* id )
{
   for ( u32 i = 0; i < 5; i++ )
      for ( u32 j = i; j < 5; j++ )
         if ( i != j )
            id[i] ^= id[j];
 
   for ( u32 i = 0; i < 5; i++ )
      id[i] ^= mask[i];
}
 
static void unsmear( u16* id )
{
   for ( u32 i = 0; i < 5; i++ )
      id[i] ^= mask[i];
    
   for ( u32 i = 0; i < 5; i++ )
      for ( u32 j = 0; j < i; j++ )
         if ( i != j )
            id[4-i] ^= id[4-j];
}


static u16* computeSystemUniqueId()
{
   static u16 id[5];
   static bool computed = false;
 
   if ( computed ) return id;
 
   // produce a number that uniquely identifies this system.
   id[0] = getCpuHash(); 
   id[1] = getVolumeHash(); 
   getMacHash( id[2], id[3] );
 
   // fifth block is some checkdigits
   id[4] = 0; 
   for ( u32 i = 0; i < 4; i++ )
      id[4] += id[i];
    
   smear( id );
    
   computed = true;
   return id;
}


const char* getSystemUniqueId()
{
   // get the name of the computer
   KxCbuf buf;
   buf << getMachineName();
 
   u16* id = computeSystemUniqueId();
   for ( u32 i = 0; i < 5; i++ )
   {
      char num[16];
      snprintf( num, 16, "%x", id[i] );
      buf << "-";
      switch( strlen( num ))
      {
      case 1: buf << "000"; break;
      case 2: buf << "00";  break;
      case 3: buf << "0";   break;
      }
      buf << num;
   }
    
   char* p = buf.getBuffer();
   while ( *p ) { *p = toupper( *p ); p++; }
 
   return KxSymbol( buf.getBuffer()).string();
}


static bool validate( KxSymbol testIdString )
{
   // unpack the given string. parse failures return false.
   KxCbuf testString;
   testString << testIdString;
   char* testName = strtok( testString.getBuffer(), "-" );
   if ( !testName ) return false;
 
   u16 testId[5];
   for ( u32 i = 0; i < 5; i++ )
   {
      char* testNum = strtok( NULL, "-" );
      if ( !testNum ) return false;
      testId[i] = (u16)(strtol( testNum, NULL, 16 ));
   }
   unsmear( testId );
 
   // make sure this id is valid - by looking at the checkdigits
   u16 check = 0;
   for ( u32 i = 0; i < 4; i++ )
      check += testId[i];
   if ( check != testId[4] ) return false;
    
   // get the current system information
   u16 systemId[5];
   memcpy( systemId, computeSystemUniqueId(), sizeof( systemId ));
   unsmear( systemId );
    
   // now start scoring the match
   u32 score = 0;
    
   for ( u32 i = 0; i < 4; i++ )
      if ( testId[i] == systemId[i] )
         score++;
 
   if ( !strcmp( getMachineName(), testName ))
      score++;
 
   // if we score 3 points or more then the id matches.
   return ( score >= 3 ) ? true : false; 
}


#endif


#ifdef DARWIN

// macOS specific
std::string UuidOfFile(const char* file)
{
  char temp[64] = { 0 };
  struct statfs b;
  statfs(file, &b);
  auto dict = DADiskCopyDescription(DADiskCreateFromBSDName(kCFAllocatorDefault, DASessionCreate(kCFAllocatorDefault), b.f_mntfromname));
  CFStringGetCString(CFUUIDCreateString(kCFAllocatorDefault, (CFUUIDRef)CFDictionaryGetValue(dict, kDADiskDescriptionVolumeUUIDKey)), temp, 64, kCFStringEncodingUTF8);
  return temp;
}

#else

std::string UuidOfFile(const char* file)
{
  // TODO: need to implement
  return file;
}

#endif


// Should work on Linux and macOS
static inline void do_cpuid(uint32_t selector, uint32_t *data)
{
  __asm__ __volatile__ (
    "xchg %%ebx, %k[tempreg]\n\t"
    "cpuid\n\t"
    "xchg %%ebx, %k[tempreg]\n"
    : "=a" (data[0]),
    [tempreg]"=&r" (data[1]),
    "=c" (data[2]),
    "=d" (data[3])
    : "a"(selector),
    "c"(0));
}


/*
static inline void getCpuid( u32* p, u32 ax )
{
  __asm __volatile
  (
    "movl %%ebx, %%esi\n\t"
    "cpuid\n\t"
    "xchgl %%ebx, %%esi"
    : "=a" (p[0]),
      "=S" (p[1]),
      "=c" (p[2]),
      "=d" (p[3])
    : "0" (ax)
  );
}
 
 u16 getCpuHash()
 {  
    u32 cpuinfo[4] = { 0, 0, 0, 0 };
    getCpuid( cpuinfo, 0 );
    u16 hash = 0;
    u32* ptr = (&cpuinfo[0]);
    for ( u32 i = 0; i < 4; i++ )
       hash += (ptr[i] & 0xFFFF) + ( ptr[i] >> 16 ); 
 
    return hash;
 }
*/


std::string CpuString()
{
  /*
  uint32_t data[4];
  for (int sel = 1; sel < 5; ++sel)
  {
    do_cpuid( sel, data );
    uint32_t c = data[3];
    data[3] = data[2];
    data[2] = c;
    printf(" %x %x %x %x  ", data[0], data[1], data[2], data[3]);
    printf("\n");
  }
  */

  std::string cpuStr;
  uint32_t data[4];
  do_cpuid( 0, data );
  data[0] = data[1];
  data[1] = data[3];

  // Get CPU brand / vendor_id
  for (int i = 0; i < 12; i++)
    if (((char*)data)[i])
      cpuStr += ((char*)data)[i];

  //  The following are known ID strings from virtual machines:
  //    "xobVxobVxobV" - VirtualBox
  //    "VboxVboxVbox" - VirtualBox (maybe)
  //    "bhyve bhyve " – bhyve
  //    "KVMKVMKVM"    – KVM
  //    "Microsoft Hv" – Microsoft Hyper-V or Windows Virtual PC
  //    " lrpepyh vr"  – Parallels (it possibly should be "prl hyperv ", but it is encoded as " lrpepyh vr" due to an endianness mismatch)
  //    "VMwareVMware" – VMware
  //    "XenVMMXenVMM" – Xen HVM
  //    "ACRNACRNACRN" – Project ACRN

  // Get model of the CPU / model_name
  cpuStr += " ";
  for (int sel = 2; sel < 4; ++sel)
  {
    do_cpuid( sel | 0x80000000, data );
    for (int i = 0; i < 16; i++)
      if (((char*)data)[i])
        cpuStr += ((char*)data)[i];
  }

  // Not sure if VMs report fixed model or host model
  // QEMU can report eg:  "QEMU Virtual CPU version 2.0.0"  (it returns GenuineIntel for vendor_id)
  return cpuStr;
}


#if defined(_WIN32)

#include <direct.h>

static std::string GetRealPath(const std::string& dirName)
{
  DWORD len = GetFullPathNameA(dirName.c_str(), 0, NULL, NULL);
  std::string res(len, '-');
  GetFullPathNameA(dirName.c_str(), len, &res[0], NULL);
  printf("result of get real path of -%s- is -%s-\n", dirName.c_str(), res.c_str());
  return res;
}

static bool getCurrentDirectory(char* dirName, size_t dirNameCapacity)
{
  return _getcwd(dirName, dirNameCapacity);
}

static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
{
  return GetModuleFileNameA(NULL, pathName, (DWORD)pathNameCapacity);
}

#elif defined(__linux__) /* elif of: #if defined(_WIN32) */

#include <unistd.h>
#include <libgen.h>

static std::string GetBaseName(const std::string& filePath)
{
  const size_t len = filePath.size() + 1;
  char tmp[len];
  memcpy(tmp, filePath.c_str(), len);
  return basename(tmp);
}

static std::string GetRealPath(const std::string& dirName)
{
  std::string res(PATH_MAX, '-');
  realpath(dirName.c_str(), &res[0]);
  res = res.c_str();  // remove trailing \0 from string
  return res;
}

static bool getCurrentDirectory(char* dirName, size_t dirNameCapacity)
{
  return getcwd(dirName, dirNameCapacity);
}

static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
{
  size_t pathNameSize = readlink("/proc/self/exe", pathName, pathNameCapacity - 1);
  pathName[pathNameSize] = '\0';
  return pathNameSize;
}

#elif defined(__APPLE__) /* elif of: #elif defined(__linux__) */

#include <mach-o/dyld.h>
#include <libgen.h>

static std::string GetBaseName(const std::string& filePath)
{
  const size_t len = filePath.size() + 1;
  char tmp[len];
  memcpy(tmp, filePath.c_str(), len);
  return basename(tmp);
}

static std::string GetRealPath(const std::string& dirName)
{
  std::string res(PATH_MAX, '-');
  realpath(dirName.c_str(), &res[0]);
  res = res.c_str();  // remove trailing \0 from string
  return res;
}

static bool getCurrentDirectory(char* dirName, size_t dirNameCapacity)
{
  return getcwd(dirName, dirNameCapacity);
}

static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
{
  uint32_t pathNameSize = 0;

  _NSGetExecutablePath(NULL, &pathNameSize);

  if (pathNameSize > pathNameCapacity)
    pathNameSize = pathNameCapacity;

  if (!_NSGetExecutablePath(pathName, &pathNameSize))
  {
    char real[PATH_MAX];

    if (realpath(pathName, real) != NULL)
    {
      pathNameSize = strlen(real);
      strncpy(pathName, real, pathNameSize);
    }

    return pathNameSize;
  }

  return 0;
}

#else /* else of: #elif defined(__APPLE__) */
#  error provide your own implementation
#endif /* end of: #if defined(_WIN32) */


std::string GetCurrentDirectory()
{
  size_t siz = 32;
  std::string exe(siz, '-');
  while (!getCurrentDirectory(&exe[0], siz) && siz < 4096)
  {
    siz *= 2;
    exe.resize(siz, '-');
  }
  if (siz >= 4096)
  {
    printf("Error 0x13d2\n");
    exit(-1);
  }
  exe = exe.c_str(); // trim trailing \0 from size of string
  return exe;
}


std::string GetExecutablePath()
{
  size_t siz = 256;
  std::string exe(siz, '-');
  while (true)
  {
    size_t realSiz = getExecutablePathName(&exe[0], siz);
    if (realSiz > 0 && realSiz < siz)
    {
      exe.resize(realSiz, '-');
      break;
    }
    if (siz > 4096)
    {
      printf("Error 0x1342\n");
      exit(-1);
    }
    siz *= 2;
    exe.resize(siz, '-');
  }
  exe = exe.c_str(); // trim trailing \0 from size of string
  return exe;
}



bool CheckApplicationName(const char* filePath)
{
  std::string exe = GetExecutablePath();
  std::string cwd = GetCurrentDirectory();
  std::string file1 = filePath;
  std::string file = GetRealPath(cwd + "/" + file1);

  if (exe != file)
  {
    // printf("exe names don't match: -%s- != -%s+%s - %s-\n", exe.c_str(), cwd.c_str(), file1.c_str(), file.c_str());
    printf("Error 0xa2e3f\n");
#ifdef _NDEBUG
    exit(-1);
#endif
    return false;
  }
  return true;
}


std::string ApplicationHash(const char* file)
{
  char exePath[1024];
  size_t siz = getExecutablePathName(exePath, 1024);
  if (std::string(exePath) != file)
  {
    // printf("exe names don't match: -%s- != -%s-\n", exePath, file);
    printf("Error 0xb2957\n");
    exit(-1);
  }


  std::vector<uint8_t> exe;
  if (!slurp(file, exe))
  {
    std::cout << "couldn't slurp " << file << std::endl;
    return "";
  }
  uint32_t hash[8];
  sha256(exe.data(), exe.size(), hash);
  std::string b64;
  std::vector<uint8_t> input_data((uint8_t*)hash, (uint8_t*)hash + 32);
  if (!Base64::Encode(input_data, b64))
  {
    std::cout << "couldn't encode base64 " << file << std::endl;
    return "";
  }
  return b64;
}


std::string SmudgedFingerPrint(const char* file)
{
  uint32_t data[12 + 6];
  do_cpuid( 0, data );
  data[0] = data[1];
  data[1] = data[3];
  data[3] = (' ' << 24) | (':' << 16) | (':' << 8) | (' ' << 0);
  do_cpuid( 0x80000002, &data[4] );
  do_cpuid( 0x80000003, &data[8] );
  uint64_t *fingerPrint = (uint64_t*)data;
  std::string vol = UuidOfFile(file);
  std::string vol2;
	std::stringstream ss(vol);
	std::string item;
	while (std::getline(ss, item, '-'))
		vol2 += item;
  fingerPrint[6] = std::stoull(vol2.substr( 0, 16).c_str(), 0, 16);
  fingerPrint[7] = std::stoull(vol2.substr(16, 16).c_str(), 0, 16);
  fingerPrint[8] = GetMacAddress();

/*  
  printf("vol: %llx%llx\n", fingerPrint[6], fingerPrint[7]);
  printf("mac: %llx\n", fingerPrint[8]);
  printf("cpu: %s\n", (char*)fingerPrint);
*/

  uint32_t position[18] = { 0, 11, 2, 5, 9, 15, 17, 8, 16, 4, 6, 13, 1, 14, 3, 10, 12, 7 };
  uint32_t pattern[] = { 0x12345678, 0x87654321, 0x43218765, 0x56781234, 0x34127856, 0x65872143, 0x78563412, 0x21436587,
                         0x12563478, 0x87436521, 0x56127834, 0x43872165, 0x12345678, 0x87654321, 0x43218765, 0x56781234,
                         0x34127856, 0x65872143, 0x78563412, 0x21436587, 0x12563478, 0x87436521, 0x56127834, 0x43872165 };
  uint32_t smudgedData[12 + 6];
  // printf("FingerPrint: 0x");
  for (int i = 0; i < 18; i++)
  {
    smudgedData[i] = data[position[i]] ^ pattern[i];
    // printf("%08x", smudgedData[i]);
  }
  // printf("\n");

  std::string b64;
  std::vector<uint8_t> input_data((uint8_t*)smudgedData, (uint8_t*)smudgedData + 18*4);
  Base64::Encode(input_data, b64);

//  printf("vol: %s\n",  vol2.c_str());
//  printf("vol: -%llx- -%llx-\n", x1, x2);
//  printf("mac: %llx\n", GetMacAddress());

  return b64;
}


std::string DesmudgeFingerPrint(const char* smudge)
{
  std::string s = smudge;
  std::vector<uint8_t> d;
  if (!Base64::Decode(s, d))
  {
    printf("error decoding base64\n");
    return "";
  }

  uint32_t* smudgedData = (uint32_t*)d.data();
  uint32_t position[18] = { 0, 11, 2, 5, 9, 15, 17, 8, 16, 4, 6, 13, 1, 14, 3, 10, 12, 7 };
  uint32_t pattern[] = { 0x12345678, 0x87654321, 0x43218765, 0x56781234, 0x34127856, 0x65872143, 0x78563412, 0x21436587,
                         0x12563478, 0x87436521, 0x56127834, 0x43872165, 0x12345678, 0x87654321, 0x43218765, 0x56781234,
                         0x34127856, 0x65872143, 0x78563412, 0x21436587, 0x12563478, 0x87436521, 0x56127834, 0x43872165 };
  uint32_t desmudgedData[12 + 6];
  for (int i = 0; i < 18; i++)
  {
    desmudgedData[position[i]] = smudgedData[i] ^ pattern[i];
  }
  uint64_t *fingerPrint = (uint64_t*)desmudgedData;
  char *cpuInfo = (char*)desmudgedData;

  std::ostringstream oss;
  oss << "Volume:     " << std::setfill('0') << std::setw(16) << std::hex << fingerPrint[6];
  oss                   << std::setfill('0') << std::setw(16) << std::hex << fingerPrint[7] << std::endl;
  oss << "MACAddress: " << std::setfill('0') << std::setw(16) << std::hex << fingerPrint[8] << std::endl;
  cpuInfo[ 12*4 ] = 0;
  oss << "CPU:        " << cpuInfo << std::endl;

  return oss.str();
}