#ifndef __kms_h
#define __kms_h

#ifndef CONFIG
#define CONFIG "config.h"
#endif // CONFIG
#include CONFIG

#if _MSC_VER
//#include <time.h>
#else
#include <sys/time.h>
#endif // _MSC_VER
//#include <stdlib.h>
#include "types.h"
//
// REQUEST... types are actually fixed size
// RESPONSE... size may vary, defined here is max possible size
//

#define MAX_RESPONSE_SIZE 384
#define PID_BUFFER_SIZE 64
#define MAX_REQUEST_SIZE sizeof(REQUEST_V6)
#define WORKSTATION_NAME_BUFFER 64

// Constants for V6 time stamp interval
#define TIME_C1 0x00000022816889BDULL
#define TIME_C2 0x000000208CBAB5EDULL
#define TIME_C3 0x3156CD5AC628477AULL

#define VERSION_INFO union \
{ \
	DWORD Version;\
	struct { \
		WORD MinorVer; \
		WORD MajorVer; \
	} /*__packed*/; \
} /*__packed*/

// Aliases for various KMS struct members
#define IsClientVM VMInfo
#define GraceTime BindingExpiration
#define MinutesRemaingInCurrentStatus BindingExpiration
#define ID ActID
#define ApplicationID AppID
#define SkuId ActID
#define KmsId KMSID
#define ClientMachineId CMID
#define MinimumClients N_Policy
#define TimeStamp ClientTime
#define PreviousCLientMachineId CMID_prev
#define Salt IV
#define XorSalt XoredIVs
#define ActivationInterval VLActivationInterval
#define RenewalInterval VLRenewalInterval

#define MAX_CLIENTS 671

typedef struct
{
	GUID Guid[MAX_CLIENTS];
	int_fast16_t CurrentCount;
	int_fast16_t MaxCount;
	int_fast16_t CurrentPosition;
} ClientList_t, *PClientList_t;

typedef struct {
	VERSION_INFO;
	DWORD VMInfo;					// 0 = client is bare metal / 1 = client is VM
	DWORD LicenseStatus;			// 0 = Unlicensed, 1 = Licensed (Activated), 2 = OOB grace, 3 = OOT grace, 4 = NonGenuineGrace, 5 = Notification, 6 = extended grace
	DWORD BindingExpiration;		// Expiration of the current status in minutes (e.g. when KMS activation or OOB grace expires).
	GUID AppID;						// Can currently be Windows, Office2010 or Office2013 (see kms.c, table AppList).
	GUID ActID;						// Most detailed product list. One product key per ActID (see kms.c, table ExtendedProductList). Is ignored by KMS server.
	GUID KMSID;						// This is actually what the KMS server uses to grant or refuse activation (see kms.c, table BasicProductList).
	GUID CMID;						// Client machine id. Used by the KMS server for counting minimum clients.
	DWORD N_Policy;					// Minimum clients required for activation.
	FILETIME ClientTime;			// Current client time.
	GUID CMID_prev;					// previous client machine id. All zeros, if it never changed.
	WCHAR WorkstationName[64];		// Workstation name. FQDN if available, NetBIOS otherwise.
} /*__packed*/ REQUEST;

typedef struct {
	VERSION_INFO;
	DWORD PIDSize;					// Size of PIDData in bytes.
	WCHAR KmsPID[PID_BUFFER_SIZE];	// ePID (must include terminating zero)
	GUID CMID;						// Client machine id. Must be the same as in request.
	FILETIME ClientTime;			// Current client time. Must be the same as in request.
	DWORD Count;					// Current activated machines. KMS server counts up to N_Policy << 1 then stops
	DWORD VLActivationInterval;		// Time in minutes when clients should retry activation if it was unsuccessful (default 2 hours)
	DWORD VLRenewalInterval;        // Time in minutes when clients should renew KMS activation (default 7 days)
} /*__packed*/ RESPONSE;

#ifdef _DEBUG
typedef struct {
	VERSION_INFO;
	DWORD PIDSize;
	WCHAR KmsPID[49]; 				// Set this to the ePID length you want to debug
	GUID CMID;
	FILETIME ClientTime;
	DWORD Count;
	DWORD VLActivationInterval;
	DWORD VLRenewalInterval;
} __packed RESPONSE_DEBUG;
#endif


typedef struct {
	REQUEST RequestBase;			// Base request
	BYTE MAC[16];					// Aes 160 bit CMAC
} /*__packed*/ REQUEST_V4;

typedef struct {
	RESPONSE ResponseBase;			// Base response
	BYTE MAC[16];					// Aes 160 bit CMAC
} /*__packed*/ RESPONSE_V4;


typedef struct {
	VERSION_INFO;					// unencrypted version info
	BYTE IV[16];					// IV
	REQUEST RequestBase;			// Base Request
	BYTE Pad[4];					// since this struct is fixed, we use fixed PKCS pad bytes
} /*__packed*/ REQUEST_V5;

typedef REQUEST_V5 REQUEST_V6;		// v5 and v6 requests are identical

typedef struct {
	VERSION_INFO;
	BYTE IV[16];
	RESPONSE ResponseBase;
	BYTE RandomXoredIVs[16];		// If RequestIV was used for decryption: Random ^ decrypted Request IV ^ ResponseIV. If NULL IV was used for decryption: Random ^ decrypted Request IV
	BYTE Hash[32];					// SHA256 of Random used in RandomXoredIVs
	BYTE HwId[8];					// HwId from the KMS server
	BYTE XoredIVs[16];				// If RequestIV was used for decryption: decrypted Request IV ^ ResponseIV. If NULL IV was used for decryption: decrypted Request IV.
	BYTE HMAC[16];					// V6 Hmac (low 16 bytes only), see kms.c CreateV6Hmac
	//BYTE Pad[10];					// Pad is variable sized. So do not include in struct
} /*__packed*/ RESPONSE_V6;

typedef struct {					// not used except for sizeof(). Fields are the same as RESPONSE_V6
	VERSION_INFO;
	BYTE IV[16];
	RESPONSE ResponseBase;
	BYTE RandomXoredIVs[16];
	BYTE Hash[32];
} /*__packed*/ RESPONSE_V5;

#ifdef _DEBUG
typedef struct {					// Debug structure for direct casting of RPC data in debugger
	VERSION_INFO;
	BYTE IV[16];
	RESPONSE_DEBUG ResponseBase;
	BYTE RandomXoredIVs[16];
	BYTE MAC[32];
	BYTE Unknown[8];
	BYTE XorSalts[16];
	BYTE HMAC[16];
	BYTE Pad[16];
} __packed RESPONSE_V6_DEBUG;
#endif

#define V4_PRE_EPID_SIZE 	( \
								sizeof(((RESPONSE*)0)->Version) + \
								sizeof(((RESPONSE*)0)->PIDSize) \
							)

#define V4_POST_EPID_SIZE 	( \
								sizeof(((RESPONSE*)0)->CMID) + \
								sizeof(((RESPONSE*)0)->ClientTime) + \
								sizeof(((RESPONSE*)0)->Count) + \
								sizeof(((RESPONSE*)0)->VLActivationInterval) + \
								sizeof(((RESPONSE*)0)->VLRenewalInterval) \
							)

#define V6_DECRYPT_SIZE		( \
								sizeof(((REQUEST_V6*)0)->IV) + \
								sizeof(((REQUEST_V6*)0)->RequestBase) + \
								sizeof(((REQUEST_V6*)0)->Pad) \
							)

#define V6_UNENCRYPTED_SIZE	( \
								sizeof(((RESPONSE_V6*)0)->Version) + \
								sizeof(((RESPONSE_V6*)0)->IV) \
							)

#define V6_PRE_EPID_SIZE 	( \
								V6_UNENCRYPTED_SIZE + \
								sizeof(((RESPONSE*)0)->Version) + \
								sizeof(((RESPONSE*)0)->PIDSize) \
							)

#define V5_POST_EPID_SIZE 	( \
								V4_POST_EPID_SIZE + \
								sizeof(((RESPONSE_V6*)0)->RandomXoredIVs) + \
								sizeof(((RESPONSE_V6*)0)->Hash) \
							)

#define V6_POST_EPID_SIZE 	( \
								V5_POST_EPID_SIZE + \
								sizeof(((RESPONSE_V6*)0)->HwId) + \
								sizeof(((RESPONSE_V6*)0)->XoredIVs) + \
								sizeof(((RESPONSE_V6*)0)->HMAC) \
							)

#define RESPONSE_RESULT_OK ((1 << 10) - 1) //(9 bits)
typedef union
{
	DWORD mask;
	struct
	{
		BOOL HashOK : 1;
		BOOL TimeStampOK : 1;
		BOOL ClientMachineIDOK : 1;
		BOOL VersionOK : 1;
		BOOL IVsOK : 1;
		BOOL DecryptSuccess : 1;
		BOOL HmacSha256OK : 1;
		BOOL PidLengthOK : 1;
		BOOL RpcOK : 1;
		BOOL IVnotSuspicious : 1;
		BOOL reserved3 : 1;
		BOOL reserved4 : 1;
		BOOL reserved5 : 1;
		BOOL reserved6 : 1;
		uint32_t effectiveResponseSize : 9;
		uint32_t correctResponseSize : 9;
	};
} RESPONSE_RESULT;

typedef BYTE hwid_t[8];

typedef struct CsvlkData
{
	union
	{
		uint64_t EPidOffset;
		char* EPid;
	};

	uint32_t GroupId;
	uint32_t MinKeyId;
	uint32_t MaxKeyId;
	uint8_t MinActiveClients;
	uint8_t Reserved[3];

} CsvlkData_t, *PCsvlkData_t;

typedef struct VlmcsdData
{
	union
	{
		GUID Guid;
		uint8_t GuidBytes[16];
	};

	union
	{
		uint64_t NameOffset;
		char* Name;
	};

	//union
	//{
	//	uint64_t X_EPidOffset;
	//	char* X_EPid;
	//};

	uint8_t AppIndex;
	uint8_t KmsIndex;
	uint8_t ProtocolVersion;
	uint8_t NCountPolicy;
	uint8_t IsRetail;
	uint8_t IsPreview;
	uint8_t EPidIndex;
	uint8_t reserved;

} VlmcsdData_t, *PVlmcsdData_t;

typedef struct
{
	union
	{
		uint64_t Offset;
		void* Pointer;
	};
} DataPointer_t;

typedef struct VlmcsdHeader
{
	BYTE Magic[4];
	VERSION_INFO;
	uint8_t CsvlkCount;
	uint8_t Reserved[3];

	union
	{
		int32_t Counts[3];

		struct
		{
			int32_t AppItemCount;
			int32_t KmsItemCount;
			int32_t SkuItemCount;
		};
	};

	union
	{
		DataPointer_t Datapointers[3];

		struct
		{
			union
			{
				uint64_t AppItemOffset;
				PVlmcsdData_t AppItemList;
			};

			union
			{
				uint64_t KmsItemOffset;
				PVlmcsdData_t KmsItemList;
			};

			union
			{
				uint64_t SkuItemOffset;
				PVlmcsdData_t SkuItemList;
			};

			CsvlkData_t CsvlkData[1];
		};
	};

} VlmcsdHeader_t, *PVlmcsdHeader_t;

#define EPID_INDEX_WINDOWS 0
#define EPID_INDEX_OFFICE2010 1
#define EPID_INDEX_OFFICE2013 2
#define EPID_INDEX_OFFICE2016 3

typedef HRESULT(__stdcall *RequestCallback_t)(const REQUEST *const baseRequest, RESPONSE *const baseResponse, BYTE *const hwId, const char* const ipstr);

size_t CreateResponseV4(REQUEST_V4 *const Request, BYTE *const response_data, const char* const ipstr);
size_t CreateResponseV6(REQUEST_V6 *restrict Request, BYTE *const response_data, const char* const ipstr);
BYTE *CreateRequestV4(size_t *size, const REQUEST* requestBase);
BYTE *CreateRequestV6(size_t *size, const REQUEST* requestBase);
void randomPidInit();
void get16RandomBytes(void* ptr);
RESPONSE_RESULT DecryptResponseV6(RESPONSE_V6* Response_v6, int responseSize, BYTE* const response, const BYTE* const request, BYTE* hwid);
RESPONSE_RESULT DecryptResponseV4(RESPONSE_V4* Response_v4, const int responseSize, BYTE* const response, const BYTE* const request);
void getUnixTimeAsFileTime(FILETIME *const ts);
__pure int64_t fileTimeToUnixTime(const FILETIME *const ts);

#ifndef IS_LIBRARY
int32_t getProductIndex(const GUID* guid, const PVlmcsdData_t list, const int32_t count, char** name, char** ePid);
#endif // IS_LIBRARY

#ifndef NO_STRICT_MODES
void InitializeClientLists();
void CleanUpClientLists();
#endif // !NO_STRICT_MODES

extern RequestCallback_t CreateResponseBase;

#ifdef _PEDANTIC
uint16_t IsValidLcid(const uint16_t Lcid);
#endif // _PEDANTIC

#endif // __kms_h
