<commit_before>#include <memory>
#include <string>

#include "imports/std/optional"

#include "SignatureVerifier_CryptoAPI.hpp"
#include "api.hpp"
#include "base64.hpp"

namespace {

constexpr int SIGNATURE_VERIFIER_UNINITIALIZED = 1;
constexpr int CRYPT_ACQUIRE_CONTEXT_FAILED = 2;
constexpr int CRYPT_CREATE_HASH_FAILED = 3;
constexpr int CRYPT_HASH_DATA_FAILED = 4;
constexpr int CRYPT_VERIFY_SIGNATURE_FAILED = 5;
constexpr int CRYPT_IMPORT_KEY_FAILED = 6;
constexpr int DIGEST_VERIFY_FINAL_FAILED = 7;
constexpr int MODULUS_TOO_LARGE = 8;
constexpr int MESSAGE_TOO_LARGE = 9;
constexpr int SIGNATURE_TOO_LARGE = 10;

}

namespace cryptolens_io {

namespace v20190401 {

void
verify(basic_Error & e, HCRYPTPROV hProv, HCRYPTKEY hPubKey, std::string const& message, std::string sig)
{
  using namespace errors;
  api::main api;

  constexpr size_t DWORD_MAX = 0xFFFFFFFF;

  if (message.size() > DWORD_MAX) { e.set(api, Subsystem::SignatureVerifier, MESSAGE_TOO_LARGE); return; }
  if (sig.size()     > DWORD_MAX) { e.set(api, Subsystem::SignatureVerifier, SIGNATURE_TOO_LARGE); return; }

  // CryptoAPI assumes things are LSB or whatever, other way around from other people.
  for (size_t i = 0, j = sig.size() - 1; i < j; ++i, --j) { std::swap(sig[i], sig[j]); }

  HCRYPTHASH hHash;
  if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) {
    DWORD code = GetLastError();
    e.set(api, Subsystem::SignatureVerifier, CRYPT_CREATE_HASH_FAILED, code);
    return;
  }

  if (!CryptHashData(hHash, (const BYTE*)message.c_str(), (DWORD)message.size(), 0)) {
    DWORD code = GetLastError();
    e.set(api, Subsystem::SignatureVerifier, CRYPT_HASH_DATA_FAILED, code);
    return;
  }

  if (!CryptVerifySignature(hHash, (const BYTE*)sig.c_str(), (DWORD)sig.size(), hPubKey, NULL, 0)) {
    DWORD code = GetLastError();
    e.set(api, Subsystem::SignatureVerifier, CRYPT_VERIFY_SIGNATURE_FAILED, code);
    return;
  }
}

SignatureVerifier_CryptoAPI::SignatureVerifier_CryptoAPI(basic_Error & e) : hProv_{}, hPubKey_{} { }

void SignatureVerifier_CryptoAPI::init(basic_Error & e)
{
  if (!CryptAcquireContext(&this->hProv_, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
    DWORD code = GetLastError();
    e.set(api::main(), errors::Subsystem::SignatureVerifier, CRYPT_ACQUIRE_CONTEXT_FAILED, code);
    return;
  }
}

SignatureVerifier_CryptoAPI::~SignatureVerifier_CryptoAPI()
{
  if (hPubKey_)<commit_msg>Fix potential memory leak in SignatureVerifier_CryptoAPI<commit_after>#include <memory>
#include <string>

#include "imports/std/optional"

#include "SignatureVerifier_CryptoAPI.hpp"
#include "api.hpp"
#include "base64.hpp"

namespace {

constexpr int SIGNATURE_VERIFIER_UNINITIALIZED = 1;
constexpr int CRYPT_ACQUIRE_CONTEXT_FAILED = 2;
constexpr int CRYPT_CREATE_HASH_FAILED = 3;
constexpr int CRYPT_HASH_DATA_FAILED = 4;
constexpr int CRYPT_VERIFY_SIGNATURE_FAILED = 5;
constexpr int CRYPT_IMPORT_KEY_FAILED = 6;
constexpr int DIGEST_VERIFY_FINAL_FAILED = 7;
constexpr int MODULUS_TOO_LARGE = 8;
constexpr int MESSAGE_TOO_LARGE = 9;
constexpr int SIGNATURE_TOO_LARGE = 10;

}

namespace cryptolens_io {

namespace v20190401 {

void
verify(basic_Error & e, HCRYPTPROV hProv, HCRYPTKEY hPubKey, std::string const& message, std::string sig)
{
  using namespace errors;
  api::main api;

  constexpr size_t DWORD_MAX = 0xFFFFFFFF;

  if (message.size() > DWORD_MAX) { e.set(api, Subsystem::SignatureVerifier, MESSAGE_TOO_LARGE); return; }
  if (sig.size()     > DWORD_MAX) { e.set(api, Subsystem::SignatureVerifier, SIGNATURE_TOO_LARGE); return; }

  // CryptoAPI assumes things are LSB or whatever, other way around from other people.
  for (size_t i = 0, j = sig.size() - 1; i < j; ++i, --j) { std::swap(sig[i], sig[j]); }

  HCRYPTHASH hHash = 0;  // Initialized following https://docs.microsoft.com/sv-se/windows/win32/seccrypto/example-c-program-signing-a-hash-and-verifying-the-hash-signature
  if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) {
    DWORD code = GetLastError();
    e.set(api, Subsystem::SignatureVerifier, CRYPT_CREATE_HASH_FAILED, code);
    goto cleanup;
  }

  if (!CryptHashData(hHash, (const BYTE*)message.c_str(), (DWORD)message.size(), 0)) {
    DWORD code = GetLastError();
    e.set(api, Subsystem::SignatureVerifier, CRYPT_HASH_DATA_FAILED, code);
    goto cleanup;
  }

  if (!CryptVerifySignature(hHash, (const BYTE*)sig.c_str(), (DWORD)sig.size(), hPubKey, NULL, 0)) {
    DWORD code = GetLastError();
    e.set(api, Subsystem::SignatureVerifier, CRYPT_VERIFY_SIGNATURE_FAILED, code);
    goto cleanup;
  }

cleanup:
  if (hHash) {
    CryptDestroyHash(hHash);
  }
}

SignatureVerifier_CryptoAPI::SignatureVerifier_CryptoAPI(basic_Error & e) : hProv_{}, hPubKey_{} { }

void SignatureVerifier_CryptoAPI::init(basic_Error & e)
{
  // TODO: Move this to the constructor now that we pass in the error object
  if (!CryptAcquireContext(&this->hProv_, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
    DWORD code = GetLastError();
    e.set(api::main(), errors::Subsystem::SignatureVerifier, CRYPT_ACQUIRE_CONTEXT_FAILED, code);
    return;
  }
}

SignatureVerifier_CryptoAPI::~SignatureVerifier_CryptoAPI()
{
  if (hPubKey_)
{'fraction_non_alphanumeric': 0.12095073797201457, 'fraction_numerical': 0.012459267778416714, 'mean_word_length': 5.490049751243781, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 13, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2538211', 'n_tokens_mistral': 2030, 'n_tokens_neox': 1938, 'n_words': 405}