/*	Benjamin DELPY `gentilkiwi`
	https://blog.gentilkiwi.com
	benjamin@gentilkiwi.com
	Licence : https://creativecommons.org/licenses/by/4.0/
*/
#include "kull_m_key.h"

PKULL_M_KEY_CAPI_BLOB kull_m_key_capi_create(PVOID data/*, DWORD size*/)
{
	PKULL_M_KEY_CAPI_BLOB capiKey = NULL;
	if(capiKey = (PKULL_M_KEY_CAPI_BLOB) LocalAlloc(LPTR, sizeof(KULL_M_KEY_CAPI_BLOB)))
	{
		RtlCopyMemory(capiKey, data, FIELD_OFFSET(KULL_M_KEY_CAPI_BLOB, pName));
		capiKey->pName = (PSTR) ((PBYTE) data + FIELD_OFFSET(KULL_M_KEY_CAPI_BLOB, pName));
		capiKey->pHash = (PBYTE) capiKey->pName + capiKey->dwNameLen;
		capiKey->pSiPublicKey = (PBYTE) capiKey->pHash + capiKey->dwHashLen;
		capiKey->pSiPrivateKey = (PBYTE) capiKey->pSiPublicKey + capiKey->dwSiPublicKeyLen;
		capiKey->pSiExportFlag = (PBYTE) capiKey->pSiPrivateKey + capiKey->dwSiPrivateKeyLen;
		capiKey->pExPublicKey = (PBYTE) capiKey->pSiExportFlag + capiKey->dwSiExportFlagLen;
		capiKey->pExPrivateKey = (PBYTE) capiKey->pExPublicKey + capiKey->dwExPublicKeyLen;
		capiKey->pExExportFlag = (PBYTE) capiKey->pExPrivateKey + capiKey->dwExPrivateKeyLen;

		kull_m_string_ptr_replace(&capiKey->pName, capiKey->dwNameLen);
		kull_m_string_ptr_replace(&capiKey->pHash, capiKey->dwHashLen);
		kull_m_string_ptr_replace(&capiKey->pSiPublicKey, capiKey->dwSiPublicKeyLen);
		kull_m_string_ptr_replace(&capiKey->pSiPrivateKey, capiKey->dwSiPrivateKeyLen);
		kull_m_string_ptr_replace(&capiKey->pSiExportFlag, capiKey->dwSiExportFlagLen);
		kull_m_string_ptr_replace(&capiKey->pExPublicKey, capiKey->dwExPublicKeyLen);
		kull_m_string_ptr_replace(&capiKey->pExPrivateKey, capiKey->dwExPrivateKeyLen);
		kull_m_string_ptr_replace(&capiKey->pExExportFlag, capiKey->dwExExportFlagLen);
	}
	return capiKey;
}

void kull_m_key_capi_delete(PKULL_M_KEY_CAPI_BLOB capiKey)
{
	if(capiKey)
	{
		if(capiKey->pName)
			LocalFree(capiKey->pName);
		if(capiKey->pHash)
			LocalFree(capiKey->pHash);
		if(capiKey->pSiPublicKey)
			LocalFree(capiKey->pSiPublicKey);
		if(capiKey->pSiPrivateKey)
			LocalFree(capiKey->pSiPrivateKey);
		if(capiKey->pSiExportFlag)
			LocalFree(capiKey->pSiExportFlag);
		if(capiKey->pExPublicKey)
			LocalFree(capiKey->pExPublicKey);
		if(capiKey->pExPrivateKey)
			LocalFree(capiKey->pExPrivateKey);
		if(capiKey->pExExportFlag)
			LocalFree(capiKey->pExExportFlag);
		LocalFree(capiKey);
	}
}

void kull_m_key_capi_descr(DWORD level, PKULL_M_KEY_CAPI_BLOB capiKey)
{
	kprintf(L"%*s" L"**KEY (capi)**\n", level << 1, L"");
	if(capiKey)
	{
		kprintf(L"%*s" L"  dwVersion          : %08x - %u\n", level << 1, L"", capiKey->dwVersion, capiKey->dwVersion);
		kprintf(L"%*s" L"  dwUniqueNameLen    : %08x - %u\n", level << 1, L"", capiKey->dwNameLen, capiKey->dwNameLen);
		kprintf(L"%*s" L"  dwSiPublicKeyLen   : %08x - %u\n", level << 1, L"", capiKey->dwSiPublicKeyLen, capiKey->dwSiPublicKeyLen);
		kprintf(L"%*s" L"  dwSiPrivateKeyLen  : %08x - %u\n", level << 1, L"", capiKey->dwSiPrivateKeyLen, capiKey->dwSiPrivateKeyLen);
		kprintf(L"%*s" L"  dwExPublicKeyLen   : %08x - %u\n", level << 1, L"", capiKey->dwExPublicKeyLen, capiKey->dwExPublicKeyLen);
		kprintf(L"%*s" L"  dwExPrivateKeyLen  : %08x - %u\n", level << 1, L"", capiKey->dwExPrivateKeyLen, capiKey->dwExPrivateKeyLen);
		kprintf(L"%*s" L"  dwHashLen          : %08x - %u\n", level << 1, L"", capiKey->dwHashLen, capiKey->dwHashLen);
		kprintf(L"%*s" L"  dwSiExportFlagLen  : %08x - %u\n", level << 1, L"", capiKey->dwSiExportFlagLen, capiKey->dwSiExportFlagLen);
		kprintf(L"%*s" L"  dwExExportFlagLen  : %08x - %u\n", level << 1, L"", capiKey->dwExExportFlagLen, capiKey->dwExExportFlagLen);


		kprintf(L"%*s" L"  pUniqueName        : ", level << 1, L""); kprintf(L"%S\n", capiKey->pName);
		kprintf(L"%*s" L"  pHash              : ", level << 1, L""); kull_m_string_wprintf_hex(capiKey->pHash, capiKey->dwHashLen, 0); kprintf(L"\n");
		kprintf(L"%*s" L"  pSiPublicKey       : ", level << 1, L""); kull_m_string_wprintf_hex(capiKey->pSiPublicKey, capiKey->dwSiPublicKeyLen, 0); kprintf(L"\n");
		kprintf(L"%*s" L"  pSiPrivateKey      :\n", level << 1, L"");
		if(capiKey->pSiPrivateKey && capiKey->dwSiPrivateKeyLen)
			kull_m_dpapi_blob_quick_descr(level + 1, capiKey->pSiPrivateKey); /*kull_m_string_wprintf_hex(capiKey->pPrivateKey, capiKey->dwPrivateKeyLen, 0);*/
		kprintf(L"%*s" L"  pSiExportFlag      :\n", level << 1, L"");
		if(capiKey->pSiExportFlag && capiKey->dwSiExportFlagLen)
			kull_m_dpapi_blob_quick_descr(level + 1, capiKey->pSiExportFlag); /*kull_m_string_wprintf_hex(capiKey->pExportFlag, capiKey->dwExportFlagLen, 0);*/
		kprintf(L"%*s" L"  pExPublicKey       : ", level << 1, L""); kull_m_string_wprintf_hex(capiKey->pExPublicKey, capiKey->dwExPublicKeyLen, 0); kprintf(L"\n");
		kprintf(L"%*s" L"  pExPrivateKey      :\n", level << 1, L"");
		if(capiKey->pExPrivateKey && capiKey->dwExPrivateKeyLen)
			kull_m_dpapi_blob_quick_descr(level + 1, capiKey->pExPrivateKey); /*kull_m_string_wprintf_hex(capiKey->pPrivateKey, capiKey->dwPrivateKeyLen, 0);*/
		kprintf(L"%*s" L"  pExExportFlag      :\n", level << 1, L"");
		if(capiKey->pExExportFlag && capiKey->dwExExportFlagLen)
			kull_m_dpapi_blob_quick_descr(level + 1, capiKey->pExExportFlag); /*kull_m_string_wprintf_hex(capiKey->pExportFlag, capiKey->dwExportFlagLen, 0);*/

	}
}

BOOL kull_m_key_capi_write(PKULL_M_KEY_CAPI_BLOB capiKey, PVOID *data, DWORD *size)
{
	BOOL status = FALSE;
	PBYTE ptr;
	*size = FIELD_OFFSET(KULL_M_KEY_CAPI_BLOB, pName) + capiKey->dwNameLen + capiKey->dwHashLen + capiKey->dwSiPublicKeyLen + capiKey->dwSiPrivateKeyLen + capiKey->dwSiExportFlagLen + capiKey->dwExPublicKeyLen + capiKey->dwExPrivateKeyLen + capiKey->dwExExportFlagLen;
	if(*data = LocalAlloc(LPTR, *size))
	{
		ptr = (PBYTE) *data;
		RtlCopyMemory(ptr, capiKey, FIELD_OFFSET(KULL_M_KEY_CAPI_BLOB, pName));
		ptr += FIELD_OFFSET(KULL_M_KEY_CAPI_BLOB, pName);
		RtlCopyMemory(ptr, capiKey->pName, capiKey->dwNameLen);
		ptr += capiKey->dwNameLen;
		RtlCopyMemory(ptr, capiKey->pHash, capiKey->dwHashLen);
		ptr += capiKey->dwHashLen;
		RtlCopyMemory(ptr, capiKey->pSiPublicKey, capiKey->dwSiPublicKeyLen);
		ptr += capiKey->dwSiPublicKeyLen;
		RtlCopyMemory(ptr, capiKey->pSiPrivateKey, capiKey->dwSiPrivateKeyLen);
		ptr += capiKey->dwSiPrivateKeyLen;
		RtlCopyMemory(ptr, capiKey->pSiExportFlag, capiKey->dwSiExportFlagLen);
		ptr += capiKey->dwSiExportFlagLen;
		RtlCopyMemory(ptr, capiKey->pExPublicKey, capiKey->dwExPublicKeyLen);
		ptr += capiKey->dwExPublicKeyLen;
		RtlCopyMemory(ptr, capiKey->pExPrivateKey, capiKey->dwExPrivateKeyLen);
		ptr += capiKey->dwExPrivateKeyLen;
		RtlCopyMemory(ptr, capiKey->pExExportFlag, capiKey->dwExExportFlagLen);
		status = TRUE;
	}
	return status;
}

BOOL kull_m_key_capi_decryptedkey_to_raw(LPCVOID publickey, DWORD publickeyLen, LPCVOID decrypted, DWORD decryptedLen, ALG_ID keyAlg, PRSA_GENERICKEY_BLOB *blob, DWORD *blobLen, DWORD *dwProviderType)
{
	BOOL status = FALSE;
	DWORD keyLen;
	PBYTE ptrDestination, ptrSource;

	if(((PDWORD) decrypted)[0] == '2ASR')
	{
		keyLen = ((PDWORD) decrypted)[2];
		*blobLen = sizeof(RSA_GENERICKEY_BLOB) + ((keyLen * 9) / 16);
		if(*blob = (PRSA_GENERICKEY_BLOB) LocalAlloc(LPTR, *blobLen))
		{
			status = TRUE;
			(*blob)->Header.bType = PRIVATEKEYBLOB;
			(*blob)->Header.bVersion = CUR_BLOB_VERSION;
			(*blob)->Header.reserved = 0;
			(*blob)->Header.aiKeyAlg = keyAlg;

			(*blob)->RsaKey.magic = ((PDWORD) decrypted)[0];
			(*blob)->RsaKey.bitlen = keyLen;
			(*blob)->RsaKey.pubexp = ((PDWORD) decrypted)[4];

			ptrDestination = ((PBYTE) (*blob)) + sizeof(RSA_GENERICKEY_BLOB);
			ptrSource = (PBYTE) ((PDWORD) decrypted + 5);

			RtlCopyMemory(ptrDestination, ptrSource, keyLen / 8);
			ptrDestination += keyLen / 8;
			ptrSource += (keyLen / 8) + 8;
			RtlCopyMemory(ptrDestination, ptrSource, keyLen / 16);
			ptrDestination += keyLen / 16;
			ptrSource += (keyLen / 16) + 4;
			RtlCopyMemory(ptrDestination, ptrSource, keyLen / 16);
			ptrDestination += keyLen / 16;
			ptrSource += (keyLen / 16) + 4;
			RtlCopyMemory(ptrDestination, ptrSource, keyLen / 16);
			ptrDestination += keyLen / 16;
			ptrSource += (keyLen / 16) + 4;
			RtlCopyMemory(ptrDestination, ptrSource, keyLen / 16);
			ptrDestination += keyLen / 16;
			ptrSource += (keyLen / 16) + 4;
			RtlCopyMemory(ptrDestination, ptrSource, keyLen / 16);
			ptrDestination += keyLen / 16;
			ptrSource += (keyLen / 16) + 4;
			RtlCopyMemory(ptrDestination, ptrSource, keyLen / 8);

			*dwProviderType = PROV_RSA_FULL;
		}
	}
	else
	{
		if(publickey && publickeyLen)
		{
			if((((PDSS_GENERICKEY3_BLOB) publickey)->Header.bType == PRIVATEKEYBLOB) && (((PDSS_GENERICKEY3_BLOB) publickey)->Header.bVersion == (CUR_BLOB_VERSION + 1)) && (((PDSS_GENERICKEY3_BLOB) publickey)->DsaKey.magic == '4SSD'))
			{
				*blobLen = publickeyLen + decryptedLen;
				if(*blob = (PRSA_GENERICKEY_BLOB) LocalAlloc(LPTR, *blobLen))
				{
					status = TRUE;
					RtlCopyMemory(*blob, publickey, publickeyLen);
					RtlCopyMemory(((PBYTE) *blob) + publickeyLen, decrypted, decryptedLen);
					((PDSS_GENERICKEY3_BLOB) *blob)->DsaKey.bitlenX = decryptedLen * 8;
					*dwProviderType = PROV_DSS;
				}
			}
		}
	}
	return status;
}

PKULL_M_KEY_CNG_BLOB kull_m_key_cng_create(PVOID data/*, DWORD size*/)
{
	PKULL_M_KEY_CNG_BLOB cngKey = NULL;

	if(cngKey = (PKULL_M_KEY_CNG_BLOB) LocalAlloc(LPTR, sizeof(KULL_M_KEY_CNG_BLOB)))
	{
		RtlCopyMemory(cngKey, data, FIELD_OFFSET(KULL_M_KEY_CNG_BLOB, pName));
		cngKey->pName = (PSTR) ((PBYTE) data + FIELD_OFFSET(KULL_M_KEY_CNG_BLOB, pName));
		if(!kull_m_key_cng_properties_create((PBYTE) cngKey->pName + cngKey->dwNameLen, cngKey->dwPublicPropertiesLen, &cngKey->pPublicProperties, &cngKey->cbPublicProperties))
			PRINT_ERROR(L"kull_m_key_cng_properties_create (public)\n");
		cngKey->pPrivateProperties = (PBYTE) cngKey->pName + cngKey->dwNameLen + cngKey->dwPublicPropertiesLen;
		cngKey->pPrivateKey = (PBYTE) cngKey->pPrivateProperties + cngKey->dwPrivatePropertiesLen;
		kull_m_string_ptr_replace(&cngKey->pName, cngKey->dwNameLen);
		kull_m_string_ptr_replace(&cngKey->pPrivateProperties, cngKey->dwPrivatePropertiesLen);
		kull_m_string_ptr_replace(&cngKey->pPrivateKey, cngKey->dwPrivateKeyLen);
	}
	return cngKey;
}

void kull_m_key_cng_delete(PKULL_M_KEY_CNG_BLOB cngKey)
{
	if(cngKey)
	{
		if(cngKey->pName)
			LocalFree(cngKey->pName);
		if(cngKey->cbPublicProperties && cngKey->pPublicProperties)
			kull_m_key_cng_properties_delete(cngKey->pPublicProperties, cngKey->cbPublicProperties);
		if(cngKey->pPrivateProperties)
			LocalFree(cngKey->pPrivateProperties);
		if(cngKey->pPrivateKey)
			LocalFree(cngKey->pPrivateKey);
		LocalFree(cngKey);
	}
}

void kull_m_key_cng_descr(DWORD level, PKULL_M_KEY_CNG_BLOB cngKey)
{
	kprintf(L"%*s" L"**KEY (cng)**\n", level << 1, L"");
	if(cngKey)
	{
		kprintf(L"%*s" L"  dwVersion             : %08x - %u\n", level << 1, L"", cngKey->dwVersion, cngKey->dwVersion);
		kprintf(L"%*s" L"  unk                   : %08x - %u\n", level << 1, L"", cngKey->unk, cngKey->unk);
		kprintf(L"%*s" L"  dwNameLen             : %08x - %u\n", level << 1, L"", cngKey->dwNameLen, cngKey->dwNameLen);
		kprintf(L"%*s" L"  type                  : %08x - %u\n", level << 1, L"", cngKey->type, cngKey->type);
		kprintf(L"%*s" L"  dwPublicPropertiesLen : %08x - %u\n", level << 1, L"", cngKey->dwPublicPropertiesLen, cngKey->dwPublicPropertiesLen);
		kprintf(L"%*s" L"  dwPrivatePropertiesLen: %08x - %u\n", level << 1, L"", cngKey->dwPrivatePropertiesLen, cngKey->dwPrivatePropertiesLen);
		kprintf(L"%*s" L"  dwPrivateKeyLen       : %08x - %u\n", level << 1, L"", cngKey->dwPrivateKeyLen, cngKey->dwPrivateKeyLen);
		kprintf(L"%*s" L"  unkArray[16]          : ", level << 1, L""); kull_m_string_wprintf_hex(cngKey->unkArray, sizeof(cngKey->unkArray), 0); kprintf(L"\n");
		kprintf(L"%*s" L"  pName                 : ", level << 1, L""); kprintf(L"%.*s\n", cngKey->dwNameLen / sizeof(wchar_t), cngKey->pName);
		kprintf(L"%*s" L"  pPublicProperties     : ", level << 1, L""); kull_m_key_cng_properties_descr(level + 1, cngKey->pPublicProperties, cngKey->cbPublicProperties);
		kprintf(L"%*s" L"  pPrivateProperties    :\n", level << 1, L"");
		if(cngKey->pPrivateProperties && cngKey->dwPrivatePropertiesLen)
			kull_m_dpapi_blob_quick_descr(level + 1, cngKey->pPrivateProperties); /*kull_m_string_wprintf_hex(cngKey->pPrivateProperties, cngKey->dwPrivatePropertiesLen, 0);*/
		kprintf(L"%*s" L"  pPrivateKey           :\n", level << 1, L"");
		if(cngKey->pPrivateKey && cngKey->dwPrivateKeyLen)
			kull_m_dpapi_blob_quick_descr(level + 1, cngKey->pPrivateKey); /*kull_m_string_wprintf_hex(cngKey->pPrivateKey, cngKey->dwPrivateKeyLen, 0);*/
	}
}

PKULL_M_KEY_CNG_PROPERTY kull_m_key_cng_property_create(PVOID data/*, DWORD size*/)
{
	PKULL_M_KEY_CNG_PROPERTY cngProperty = NULL;
	if(cngProperty = (PKULL_M_KEY_CNG_PROPERTY) LocalAlloc(LPTR, sizeof(KULL_M_KEY_CNG_PROPERTY)))
	{
		RtlCopyMemory(cngProperty, data, FIELD_OFFSET(KULL_M_KEY_CNG_PROPERTY, pName));
		cngProperty->pName = (PSTR) ((PBYTE) data + FIELD_OFFSET(KULL_M_KEY_CNG_PROPERTY, pName));
		cngProperty->pProperty = (PBYTE) cngProperty->pName + cngProperty->dwNameLen;
		kull_m_string_ptr_replace(&cngProperty->pName, cngProperty->dwNameLen);
		kull_m_string_ptr_replace(&cngProperty->pProperty, cngProperty->dwPropertyLen);
	}
	return cngProperty;
}

void kull_m_key_cng_property_delete(PKULL_M_KEY_CNG_PROPERTY property)
{
	if(property)
	{
		if(property->pName)
			LocalFree(property->pName);
		if(property->pProperty)
			LocalFree(property->pProperty);
		LocalFree(property);
	}
}

void kull_m_key_cng_property_descr(DWORD level, PKULL_M_KEY_CNG_PROPERTY property)
{
	kprintf(L"%*s" L"**KEY CNG PROPERTY**\n", level << 1, L"");
	if(property)
	{
		kprintf(L"%*s" L"  dwStructLen     : %08x - %u\n", level << 1, L"", property->dwStructLen, property->dwStructLen);
		kprintf(L"%*s" L"  type            : %08x - %u\n", level << 1, L"", property->type, property->type);
		kprintf(L"%*s" L"  unk             : %08x - %u\n", level << 1, L"", property->unk, property->unk);
		kprintf(L"%*s" L"  dwNameLen       : %08x - %u\n", level << 1, L"", property->dwNameLen, property->dwNameLen);
		kprintf(L"%*s" L"  dwPropertyLen   : %08x - %u\n", level << 1, L"", property->dwPropertyLen, property->dwPropertyLen);
		kprintf(L"%*s" L"  pName           : ", level << 1, L""); kprintf(L"%.*s\n", property->dwNameLen / sizeof(wchar_t), property->pName);
		kprintf(L"%*s" L"  pProperty       : ", level << 1, L""); kull_m_string_wprintf_hex(property->pProperty, property->dwPropertyLen, 0); kprintf(L"\n\n");
	}
}

BOOL kull_m_key_cng_properties_create(PVOID data, DWORD size, PKULL_M_KEY_CNG_PROPERTY **properties, DWORD *count)
{
	BOOL status = FALSE;
	DWORD i, j;

	for(i = 0, *count = 0; i < size; i += ((PKULL_M_KEY_CNG_PROPERTY) ((PBYTE) data + i))->dwStructLen, (*count)++);

	if((*properties) = (PKULL_M_KEY_CNG_PROPERTY *) LocalAlloc(LPTR, *count * sizeof(PKULL_M_KEY_CNG_PROPERTY)))
	{
		for(i = 0, j = 0, status = TRUE; (i < (*count)) && status; i++)
		{
			if((*properties)[i] = kull_m_key_cng_property_create((PBYTE) data + j))
				j +=  (*properties)[i]->dwStructLen;
			else status = FALSE;
		}
	}
	if(!status)
	{
		kull_m_key_cng_properties_delete(*properties, *count);
		*properties = NULL;
		*count = 0;
	}
	return status;
}

void kull_m_key_cng_properties_delete(PKULL_M_KEY_CNG_PROPERTY *properties, DWORD count)
{
	DWORD i;
	if(properties)
	{
		for(i = 0; i < count; i++)
			kull_m_key_cng_property_delete(properties[i]);
		LocalFree(properties);
	}
}

void kull_m_key_cng_properties_descr(DWORD level, PKULL_M_KEY_CNG_PROPERTY *properties, DWORD count)
{
	DWORD i;
	if(count && properties)
	{
		kprintf(L"%u field(s)\n", count);
		for(i = 0; i < count; i++)
			kull_m_key_cng_property_descr(level, properties[i]);
	}
}