#define MODULE_LOG_PREFIX "emu"

#include "globals.h"

#ifdef WITH_EMU

#include "cscrypt/des.h"
#include "module-emulator-osemu.h"
#include "module-newcamd-des.h"
#include "oscam-aes.h"
#include "oscam-string.h"

// from reader-viaccess.c:
void hdSurEncPhase1_D2_0F_11(uint8_t *CWs);
void hdSurEncPhase2_D2_0F_11(uint8_t *CWs);
void hdSurEncPhase1_D2_13_15(uint8_t *cws);
void hdSurEncPhase2_D2_13_15(uint8_t *cws);

// Viaccess EMU

static int8_t get_key(uint8_t *buf, uint32_t ident, char keyName, uint32_t keyIndex,
						uint32_t keyLength, uint8_t isCriticalKey)
{
	char keyStr[EMU_MAX_CHAR_KEYNAME];
	snprintf(keyStr, EMU_MAX_CHAR_KEYNAME, "%c%X", keyName, keyIndex);

	if (emu_find_key('V', ident, 0, keyStr, buf, keyLength, isCriticalKey, 0, 0, NULL))
	{
		return 1;
	}

	if (ident == 0xD00040 && emu_find_key('V', 0x030B00, 0, keyStr, buf, keyLength, isCriticalKey, 0, 0, NULL))
	{
		return 1;
	}

	return 0;
}

static void via1_mod(const uint8_t *key2, uint8_t *data)
{
	int32_t kb, db;

	for (db = 7; db >= 0; db--)
	{
		for (kb = 7; kb > 3; kb--)
		{
			int32_t a0 = kb ^ db;
			int32_t pos = 7;

			if (a0 & 4)
			{
				a0 ^= 7;
				pos ^= 7;
			}

			a0 = (a0 ^ (kb & 3)) + (kb & 3);

			if (!(a0 & 4))
			{
				data[db] ^= (key2[kb] ^ ((data[kb ^ pos] * key2[kb ^ 4]) & 0xFF));
			}
		}
	}

	for (db = 0; db < 8; db++)
	{
		for (kb = 0; kb < 4; kb++)
		{
			int32_t a0 = kb ^ db;
			int32_t pos = 7;

			if (a0 & 4)
			{
				a0 ^= 7;
				pos ^= 7;
			}

			a0 = (a0 ^ (kb & 3)) + (kb & 3);

			if (!(a0 & 4))
			{
				data[db] ^= (key2[kb] ^ ((data[kb ^ pos] * key2[kb ^ 4]) & 0xFF));
			}
		}
	}
}

static void via1_decode(uint8_t *data, uint8_t *key)
{
	via1_mod(key + 8, data);
	nc_des(key, DES_ECM_CRYPT, data);
	via1_mod(key + 8, data);
}

static void via1_hash(uint8_t *data, uint8_t *key)
{
	via1_mod(key + 8, data);
	nc_des(key, DES_ECM_HASH, data);
	via1_mod(key + 8, data);
}

static inline void via1_do_hash(uint8_t *hashbuffer, uint8_t *pH, uint8_t data, uint8_t *hashkey)
{
	hashbuffer[*pH] ^= data;
	(*pH)++;

	if (*pH == 8)
	{
		via1_hash(hashbuffer, hashkey);
		*pH = 0;
	}
}

static int8_t via1_decrypt(uint8_t *ecm, uint8_t *dw, uint32_t ident, uint8_t desKeyIndex)
{
	int32_t msg_pos, encStart = 0, hash_start, i;

	uint8_t tmp, k, pH, foundData = 0;
	uint8_t work_key[16], signature[8], hashbuffer[8], prepared_key[16], hashkey[16];
	uint8_t *data, *des_data1, *des_data2;

	uint16_t ecmLen = SCT_LEN(ecm);

	if (ident == 0)
	{
		return EMU_CORRUPT_DATA;
	}

	memset(work_key, 0, 16);

	if (!get_key(work_key, ident, '0', desKeyIndex, 8, 1))
	{
		return EMU_KEY_NOT_FOUND;
	}

	if (ecmLen < 11)
	{
		return EMU_NOT_SUPPORTED;
	}

	data = ecm + 9;
	des_data1 = dw;
	des_data2 = dw + 8;

	msg_pos = 0;
	pH = 0;
	memset(hashbuffer, 0, sizeof(hashbuffer));
	memcpy(hashkey, work_key, sizeof(hashkey));
	memset(signature, 0, 8);

	while (9 + msg_pos + 2 < ecmLen)
	{
		switch (data[msg_pos])
		{
			case 0xEA:
				if (9 + msg_pos + 2 + 15 < ecmLen)
				{
					encStart = msg_pos + 2;
					memcpy(des_data1, &data[msg_pos + 2], 8);
					memcpy(des_data2, &data[msg_pos + 2 + 8], 8);
					foundData |= 1;
				}
				break;

			case 0xF0:
				if (9 + msg_pos + 2 + 7 < ecmLen)
				{
					memcpy(signature, &data[msg_pos + 2], 8);
					foundData |= 2;
				}
				break;
		}
		msg_pos += data[msg_pos + 1] + 2;
	}

	if (foundData != 3)
	{
		return EMU_NOT_SUPPORTED;
	}

	pH = i = 0;

	if (data[0] == 0x9F && 10 + data[1] <= ecmLen)
	{
		via1_do_hash(hashbuffer, &pH, data[i++], hashkey);
		via1_do_hash(hashbuffer, &pH, data[i++], hashkey);

		for (hash_start = 0; hash_start < data[1]; hash_start++)
		{
			via1_do_hash(hashbuffer, &pH, data[i++], hashkey);
		}

		while (pH != 0)
		{
			via1_do_hash(hashbuffer, &pH, 0, hashkey);
		}
	}

	if (work_key[7] == 0)
	{
		for (; i < encStart + 16; i++)
		{
			via1_do_hash(hashbuffer, &pH, data[i], hashkey);
		}
		memcpy(prepared_key, work_key, 8);
	}
	else
	{
		prepared_key[0] = work_key[2];
		prepared_key[1] = work_key[3];
		prepared_key[2] = work_key[4];
		prepared_key[3] = work_key[5];
		prepared_key[4] = work_key[6];
		prepared_key[5] = work_key[0];
		prepared_key[6] = work_key[1];
		prepared_key[7] = work_key[7];

		memcpy(prepared_key + 8, work_key + 8, 8);

		if (work_key[7] & 1)
		{
			for (; i < encStart; i++)
			{
				via1_do_hash(hashbuffer, &pH, data[i], hashkey);
			}

			k = ((work_key[7] & 0xF0) == 0) ? 0x5A : 0xA5;

			for (i = 0; i < 8; i++)
			{
				tmp = des_data1[i];
				des_data1[i] = (k & hashbuffer[pH] ) ^ tmp;
				via1_do_hash(hashbuffer, &pH, tmp, hashkey);
			}

			for (i = 0; i < 8; i++)
			{
				tmp = des_data2[i];
				des_data2[i] = (k & hashbuffer[pH] ) ^ tmp;
				via1_do_hash(hashbuffer, &pH, tmp, hashkey);
			}
		}
		else
		{
			for ( ; i < encStart + 16; i++)
			{
				via1_do_hash(hashbuffer, &pH, data[i], hashkey);
			}
		}
	}

	via1_decode(des_data1, prepared_key);
	via1_decode(des_data2, prepared_key);
	via1_hash(hashbuffer, hashkey);

	if (memcmp(signature, hashbuffer, 8))
	{
		return EMU_CHECKSUM_ERROR;
	}

	return EMU_OK;
}

static int8_t via26_process_dw(uint8_t *indata, uint32_t ident, uint8_t desKeyIndex)
{
	uint8_t pv1,pv2, i;
	uint8_t Tmp[8], T1Key[300], P1Key[8], KeyDes1[16], KeyDes2[16], XorKey[8];
	uint32_t ks1[32], ks2[32];

	if (!get_key(T1Key, ident, 'T', 1, 300, 1))
	{
		return 2;
	}

	if (!get_key(P1Key, ident, 'P', 1, 8, 1))
	{
		return 2;
	}

	if (!get_key(KeyDes1, ident, 'D', 1, 16, 1))
	{
		return 2;
	}

	if (!get_key(KeyDes2, ident, '0', desKeyIndex, 16, 1))
	{
		return 2;
	}

	if (!get_key(XorKey, ident, 'X', 1, 8, 1))
	{
		return 2;
	}

	for (i = 0; i < 8; i++)
	{
		pv1 = indata[i];
		Tmp[i] = T1Key[pv1];
	}

	for (i = 0; i < 8; i++)
	{
		pv1 = P1Key[i];
		pv2 = Tmp[pv1];
		indata[i] = pv2;
	}

	des_set_key(KeyDes1, ks1);
	des(indata, ks1, 1);

	for (i = 0; i < 8; i++)
	{
		indata[i] ^= XorKey[i];
	}

	des_set_key(KeyDes2, ks1);
	des_set_key(KeyDes2 + 8, ks2);
	des(indata, ks1, 0);
	des(indata, ks2, 1);
	des(indata, ks1, 0);

	for (i = 0; i < 8; i++)
	{
		indata[i] ^= XorKey[i];
	}

	des_set_key(KeyDes1, ks1);
	des(indata, ks1, 0);

	for (i = 0; i < 8; i++)
	{
		pv1 = indata[i];
		pv2 = P1Key[i];
		Tmp[pv2] = pv1;
	}

	for (i = 0; i < 8; i++)
	{
		pv1 = Tmp[i];
		pv2 = T1Key[pv1];
		indata[i] = pv2;
	}

	return 0;
}

static int8_t via26_decrypt(uint8_t *source, uint8_t *dw, uint32_t ident, uint8_t desKeyIndex)
{
	uint8_t tmpData[8], C1[8];
	uint8_t *pXorVector;
	int32_t i, j;

	if (ident == 0)
	{
		return EMU_CORRUPT_DATA;
	}

	if (!get_key(C1, ident, 'C', 1, 8, 1))
	{
		return EMU_KEY_NOT_FOUND;
	}

	for (i = 0; i < 2; i++)
	{
		memcpy(tmpData, source + i * 8, 8);
		via26_process_dw(tmpData, ident, desKeyIndex);

		if (i != 0)
		{
			pXorVector = source;
		}
		else
		{
			pXorVector = &C1[0];
		}

		for (j = 0; j < 8; j++)
		{
			dw[i * 8 + j] = tmpData[j] ^ pXorVector[j];
		}

		// Fix CW checksum bytes
		for (j = 3; j < 8; j += 4)
		{
			dw[i * 8 + j] = (dw[i * 8 + j - 3] + dw[i * 8 + j - 2] + dw[i * 8 + j - 1]) & 0xFF;
		}
	}

	return EMU_OK;
}

static void via3_core(uint8_t *data, uint8_t Off, uint32_t ident, uint8_t *XorKey, uint8_t *T1Key)
{
	uint8_t i;
	uint32_t lR2, lR3, lR4, lR6, lR7;

	switch (ident)
	{
		case 0x032820:
		{
			for (i = 0; i < 4; i++)
			{
				data[i] ^= XorKey[(Off + i) & 0x07];
			}

			lR2 = (data[0] ^ 0xBD) + data[0];
			lR3 = (data[3] ^ 0xEB) + data[3];
			lR2 = (lR2 - lR3) ^ data[2];
			lR3 = ((0x39 * data[1]) << 2);
			data[4] = (lR2 | lR3) + data[2];

			lR3 = ((((data[0] + 6) ^ data[0]) | (data[2] << 1)) ^ 0x65) + data[0];
			lR2 = (data[1] ^ 0xED) + data[1];
			lR7 = ((data[3] + 0x29) ^ data[3]) * lR2;
			data[5] = lR7 + lR3;

			lR2 = ((data[2] ^ 0x33) + data[2]) & 0x0A;
			lR3 = (data[0] + 0xAD) ^ data[0];
			lR3 = lR3 + lR2;
			lR2 = data[3] * data[3];
			lR7 = (lR2 | 1) + data[1];
			data[6] = (lR3 | lR7) + data[1];

			lR3 = data[1] & 0x07;
			lR2 = (lR3 - data[2]) & (data[0] | lR2 | 0x01);
			data[7] = lR2 + data[3];

			for (i = 0; i < 4; i++)
			{
				data[i + 4] = T1Key[data[i + 4]];
			}
		}
		break;

		case 0x030B00:
		{
			for (i = 0; i < 4; i++)
			{
				data[i] ^= XorKey[(Off + i) & 0x07];
			}

			lR6 = (data[3] + 0x6E) ^ data[3];
			lR6 = (lR6 * (data[2] << 1)) + 0x17;
			lR3 = (data[1] + 0x77) ^ data[1];
			lR4 = (data[0] + 0xD7) ^ data[0];
			data[4] = ((lR4 & lR3) | lR6) + data[0];

			lR4 = ((data[3] + 0x71) ^ data[3]) ^ 0x90;
			lR6 = (data[1] + 0x1B) ^ data[1];
			lR4 = (lR4 * lR6) ^ data[0];
			data[5] = (lR4 ^ (data[2] << 1)) + data[1];

			lR3 = (data[3] * data[3]) | 0x01;
			lR4 = (((data[2] ^ 0x35) + data[2]) | lR3) + data[2];
			lR6 = data[1] ^ (data[0] + 0x4A);
			data[6] = lR6 + lR4;

			lR3 = (data[0] * (data[2] << 1)) | data[1];
			lR4 = 0xFE - data[3];
			lR3 = lR4 ^ lR3;
			data[7] = lR3 + data[3];

			for (i = 0; i < 4; i++)
			{
				data[4 + i] = T1Key[data[4 + i]];
			}
		}
		break;

		default:
			break;
	}
}

static void via3_fct1(uint8_t *data, uint32_t ident, uint8_t *XorKey, uint8_t *T1Key)
{
	uint8_t t;

	via3_core(data, 0, ident, XorKey, T1Key);

	switch (ident)
	{
		case 0x032820:
		{
			t = data[4];
			data[4] = data[7];
			data[7] = t;
		}
		break;

		case 0x030B00:
		{
			t = data[5];
			data[5] = data[7];
			data[7] = t;
		}
		break;

		default:
			break;
	}
}

static void via3_fct2(uint8_t *data, uint32_t ident, uint8_t *XorKey, uint8_t *T1Key)
{
	uint8_t t;

	via3_core(data, 4, ident, XorKey, T1Key);

	switch (ident)
	{
		case 0x032820:
		{
			t = data[4];
			data[4] = data[7];
			data[7] = data[5];
			data[5] = data[6];
			data[6] = t;
		}
		break;

		case 0x030B00:
		{
			t = data[6];
			data[6] = data[7];
			data[7] = t;
		}
		break;

		default:
			break;
	}
}

static int8_t via3_process_dw(uint8_t *data, uint32_t ident, uint8_t desKeyIndex)
{
	uint8_t i;
	uint8_t tmp[8], T1Key[300], P1Key[8], KeyDes[16], XorKey[8];
	uint32_t ks1[32], ks2[32];

	if (!get_key(T1Key, ident, 'T', 1, 300, 1))
	{
		return 2;
	}

	if (!get_key(P1Key, ident, 'P', 1, 8, 1))
	{
		return 2;
	}

	if (!get_key(KeyDes, ident, '0', desKeyIndex, 16, 1))
	{
		return 2;
	}

	if (!get_key(XorKey, ident, 'X', 1, 8, 1))
	{
		return 2;
	}

	for (i = 0; i < 4; i++)
	{
		tmp[i] = data[i + 4];
	}

	via3_fct1(tmp, ident, XorKey, T1Key);

	for (i = 0; i < 4; i++)
	{
		tmp[i] = data[i] ^ tmp[i + 4];
	}

	via3_fct2(tmp, ident, XorKey, T1Key);

	for (i = 0; i < 4; i++)
	{
		tmp[i] ^= XorKey[i + 4];
	}

	for (i = 0; i < 4; i++)
	{
		data[i] = data[i + 4] ^ tmp[i + 4];
		data[i + 4] = tmp[i];
	}

	des_set_key(KeyDes, ks1);
	des_set_key(KeyDes + 8, ks2);

	des(data, ks1, 0);
	des(data, ks2, 1);
	des(data, ks1, 0);

	for (i = 0; i < 4; i++)
	{
		tmp[i] = data[i + 4];
	}

	via3_fct2(tmp, ident, XorKey, T1Key);

	for (i = 0; i < 4; i++)
	{
		tmp[i] = data[i] ^ tmp[i + 4];
	}

	via3_fct1(tmp, ident, XorKey, T1Key);

	for (i = 0; i < 4; i++)
	{
		tmp[i] ^= XorKey[i];
	}

	for (i = 0; i < 4; i++)
	{
		data[i] = data[i + 4] ^ tmp[i + 4];
		data[i + 4] = tmp[i];
	}

	return 0;
}

static void via3_final_mix(uint8_t *dw)
{
	uint8_t tmp[4];

	memcpy(tmp, dw, 4);
	memcpy(dw, dw + 4, 4);
	memcpy(dw + 4, tmp, 4);

	memcpy(tmp, dw + 8, 4);
	memcpy(dw + 8, dw + 12, 4);
	memcpy(dw + 12, tmp, 4);
}

static int8_t via3_decrypt(uint8_t *source, uint8_t *dw, uint32_t ident, uint8_t desKeyIndex,
							uint8_t aesKeyIndex, uint8_t aesMode, int8_t doFinalMix)
{
	int8_t aesAfterCore = 0, needsAES = (aesKeyIndex != 0xFF);
	int32_t i, j;

	uint8_t tmpData[8], C1[8];
	uint8_t *pXorVector;

	char aesKey[16];

	if (ident == 0)
	{
		return EMU_CORRUPT_DATA;
	}

	if (!get_key(C1, ident, 'C', 1, 8, 1))
	{
		return EMU_KEY_NOT_FOUND;
	}

	if (needsAES && !get_key((uint8_t *)aesKey, ident, 'E', aesKeyIndex, 16, 1))
	{
		return EMU_KEY_NOT_FOUND;
	}

	if (aesMode == 0x0D || aesMode == 0x11 || aesMode == 0x15)
	{
		aesAfterCore = 1;
	}

	if (needsAES && !aesAfterCore)
	{
		if (aesMode == 0x0F)
		{
			hdSurEncPhase1_D2_0F_11(source);
			hdSurEncPhase2_D2_0F_11(source);
		}
		else if (aesMode == 0x13)
		{
			hdSurEncPhase1_D2_13_15(source);
		}

		struct aes_keys aes;
		aes_set_key(&aes, aesKey);
		aes_decrypt(&aes, source, 16);

		if (aesMode == 0x0F)
		{
			hdSurEncPhase1_D2_0F_11(source);
		}
		else if (aesMode == 0x13)
		{
			hdSurEncPhase2_D2_13_15(source);
		}
	}

	for (i = 0; i < 2; i++)
	{
		memcpy(tmpData, source + i * 8, 8);
		via3_process_dw(tmpData, ident, desKeyIndex);

		if (i != 0)
		{
			pXorVector = source;
		}
		else
		{
			pXorVector = &C1[0];
		}

		for (j = 0; j < 8; j++)
		{
			dw[i * 8 + j] = tmpData[j] ^ pXorVector[j];
		}
	}

	if (needsAES && aesAfterCore)
	{
		if (aesMode == 0x11)
		{
			hdSurEncPhase1_D2_0F_11(dw);
			hdSurEncPhase2_D2_0F_11(dw);
		}
		else if (aesMode == 0x15)
		{
			hdSurEncPhase1_D2_13_15(dw);
		}

		struct aes_keys aes;
		aes_set_key(&aes, aesKey);
		aes_decrypt(&aes, dw, 16);

		if (aesMode == 0x11)
		{
			hdSurEncPhase1_D2_0F_11(dw);
		}

		if (aesMode == 0x15)
		{
			hdSurEncPhase2_D2_13_15(dw);
		}
	}

	if (ident == 0x030B00)
	{
		if (doFinalMix)
		{
			via3_final_mix(dw);
		}

		if (!is_valid_dcw(dw) || !is_valid_dcw(dw + 8))
		{
			return EMU_CHECKSUM_ERROR;
		}
	}

	return EMU_OK;
}

int8_t viaccess_ecm(uint8_t *ecm, uint8_t *dw)
{
	int8_t doFinalMix = 0;

	uint8_t nanoCmd = 0, nanoLen = 0, version = 0, providerKeyLen = 0;
	uint8_t desKeyIndex = 0, aesMode = 0, aesKeyIndex = 0xFF;
	uint16_t i = 0, keySelectPos = 0, ecmLen = SCT_LEN(ecm);
	uint32_t currentIdent = 0;

	for (i = 4; i + 2 < ecmLen; )
	{
		nanoCmd = ecm[i++];
		nanoLen = ecm[i++];

		if (i + nanoLen > ecmLen)
		{
			return EMU_NOT_SUPPORTED;
		}

		switch (nanoCmd)
		{
			case 0x40:
				if (nanoLen < 0x03)
				{
					break;
				}
				version = ecm[i];
				if (nanoLen == 3)
				{
					currentIdent = ((ecm[i] << 16) | (ecm[i + 1] << 8)) | (ecm[i + 2] & 0xF0);
					desKeyIndex = ecm[i + 2] & 0x0F;
					keySelectPos = i + 3;
				}
				else
				{
					currentIdent = (ecm[i] << 16) | (ecm[i + 1] << 8) | ((ecm[i + 2] >> 4) & 0x0F);
					desKeyIndex = ecm[i + 3];
					keySelectPos = i + 4;
				}
				providerKeyLen = nanoLen;
				break;

			case 0x90:
				if (nanoLen < 0x03)
				{
					break;
				}
				version = ecm[i];
				currentIdent = ((ecm[i] << 16) | (ecm[i + 1] << 8)) | (ecm[i + 2] & 0xF0);
				desKeyIndex = ecm[i + 2] & 0x0F;
				keySelectPos = i + 4;
				if ((version == 3) && (nanoLen > 3))
				{
					desKeyIndex = ecm[i + (nanoLen - 4)] & 0x0F;
				}
				providerKeyLen = nanoLen;
				break;

			case 0x80:
				nanoLen = 0;
				break;

			case 0xD2:
				if (nanoLen < 0x02)
				{
					break;
				}
				aesMode = ecm[i];
				aesKeyIndex = ecm[i + 1];
				break;

			case 0xDD:
				nanoLen = 0;
				break;

			case 0xEA:
				if (nanoLen < 0x10)
				{
					break;
				}
				if (version < 2)
				{
					return via1_decrypt(ecm, dw, currentIdent, desKeyIndex);
				}
				else if (version == 2)
				{
					return via26_decrypt(ecm + i, dw, currentIdent, desKeyIndex);
				}
				else if (version == 3)
				{
					doFinalMix = 0;
					if (currentIdent == 0x030B00 && providerKeyLen > 3)
					{
						if (keySelectPos + 2 >= ecmLen)
						{
							break;
						}
						if (ecm[keySelectPos] == 0x05 && ecm[keySelectPos + 1] == 0x67 &&
							(ecm[keySelectPos + 2] == 0x00 || ecm[keySelectPos + 2] == 0x01))
						{
							if (ecm[keySelectPos + 2] == 0x01)
							{
								doFinalMix = 1;
							}
						}
						else
						{
							break;
						}
					}

					return via3_decrypt(ecm + i, dw, currentIdent, desKeyIndex, aesKeyIndex, aesMode, doFinalMix);
				}
				break;

			default:
				break;
		}

		i += nanoLen;
	}

	return EMU_NOT_SUPPORTED;
}

// Viaccess EMM EMU

int8_t viaccess_emm(uint8_t *emm, uint32_t *keysAdded)
{
	uint8_t nanoCmd = 0, subNanoCmd = 0, *tmp;
	uint8_t ecmKeyCount = 0, emmKeyIndex = 0, aesMode = 0x0D;
	uint8_t nanoLen = 0, subNanoLen = 0, haveEmmXorKey = 0, haveNewD0 = 0;
	uint8_t ecmKeys[6][16], keyD0[2], emmKey[16], emmXorKey[16], provName[17];

	uint16_t i = 0, j = 0, k = 0, emmLen = SCT_LEN(emm);
	uint32_t ui1, ui2, ui3, ecmKeyIndex[6], provider = 0, ecmProvider = 0;

	char keyName[EMU_MAX_CHAR_KEYNAME], keyValue[36];
	struct aes_keys aes;

	memset(keyD0, 0, 2);
	memset(ecmKeyIndex, 0, sizeof(uint32_t) * 6);

	for (i = 3; i + 2 < emmLen; )
	{
		nanoCmd = emm[i++];
		nanoLen = emm[i++];

		if (i + nanoLen > emmLen)
		{
			return EMU_NOT_SUPPORTED;
		}

		switch (nanoCmd)
		{
			case 0x90:
			{
				if (nanoLen < 3)
				{
					break;
				}

				ui1 = emm[i + 2];
				ui2 = emm[i + 1];
				ui3 = emm[i];
				provider = (ui1 | (ui2 << 8) | (ui3 << 16));

				if (provider == 0x00D00040)
				{
					ecmProvider = 0x030B00;
				}
				else
				{
					return EMU_NOT_SUPPORTED;
				}
				break;
			}

			case 0xD2:
			{
				if (nanoLen < 2)
				{
					break;
				}

				emmKeyIndex = emm[i + 1];
				break;
			}

			case 0x41:
			{
				if (nanoLen < 1)
				{
					break;
				}

				if (!get_key(emmKey, provider, 'M', emmKeyIndex, 16, 1))
				{
					return EMU_KEY_NOT_FOUND;
				}

				memset(provName, 0, 17);
				memset(emmXorKey, 0, 16);

				k = nanoLen < 16 ? nanoLen : 16;

				memcpy(provName, &emm[i], k);
				aes_set_key(&aes, (char *)emmKey);
				aes_decrypt(&aes, emmXorKey, 16);

				for (j = 0; j < 16; j++)
				{
					provName[j] ^= emmXorKey[j];
				}
				provName[k] = 0;

				if (strcmp((char *)provName, "TNTSAT") != 0 &&
					strcmp((char *)provName, "TNTSATPRO") != 0 &&
					strcmp((char *)provName, "CSAT V") != 0)
				{
					return EMU_NOT_SUPPORTED;
				}

				break;
			}

			case 0xBA:
			{
				if (nanoLen < 2)
				{
					break;
				}

				get_key(keyD0, ecmProvider, 'D', 0, 2, 0);

				ui1 = (emm[i] << 8) | emm[i + 1];

				if( (uint32_t)((keyD0[0] << 8) | keyD0[1]) < ui1 || (keyD0[0] == 0x00 && keyD0[1] == 0x00))
				{
					keyD0[0] = emm[i];
					keyD0[1] = emm[i + 1];
					haveNewD0 = 1;
					break;
				}

				return EMU_OK;
			}

			case 0xBC:
			{
				break;
			}

			case 0x43:
			{
				if (nanoLen < 16)
				{
					break;
				}

				memcpy(emmXorKey, &emm[i], 16);
				haveEmmXorKey = 1;

				break;
			}

			case 0x44:
			{
				if (nanoLen < 3)
				{
					break;
				}

				if (!haveEmmXorKey)
				{
					memset(emmXorKey, 0, 16);
				}

				tmp = (uint8_t *)malloc(((nanoLen / 16) + 1) * 16 * sizeof(uint8_t));
				if (tmp == NULL)
				{
					return EMU_OUT_OF_MEMORY;
				}

				memcpy(tmp, &emm[i], nanoLen);
				aes_set_key(&aes, (char *)emmKey);

				for (j = 0; j < nanoLen; j += 16)
				{
					aes_decrypt(&aes, emmXorKey, 16);

					for (k = 0; k < 16; k++)
					{
						tmp[j + k] ^= emmXorKey[k];
					}
				}

				memcpy(&emm[i - 2], tmp, nanoLen);
				free(tmp);
				nanoLen = 0;
				i -= 2;
				break;
			}

			case 0x68:
			{
				if (ecmKeyCount > 5)
				{
					break;
				}

				for (j = i; j + 2 < i + nanoLen; )
				{
					subNanoCmd = emm[j++];
					subNanoLen = emm[j++];

					if (j + subNanoLen > i + nanoLen)
					{
						break;
					}

					switch (subNanoCmd)
					{
						case 0xD2:
						{
							if (nanoLen < 2)
							{
								break;
							}

							aesMode = emm[j];
							emmKeyIndex = emm[j + 1];
							break;
						}

						case 0x01:
						{
							if(nanoLen < 17)
							{
								break;
							}

							ecmKeyIndex[ecmKeyCount] = emm[j];
							memcpy(&ecmKeys[ecmKeyCount], &emm[j + 1], 16);

							if (!get_key(emmKey, provider, 'M', emmKeyIndex, 16, 1))
							{
								break;
							}

							if (aesMode == 0x0F || aesMode == 0x11)
							{
								hdSurEncPhase1_D2_0F_11(ecmKeys[ecmKeyCount]);
								hdSurEncPhase2_D2_0F_11(ecmKeys[ecmKeyCount]);
							}
							else if (aesMode == 0x13 || aesMode == 0x15)
							{
								hdSurEncPhase1_D2_13_15(ecmKeys[ecmKeyCount]);
							}

							aes_set_key(&aes, (char *)emmKey);
							aes_decrypt(&aes, ecmKeys[ecmKeyCount], 16);

							if (aesMode == 0x0F || aesMode == 0x11)
							{
								hdSurEncPhase1_D2_0F_11(ecmKeys[ecmKeyCount]);
							}
							else if (aesMode == 0x13 || aesMode == 0x15)
							{
								hdSurEncPhase2_D2_13_15(ecmKeys[ecmKeyCount]);
							}

							ecmKeyCount++;
							break;
						}

						default:
							break;
					}

					j += subNanoLen;
				}
				break;
			}

			case 0xF0:
			{
				if (nanoLen != 4)
				{
					break;
				}

				ui1 = ((emm[i + 2] << 8) | (emm[i + 1] << 16) | (emm[i] << 24) | emm[i + 3]);

				if (ccitt32_crc(emm + 3, emmLen - 11) != ui1)
				{
					return EMU_CHECKSUM_ERROR;
				}

				if (haveNewD0)
				{
					SAFE_MUTEX_LOCK(&emu_key_data_mutex);
					emu_set_key('V', ecmProvider, "D0", keyD0, 2, 1, NULL, NULL);

					for (j = 0; j < ecmKeyCount; j++)
					{
						snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "E%X", ecmKeyIndex[j]);
						emu_set_key('V', ecmProvider, keyName, ecmKeys[j], 16, 1, NULL, NULL);

						(*keysAdded)++;
						cs_hexdump(0, ecmKeys[j], 16, keyValue, sizeof(keyValue));
						cs_log("Key found in EMM: V %06X %s %s", ecmProvider, keyName, keyValue);
					}
					SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
				}
				break;
			}

			default:
				break;
		}

		i += nanoLen;
	}

	return EMU_OK;
}

#endif // WITH_EMU
