gcontini
2019-11-30 492558c6231e7738f35f140005df5bb31a0af9bc
src/library/os/windows/signature_verifier.cpp
@@ -5,16 +5,249 @@
 *      Author: devel
 */
#include "verifier.hpp"
#include "../os.h"
#include <stdio.h>
#include <sstream>
#include <iostream>
#include <fstream>
#include <vector>
#include <bcrypt.h>
#include <wincrypt.h>
#include <iphlpapi.h>
#include <windows.h>
#pragma comment(lib, "bcrypt.lib")
#include <public_key.h>
#include "../../base/logger.h"
#include "../../base/base64.h"
#include "../signature_verifier.h"
#define RSA_KEY_BITLEN 1024
namespace license {
using namespace std;
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
verifier::verifier() {
   // TODO Auto-generated constructor stub
static const void formatError(DWORD status, const char* description) {
   char msgBuffer[256];
   FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, status, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), &msgBuffer[0],
              sizeof(msgBuffer) - 1, nullptr);
   LOG_DEBUG("error %s : %s %h", description, msgBuffer, status);
}
verifier::~verifier() {
   // TODO Auto-generated destructor stub
#pragma pack(push, 1)
typedef struct {
   BCRYPT_RSAKEY_BLOB rsakey;
   BYTE pkExp[3];
   BYTE modulus[RSA_KEY_BITLEN / 8];
} PUBKEY_BLOB, *P_PUBKEY_BLOB;
#pragma pack(pop)
static BCRYPT_ALG_HANDLE openSignatureProvider() {
   DWORD status;
   BCRYPT_ALG_HANDLE hSignAlg = nullptr;
   return hSignAlg;
}
static BCRYPT_ALG_HANDLE openHashProvider() {
   DWORD status;
   BCRYPT_ALG_HANDLE hHashAlg = nullptr;
   if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(&hHashAlg, BCRYPT_SHA256_ALGORITHM, NULL, 0))) {
      throw logic_error("Error opening hash provider");
   }
   return hHashAlg;
}
static DWORD hashData(BCRYPT_HASH_HANDLE& hHash, const string& data, PBYTE pbHash, DWORD hashDataLenght) {
   DWORD status;
   bool success = false;
   if (NT_SUCCESS(status = BCryptHashData(hHash, (BYTE*)data.c_str(), (ULONG)data.length(), 0))) {
      status = BCryptFinishHash(hHash, pbHash, hashDataLenght, 0);
   }
   return status;
}
static size_t read_length(uint8_t*& ptr) {
   uint8_t len = *ptr++;
   size_t result = 0;
   cout << (len & 0x80) << endl;
   if ((len & 0x80) > 0) {
      size_t blen = len & 0x7F;
      for (int i = 0; i < blen; i++) {
         result += (*(ptr++) << (i * 8));
      }
   } else {
      result = len;
   }
   return result;
}
static FUNCTION_RETURN read_sequence(uint8_t*& ptr) {
   uint8_t tag = *ptr++;
   if (tag != 0x30) {
      return FUNC_RET_ERROR;
   }
   read_length(ptr);
   return FUNC_RET_OK;
}
static FUNCTION_RETURN read_integer(uint8_t*& ptr, BYTE* location, const size_t expected_length) {
   uint8_t tag = *ptr++;
   if (tag != 0x02) {
      return FUNC_RET_ERROR;
   }
   size_t length = read_length(ptr);
   // skip the padding byte
   if (*ptr == 0) {
      length--;
      ptr++;
   }
   if (expected_length < length) {
      return FUNC_RET_ERROR;
   }
   for (int i = 0; i < length; i++) {
      location[i] = *(ptr++);
   }
   return FUNC_RET_OK;
}
static FUNCTION_RETURN readPublicKey(const BCRYPT_ALG_HANDLE sig_alg, BCRYPT_KEY_HANDLE* hKey) {
   FUNCTION_RETURN result = FUNC_RET_ERROR;
   DWORD status;
   PUBKEY_BLOB pubk;
   pubk.rsakey.Magic = BCRYPT_RSAPUBLIC_MAGIC;
   pubk.rsakey.BitLength = RSA_KEY_BITLEN;
   pubk.rsakey.cbPublicExp = 3;
   pubk.rsakey.cbModulus = RSA_KEY_BITLEN / 8;
   pubk.rsakey.cbPrime1 = 0;
   pubk.rsakey.cbPrime2 = 0;
   uint8_t pubKey[] = PUBLIC_KEY;
   uint8_t* pub_key_idx = &pubKey[0];
   read_sequence(pub_key_idx);
   read_integer(pub_key_idx, (BYTE*)&pubk.modulus, sizeof(pubk.modulus));
   read_integer(pub_key_idx, (BYTE*)&pubk.pkExp, sizeof(pubk.pkExp));
   if (NT_SUCCESS(status = BCryptImportKeyPair(sig_alg, nullptr, BCRYPT_RSAPUBLIC_BLOB, hKey, (PUCHAR)&pubk,
                                    sizeof(pubk), 0))) {
      result = FUNC_RET_OK;
   } else {
#ifdef _DEBUG
      formatError(status, "error importing public key");
#endif
   }
   return result;
}
static FUNCTION_RETURN verifyHash(const PBYTE pbHash, const DWORD hashDataLenght, const string& signatureBuffer) {
   BCRYPT_KEY_HANDLE phKey = nullptr;
   DWORD status;
   FUNCTION_RETURN result = FUNC_RET_ERROR;
   PBYTE pbSignature = nullptr;
   DWORD dwSigLen;
   BYTE* sigBlob = nullptr;
   BCRYPT_ALG_HANDLE hSignAlg = nullptr;
   // FIXME!!
   sigBlob = unbase64(signatureBuffer.c_str(), (int)signatureBuffer.size(), (int*)&dwSigLen);
   if (NT_SUCCESS(status = BCryptOpenAlgorithmProvider(&hSignAlg, BCRYPT_RSA_ALGORITHM, NULL, 0))) {
      if ((result = readPublicKey(hSignAlg, &phKey)) == FUNC_RET_OK) {
         BCRYPT_PKCS1_PADDING_INFO paddingInfo;
         ZeroMemory(&paddingInfo, sizeof(paddingInfo));
         paddingInfo.pszAlgId = BCRYPT_SHA256_ALGORITHM;
         if (NT_SUCCESS(status = BCryptVerifySignature(phKey, &paddingInfo, pbHash, hashDataLenght, sigBlob,
                                            dwSigLen, BCRYPT_PAD_PKCS1))) {
            result = FUNC_RET_OK;
         } else {
            result = FUNC_RET_ERROR;
#ifdef _DEBUG
            formatError(status, "error verifying signature");
#endif
         }
      } else {
         LOG_DEBUG("Error reading public key");
      }
   } else {
      result = FUNC_RET_NOT_AVAIL;
#ifdef _DEBUG
      formatError(status, "error opening RSA provider");
#endif
   }
   if (phKey != nullptr) {
      BCryptDestroyKey(phKey);
   }
   if (hSignAlg != nullptr) {
      BCryptCloseAlgorithmProvider(hSignAlg, 0);
   }
   if (sigBlob) {
      free(sigBlob);
   }
   return result;
}
FUNCTION_RETURN verify_signature(const std::string& stringToVerify, const std::string& signatureB64) {
   BCRYPT_HASH_HANDLE hHash = nullptr;
   PBYTE pbHashObject = nullptr, pbHashData = nullptr;
   FUNCTION_RETURN result = FUNC_RET_ERROR;
   const HANDLE hProcessHeap = GetProcessHeap();
   // BCRYPT_ALG_HANDLE sig_alg = openSignatureProvider();
   BCRYPT_ALG_HANDLE hash_alg = openHashProvider();
   DWORD status;
   // calculate the size of the buffer to hold the hash object
   DWORD cbData = 0, cbHashObject = 0;
   // and the size to keep the hashed data
   DWORD cbHashDataLenght = 0;
   if (NT_SUCCESS(status = BCryptGetProperty(hash_alg, BCRYPT_OBJECT_LENGTH, (PBYTE)&cbHashObject, sizeof(DWORD),
                                   &cbData, 0)) &&
      NT_SUCCESS(status = BCryptGetProperty(hash_alg, BCRYPT_HASH_LENGTH, (PBYTE)&cbHashDataLenght, sizeof(DWORD),
                                   &cbData, 0))) {
      // allocate the hash object on the heap
      pbHashObject = (PBYTE)HeapAlloc(hProcessHeap, 0, cbHashObject);
      pbHashData = (PBYTE)HeapAlloc(hProcessHeap, 0, cbHashDataLenght);
      if (NULL != pbHashObject && nullptr != pbHashData) {
         if (NT_SUCCESS(status = BCryptCreateHash(hash_alg, &hHash, pbHashObject, cbHashObject, NULL, 0, 0))) {
            if (NT_SUCCESS(status = hashData(hHash, stringToVerify, pbHashData, cbHashDataLenght))) {
               result = verifyHash(pbHashData, cbHashDataLenght, signatureB64);
            } else {
               result = FUNC_RET_NOT_AVAIL;
#ifdef _DEBUG
               formatError(status, "error hashing data");
#endif
            }
         } else {
            result = FUNC_RET_NOT_AVAIL;
#ifdef _DEBUG
            formatError(status, "error creating hash");
#endif
         }
      } else {
         result = FUNC_RET_BUFFER_TOO_SMALL;
         LOG_DEBUG("Error allocating memory");
      }
   } else {
      result = FUNC_RET_NOT_AVAIL;
#ifdef _DEBUG
      formatError(status, "**** Error returned by BCryptGetProperty");
#endif
   }
   if (hHash) {
      BCryptDestroyHash(hHash);
   }
   if (pbHashObject) {
      HeapFree(hProcessHeap, 0, pbHashObject);
   }
   if (pbHashData) {
      HeapFree(hProcessHeap, 0, pbHashData);
   }
   if (hash_alg != nullptr) {
      BCryptCloseAlgorithmProvider(hash_alg, 0);
   }
   return result;
}
} /* namespace license */