#define MODULE_LOG_PREFIX "emu"

#include "globals.h"

#ifdef WITH_EMU

#include "cscrypt/des.h"
#include "module-emulator-osemu.h"

// Cryptoworks EMU

static int8_t get_key(uint8_t *buf,uint32_t ident, uint8_t keyIndex, uint32_t keyLength, uint8_t isCriticalKey)
{
	char keyName[EMU_MAX_CHAR_KEYNAME];
	uint32_t tmp;

	if ((ident >> 4) == 0xD02A)
	{
		keyIndex &= 0xFE; // map to even number key indexes
	}

	if ((ident >> 4) == 0xD00C)
	{
		ident = 0x0D00C0; // map provider C? to C0
	}
	else if (keyIndex == 6 && ((ident >> 8) == 0x0D05))
	{
		ident = 0x0D0504; // always use provider 04 system key
	}

	tmp = keyIndex;
	snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "%.2X", tmp);

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

	return 0;
}

static const uint8_t cw_sbox1[64] =
{
	0xD8, 0xD7, 0x83, 0x3D, 0x1C, 0x8A, 0xF0, 0xCF, 0x72, 0x4C, 0x4D, 0xF2, 0xED, 0x33, 0x16, 0xE0,
	0x8F, 0x28, 0x7C, 0x82, 0x62, 0x37, 0xAF, 0x59, 0xB7, 0xE0, 0x00, 0x3F, 0x09, 0x4D, 0xF3, 0x94,
	0x16, 0xA5, 0x58, 0x83, 0xF2, 0x4F, 0x67, 0x30, 0x49, 0x72, 0xBF, 0xCD, 0xBE, 0x98, 0x81, 0x7F,
	0xA5, 0xDA, 0xA7, 0x7F, 0x89, 0xC8, 0x78, 0xA7, 0x8C, 0x05, 0x72, 0x84, 0x52, 0x72, 0x4D, 0x38
};

static const uint8_t cw_sbox2[64] =
{
	0xD8, 0x35, 0x06, 0xAB, 0xEC, 0x40, 0x79, 0x34, 0x17, 0xFE, 0xEA, 0x47, 0xA3, 0x8F, 0xD5, 0x48,
	0x0A, 0xBC, 0xD5, 0x40, 0x23, 0xD7, 0x9F, 0xBB, 0x7C, 0x81, 0xA1, 0x7A, 0x14, 0x69, 0x6A, 0x96,
	0x47, 0xDA, 0x7B, 0xE8, 0xA1, 0xBF, 0x98, 0x46, 0xB8, 0x41, 0x45, 0x9E, 0x5E, 0x20, 0xB2, 0x35,
	0xE4, 0x2F, 0x9A, 0xB5, 0xDE, 0x01, 0x65, 0xF8, 0x0F, 0xB2, 0xD2, 0x45, 0x21, 0x4E, 0x2D, 0xDB
};

static const uint8_t cw_sbox3[64] =
{
	0xDB, 0x59, 0xF4, 0xEA, 0x95, 0x8E, 0x25, 0xD5, 0x26, 0xF2, 0xDA, 0x1A, 0x4B, 0xA8, 0x08, 0x25,
	0x46, 0x16, 0x6B, 0xBF, 0xAB, 0xE0, 0xD4, 0x1B, 0x89, 0x05, 0x34, 0xE5, 0x74, 0x7B, 0xBB, 0x44,
	0xA9, 0xC6, 0x18, 0xBD, 0xE6, 0x01, 0x69, 0x5A, 0x99, 0xE0, 0x87, 0x61, 0x56, 0x35, 0x76, 0x8E,
	0xF7, 0xE8, 0x84, 0x13, 0x04, 0x7B, 0x9B, 0xA6, 0x7A, 0x1F, 0x6B, 0x5C, 0xA9, 0x86, 0x54, 0xF9
};

static const uint8_t cw_sbox4[64] =
{
	0xBC, 0xC1, 0x41, 0xFE, 0x42, 0xFB, 0x3F, 0x10, 0xB5, 0x1C, 0xA6, 0xC9, 0xCF, 0x26, 0xD1, 0x3F,
	0x02, 0x3D, 0x19, 0x20, 0xC1, 0xA8, 0xBC, 0xCF, 0x7E, 0x92, 0x4B, 0x67, 0xBC, 0x47, 0x62, 0xD0,
	0x60, 0x9A, 0x9E, 0x45, 0x79, 0x21, 0x89, 0xA9, 0xC3, 0x64, 0x74, 0x9A, 0xBC, 0xDB, 0x43, 0x66,
	0xDF, 0xE3, 0x21, 0xBE, 0x1E, 0x16, 0x73, 0x5D, 0xA2, 0xCD, 0x8C, 0x30, 0x67, 0x34, 0x9C, 0xCB
};

static const uint8_t AND_bit1[8] = { 0x00, 0x40, 0x04, 0x80, 0x21, 0x10, 0x02, 0x08 };
static const uint8_t AND_bit2[8] = { 0x80, 0x08, 0x01, 0x40, 0x04, 0x20, 0x10, 0x02 };
static const uint8_t AND_bit3[8] = { 0x82, 0x40, 0x01, 0x10, 0x00, 0x20, 0x04, 0x08 };
static const uint8_t AND_bit4[8] = { 0x02, 0x10, 0x04, 0x40, 0x80, 0x08, 0x01, 0x20 };

static void swap_key(uint8_t *key)
{
	uint8_t k[8];
	memcpy(k, key, 8);
	memcpy(key, key + 8, 8);
	memcpy(key + 8, k, 8);
}

static void swap_data(uint8_t *k)
{
	uint8_t d[4];
	memcpy(d, k + 4, 4);
	memcpy(k + 4, k, 4);
	memcpy(k, d, 4);
}

static void des_round(uint8_t *d, uint8_t *k)
{
	uint8_t aa[44] =
	{
		1, 0, 3, 1, 2, 2, 3, 2, 1, 3, 1, 1, 3, 0, 1, 2, 3, 1, 3, 2, 2, 0,
		7, 6, 5, 4, 7, 6, 5, 7, 6, 5, 6, 7, 5, 7, 5, 7, 6, 6, 7, 5, 4, 4
	};

	uint8_t bb[44] =
	{
		0x80, 0x08, 0x10, 0x02, 0x08, 0x40, 0x01, 0x20, 0x40, 0x80, 0x04,
		0x10, 0x04, 0x01, 0x01, 0x02, 0x20, 0x20, 0x02, 0x01, 0x80, 0x04,
		0x02, 0x02, 0x08, 0x02, 0x10, 0x80, 0x01, 0x20, 0x08, 0x80, 0x01,
		0x08, 0x40, 0x01, 0x02, 0x80, 0x10, 0x40, 0x40, 0x10, 0x08, 0x01
	};

	uint8_t ff[4] = { 0x02, 0x10, 0x04, 0x04};
	uint8_t l[24] = { 0, 2, 4, 6, 7, 5, 3, 1, 4, 5, 6, 7, 7, 6, 5, 4, 7, 4, 5, 6, 4, 7, 6, 5 };

	uint8_t des_td[8], i, o, n, c = 1, m = 0, r = 0;
	uint8_t *a = aa, *b = bb, *f = ff, *p1 = l, *p2 = l + 8, *p3 = l + 16;

	for (m = 0; m < 2; m++)
	{
		for (i = 0; i < 4; i++)
		{
			des_td[*p1++] = (m) ? ((d[*p2++] * 2) & 0x3F) | ((d[*p3++] & 0x80) ? 0x01 : 0x00) :
									(d[*p2++] / 2) | ((d[*p3++] & 0x01) ? 0x80 : 0x00);
		}
	}

	for (i = 0; i < 8; i++)
	{
		c = (c) ? 0 : 1;
		r = (c) ? 6 : 7;
		n = (i) ? i - 1 : 1;
		o = (c) ? ((k[n] & *f++) ? 1 : 0) : des_td[n];

		for (m = 1; m < r; m++)
		{
			o = (c) ? (o * 2) | ((k[*a++] & *b++) ? 0x01 : 0x00) : (o / 2) | ((k[*a++] & *b++) ? 0x80 : 0x00);
		}

		n = (i) ? n + 1 : 0;
		des_td[n] = (c) ? des_td[n] ^ o : (o ^ des_td[n]) / 4;
	}

	for (i = 0; i < 8; i++)
	{
		d[0] ^= (AND_bit1[i] & cw_sbox1[des_td[i]]);
		d[1] ^= (AND_bit2[i] & cw_sbox2[des_td[i]]);
		d[2] ^= (AND_bit3[i] & cw_sbox3[des_td[i]]);
		d[3] ^= (AND_bit4[i] & cw_sbox4[des_td[i]]);
	}

	swap_data(d);
}

static void cw_48_key(uint8_t *inkey, uint8_t *outkey, uint8_t algotype)
{
	uint8_t round_counter, i = 8;
	uint8_t *key128 = inkey;
	uint8_t *key48 = inkey + 0x10;

	round_counter = 7 - (algotype & 7);

	memset(outkey, 0, 16);
	memcpy(outkey, key48, 6);

	for ( ; i > round_counter; i--)
	{
		if (i > 1)
		{
			outkey[i - 2] = key128[i];
		}
	}
}

static void ls_des_key(uint8_t *key, uint8_t rotate_counter)
{
	uint8_t i, n;
	uint8_t rnd[] = { 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, 1 };
	uint16_t k[8];

	n = rnd[rotate_counter];

	for (i = 0; i < 8; i++)
	{
		k[i] = key[i];
	}

	for (i = 1; i < n + 1; i++)
	{
		k[7] = (k[7] * 2) | ((k[4] & 0x008) ? 1 : 0);
		k[6] = (k[6] * 2) | ((k[7] & 0xF00) ? 1 : 0);
		k[7] &= 0xFF;

		k[5] = (k[5] * 2) | ((k[6] & 0xF00) ? 1 : 0);
		k[6] &= 0xFF;

		k[4] = ((k[4] * 2) | ((k[5] & 0xF00) ? 1 : 0)) & 0xFF;
		k[5] &= 0xFF;

		k[3] = (k[3] * 2) | ((k[0] & 0x008) ? 1 : 0);
		k[2] = (k[2] * 2) | ((k[3] & 0xF00) ? 1 : 0);
		k[3] &= 0xFF;

		k[1] = (k[1] * 2) | ((k[2] & 0xF00) ? 1 : 0);
		k[2] &= 0xFF;

		k[0] = ((k[0] * 2) | ((k[1] & 0xF00) ? 1 : 0)) & 0xFF;
		k[1] &= 0xFF;
	}

	for (i = 0; i < 8; i++)
	{
		key[i] = (uint8_t) k[i];
	}
}

static void rs_des_key(uint8_t *k, uint8_t rotate_counter)
{
	uint8_t i, c;

	for (i = 1; i < rotate_counter + 1; i++)
	{
		c = (k[3] & 0x10) ? 0x80 : 0;
		k[3] /= 2;

		if (k[2] & 1)
		{
			k[3] |= 0x80;
		}

		k[2] /= 2;

		if (k[1] & 1)
		{
			k[2] |= 0x80;
		}

		k[1] /= 2;

		if (k[0] & 1)
		{
			k[1] |= 0x80;
		}

		k[0] /= 2;
		k[0] |= c ;
		c = (k[7] & 0x10) ? 0x80 : 0;
		k[7] /= 2;

		if (k[6] & 1)
		{
			k[7] |= 0x80;
		}

		k[6] /= 2;

		if (k[5] & 1)
		{
			k[6] |= 0x80;
		}

		k[5] /= 2;

		if (k[4] & 1)
		{
			k[5] |= 0x80;
		}

		k[4] /= 2;
		k[4] |= c;
	}
}

static void rs_des_subkey(uint8_t *k, uint8_t rotate_counter)
{
	uint8_t rnd[] = { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 };

	rs_des_key(k, rnd[rotate_counter]);
}

static void prep_key(uint8_t *key)
{
	int32_t round_counter = 6, i, a;
	uint8_t DES_key[8], j;

	key[7] = 6;
	memset(DES_key, 0, 8);

	do
	{
		a = 7;
		i = key[7];
		j = key[round_counter];

		do
		{
			DES_key[i] = ( (DES_key[i] * 2) | ((j & 1) ? 1 : 0) ) & 0xFF;

			j /= 2;
			i--;

			if (i < 0)
			{
				i = 6;
			}
			a--;
		}
		while (a >= 0);

		key[7] = i;
		round_counter--;
	}
	while (round_counter >= 0);

	a = DES_key[4];
	DES_key[4] = DES_key[6];
	DES_key[6] = a;
	DES_key[7] = (DES_key[3] * 16) & 0xFF;

	memcpy(key, DES_key, 8);
	rs_des_key(key, 4);
}

static void l2_des(uint8_t *data, uint8_t *key, uint8_t algo)
{
	uint8_t i, k0[22], k1[22];

	memcpy(k0, key, 22);
	memcpy(k1, key, 22);

	cw_48_key(k0, k1, algo);
	prep_key(k1);

	for (i = 0; i < 2; i++)
	{
		ls_des_key(k1, 15);
		des_round(data, k1);
	}
}

static void r2_des(uint8_t *data, uint8_t *key, uint8_t algo)
{
	uint8_t i, k0[22], k1[22];

	memcpy(k0, key, 22);
	memcpy(k1, key, 22);

	cw_48_key(k0, k1, algo);
	prep_key(k1);

	for (i = 0; i < 2; i++)
	{
		ls_des_key(k1, 15);
	}

	for (i = 0; i < 2; i++)
	{
		des_round(data, k1);
		rs_des_subkey(k1, 1);
	}

	swap_data(data);
}

static void cw_des(uint8_t *data, uint8_t *inkey, uint8_t m)
{
	uint8_t key[22], i;

	memcpy(key, inkey + 9, 8);
	prep_key(key);

	for (i = 16; i > 0; i--)
	{
		if (m == 1)
		{
			ls_des_key(key, (uint8_t) (i - 1));
		}

		des_round( data ,key);

		if (m == 0)
		{
			rs_des_subkey(key, (uint8_t) (i - 1));
		}
	}
}

static void cw_dec_enc(uint8_t *d, uint8_t *k, uint8_t a, uint8_t m)
{
	uint8_t n = m & 1;

	l2_des(d, k, a);
	cw_des(d, k, n);
	r2_des(d, k, a);

	if (m & 2)
	{
		swap_key(k);
	}
}

static uint8_t process_nano80(uint8_t *data, uint32_t caid, int32_t provider, uint8_t *opKey,
								uint8_t nanoLength, uint8_t nano80Algo)
{
	int32_t i, j;
	uint8_t key[16], desKey[16], t[8], dat1[8], dat2[8], k0D00C000[16];

	if (nanoLength < 11)
	{
		return 0;
	}

	if (caid == 0x0D00 && provider != 0xA0 && !get_key(k0D00C000, 0x0D00C0, 0, 16, 1))
	{
		return 0;
	}

	if (nano80Algo > 1)
	{
		return 0;
	}

	memset(t, 0, 8);
	memcpy(dat1, data, 8);

	if(caid == 0x0D00 && provider != 0xA0)
	{
		memcpy(key, k0D00C000, 16);
	}
	else
	{
		memcpy(key, opKey, 16);
	}

	des_ecb3_decrypt(data, key);
	memcpy(desKey, data, 8);
	memcpy(data, dat1, 8);

	if (caid == 0x0D00 && provider != 0xA0)
	{
		memcpy(key, &k0D00C000[8], 8);
		memcpy(&key[8], k0D00C000, 8);
	}
	else
	{
		memcpy(key, &opKey[8], 8);
		memcpy(&key[8], opKey, 8);
	}

	des_ecb3_decrypt(data, key);
	memcpy(&desKey[8], data, 8);

	for (i = 8; i + 7 < nanoLength; i += 8)
	{
		memcpy(dat1, &data[i], 8);
		memcpy(dat2, dat1, 8);
		memcpy(key, desKey, 16);
		des_ecb3_decrypt(dat1, key);

		for (j = 0; j < 8; j++)
		{
			dat1[j] ^= t[j];
		}

		memcpy(&data[i], dat1, 8);
		memcpy(t, dat2, 8);
	}

	return data[10] + 5;
}

static void cryptoworks_signature(const uint8_t *data, uint32_t length, uint8_t *key, uint8_t *signature)
{
	uint32_t i, sigPos;
	int8_t algo, first;

	algo = data[0] & 7;
	if (algo == 7)
	{
		algo = 6;
	}

	memset(signature, 0, 8);
	first = 1;
	sigPos = 0;

	for (i = 0; i < length; i++)
	{
		signature[sigPos] ^= data[i];
		sigPos++;

		if (sigPos > 7)
		{
			if (first)
			{
				l2_des(signature, key, algo);
			}

			cw_des(signature, key, 1);

			sigPos = 0;
			first = 0;
		}
	}

	if (sigPos > 0)
	{
		cw_des(signature, key, 1);
	}

	r2_des(signature, key, algo);
}

static void decrypt_des(uint8_t *data, uint8_t algo, uint8_t *key)
{
	int32_t i;
	uint8_t k[22], t[8];

	algo &= 7;

	if (algo < 7)
	{
		cw_dec_enc(data, key, algo, 0);
	}
	else
	{
		memcpy(k, key, 22);

		for (i = 0; i < 3; i++)
		{
			cw_dec_enc(data, k, algo, i & 1);

			memcpy(t, k, 8);
			memcpy(k, k + 8, 8);
			memcpy(k + 8, t, 8);
		}
	}
}

int8_t cryptoworks_ecm(uint32_t caid, uint8_t *ecm, uint8_t *cw)
{
	int32_t provider = -1;
	uint8_t keyIndex = 0, nanoLength, newEcmLength, key[22], signature[8], nano80Algo = 1;
	uint16_t i, j, ecmLen = SCT_LEN(ecm);
	uint32_t ident;

	if (ecmLen < 8)
	{
		return EMU_NOT_SUPPORTED;
	}

	if (ecm[7] != ecmLen - 8)
	{
		return EMU_NOT_SUPPORTED;
	}

	memset(key, 0, 22);

	for (i = 8; i + 1 < ecmLen; i += ecm[i + 1] + 2)
	{
		if (ecm[i] == 0x83 && i + 2 < ecmLen)
		{
			provider = ecm[i + 2] & 0xFC;
			keyIndex = ecm[i + 2] & 3;
			keyIndex = keyIndex ? 1 : 0;
		}
		else if (ecm[i] == 0x84 && i + 3 < ecmLen)
		{
			//nano80Provider = ecm[i + 2] & 0xFC;
			//nano80KeyIndex = ecm[i + 2] & 3;
			//nano80KeyIndex = nano80KeyIndex ? 1 : 0;
			nano80Algo = ecm[i + 3];
		}
	}

	if (provider < 0)
	{
		switch (caid)
		{
			case 0x0D00:
				provider = 0xC0;
				break;

			case 0x0D02:
				provider = 0xA0;
				break;

			case 0x0D03:
				provider = 0x04;
				break;

			case 0x0D05:
				provider = 0x04;
				break;

			default:
				return EMU_NOT_SUPPORTED;
		}
	}

	ident = (caid << 8) | provider;

	if (!get_key(key, ident, keyIndex, 16, 1))
	{
		return EMU_KEY_NOT_FOUND;
	}

	if (!get_key(&key[16], ident, 6, 6, 1))
	{
		return EMU_KEY_NOT_FOUND;
	}

	for (i = 8; i + 1 < ecmLen; i += ecm[i + 1] + 2)
	{
		if (ecm[i] == 0x80 && i + 2 + 7 < ecmLen && i + 2 + ecm[i + 1] <= ecmLen &&
			(provider == 0xA0 || provider == 0xC0 || provider == 0xC4 || provider == 0xC8))
		{
			nanoLength = ecm[i + 1];
			newEcmLength = process_nano80(ecm + i + 2, caid, provider, key, nanoLength, nano80Algo);

			if (newEcmLength == 0 || newEcmLength > ecmLen - (i + 2 + 3))
			{
				return EMU_NOT_SUPPORTED;
			}

			ecm[i + 2 + 3] = 0x81;
			ecm[i + 2 + 4] = 0x70;
			ecm[i + 2 + 5] = newEcmLength;
			ecm[i + 2 + 6] = 0x81;
			ecm[i + 2 + 7] = 0xFF;

			return cryptoworks_ecm(caid, ecm + i + 2 + 3, cw);
		}
	}

	if (ecmLen - 15 < 1)
	{
		return EMU_NOT_SUPPORTED;
	}

	cryptoworks_signature(ecm + 5, ecmLen - 15, key, signature);

	for (i = 8; i + 1 < ecmLen; i += ecm[i + 1] + 2)
	{
		switch (ecm[i])
		{
			case 0xDA:
			case 0xDB:
			case 0xDC:
				if (i + 2 + ecm[i + 1] > ecmLen)
				{
					break;
				}
				for (j = 0; j + 7 < ecm[i + 1]; j += 8)
				{
					decrypt_des(&ecm[i + 2 + j], ecm[5], key);
				}
				break;

			case 0xDF:
				if (i + 2 + 8 > ecmLen)
				{
					break;
				}
				if (memcmp(&ecm[i + 2], signature, 8))
				{
					return EMU_CHECKSUM_ERROR;
				}
				break;
		}
	}

	for (i = 8; i + 1 < ecmLen; i += ecm[i + 1] + 2)
	{
		switch (ecm[i])
		{
			case 0xDB:
				if (i + 2 + ecm[i + 1] <= ecmLen && ecm[i + 1] == 16)
				{
					memcpy(cw, &ecm[i + 2], 16);
					return EMU_OK;
				}
				break;
		}
	}

	return EMU_CW_NOT_FOUND;
}

#endif // WITH_EMU
