#include "rsa.h"
#include "../../checksum/lib/crc.h"
#include <stdio.h>
#include <windows.h>

BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpvReserved) {
  switch (fdwReason) {

  case DLL_PROCESS_ATTACH:
    fprintf(stderr,
            "DLL loaded into process. hinstDLL = %p, lpvReserved = %p\n",
            hInstDLL, lpvReserved);

    {
      fprintf(stderr, "Info: Checking integrity of rsa.dll.\n");

      char dll_path[MAX_PATH] = {0};
      if (GetModuleFileNameA(hInstDLL, dll_path, MAX_PATH) == 0) {
        fprintf(stderr, "Error: Can't get path to rsa.dll file.\n");

        return FALSE;
      }

      if (!crc_verify(dll_path)) {
        fprintf(stderr, "Error: Can't verify rsa.dll integrity.\n");

        return FALSE;
      } else {
        fprintf(stderr, "Info: Integrity of rsa.dll is verified.\n");
      }
    }
    break;

  case DLL_THREAD_ATTACH:
    fprintf(stderr,
            "New thread created in the process using the DLL. "
            "hinstDLL = %p, lpvReserved = %p\n",
            hInstDLL, lpvReserved);

    return FALSE;
    break;

  case DLL_THREAD_DETACH:
    fprintf(stderr,
            "Thread using the DLL is terminating. hinstDLL = %p, "
            "lpvReserved = %p\n",
            hInstDLL, lpvReserved);

    return FALSE;
    break;

  case DLL_PROCESS_DETACH:
    fprintf(stderr,
            "DLL unloaded from process. hinstDLL = %p, lpvReserved = "
            "%p\n",
            hInstDLL, lpvReserved);

    return FALSE;
    break;
  }

  return TRUE;
}

__int128_t mul_mod(__int128_t a, __int128_t b, __int128_t mod) {
  __int128_t res = 0;
  a %= mod;

  while (b > 0) {
    if (b & 1)
      res = (res + a) % mod;

    a = (a << 1) % mod;
    b >>= 1;
  }

  return res;
}

__int128_t pow_mod(__int128_t base, __int128_t exp, __int128_t mod) {
  __int128_t res = 1;
  base %= mod;

  while (exp > 0) {
    if (exp & 1)
      res = mul_mod(res, base, mod);

    base = mul_mod(base, base, mod);
    exp >>= 1;
  }

  return res;
}

__int128_t mod_inverse(__int128_t e, __int128_t phi) {
  __int128_t a = e, b = phi;
  __int128_t x = 1, y = 0;
  __int128_t x1 = 0, y1 = 1;
  __int128_t q, temp;

  while (b != 0) {
    q = a / b;

    temp = a % b;
    a = b;
    b = temp;

    temp = x - q * x1;
    x = x1;
    x1 = temp;

    temp = y - q * y1;
    y = y1;
    y1 = temp;
  }

  if (x < 0)
    x += phi;

  return x;
}

rsa_Pair rsa_gen_pair(__int128_t p, __int128_t q, __int128_t e) {
  rsa_Pair pair;
  pair.e = e;
  pair.n = p * q;

  __int128_t phi = (p - 1) * (q - 1);
  pair.d = mod_inverse(e, phi);

  return pair;
}

__int128_t rsa_encrypt(__int128_t data, __int128_t e, __int128_t n) {
  return pow_mod(data, e, n);
}

__int128_t rsa_decrypt(__int128_t data, __int128_t d, __int128_t n) {
  return pow_mod(data, d, n);
}
