#include "globals.h"
#ifdef READER_CONAX
#include "cscrypt/bn.h"
#include "reader-common.h"
#include "cscrypt/des.h"

static int32_t CWPK_CNX(struct s_reader *reader,uint8_t *msg)
{
	uint8_t CWp[16], CWs[16];

	memcpy(CWp, msg + 7, 8);
	memcpy(CWp + 8, msg + 22, 8);

	des_ecb3_decrypt(CWp, reader->cwpk_mod);
	des_ecb3_decrypt(CWp + 8, reader->cwpk_mod);

	memcpy(CWs, CWp + 4, 4);
	memcpy(CWs + 4, CWp, 4);
	memcpy(CWs + 8, CWp + 12, 4);
	memcpy(CWs + 12, CWp + 8, 4);

	int chkok = 1;
	if(((CWs[0] + CWs[1] + CWs[2]) & 0xFF) != CWs[3])
	{
		chkok = 0;
		rdr_log(reader, "CW0 checksum error [0]");
	}
	if(((CWs[4] + CWs[5] + CWs[6]) & 0xFF) != CWs[7])
	{
		chkok = 0;
		rdr_log(reader, "CW0 checksum error [1]");
	}
	if(((CWs[8] + CWs[9] + CWs[10]) & 0xFF) != CWs[11])
	{
		chkok = 0;
		rdr_log(reader, "CW1 checksum error [0]");
	}
	if(((CWs[12] + CWs[13] + CWs[14]) & 0xFF) != CWs[15])
	{
		chkok = 0;
		rdr_log(reader, "CW1 checksum error [1]");
	}

	if(!chkok)
		return -8;

	memcpy(msg + 7, CWs, 8);
	memcpy(msg + 22, CWs + 8, 8);
	return 0;
}

static int32_t RSA_CNX(struct s_reader *reader, uint8_t *msg, uint8_t *mod, uint8_t *exp, uint32_t cta_lr, uint32_t modbytes, uint32_t expbytes)
{
	int32_t ret = 0;
	uint32_t n = 0, pre_size = 0, size = 0;
	BN_CTX *ctx;
	BIGNUM *bn_mod, *bn_exp, *bn_data, *bn_res;
	uint8_t data[64];

	/*prefix size*/
	pre_size = 2 + 4 + msg[5];

	/*size of data to decryption*/
	if(msg[1] > (pre_size - 2))
		{ size = msg[1] - pre_size + 2; }

	if(cta_lr > (pre_size + size) && size >= modbytes && size < 128)
	{
		ctx = BN_CTX_new();

		if(ctx == NULL)
		{
			rdr_log_dbg(reader, D_READER, "RSA Error in RSA_CNX");
			return -1;
		}

		BN_CTX_start(ctx);
		bn_mod = BN_CTX_get(ctx);
		bn_exp = BN_CTX_get(ctx);
		bn_data = BN_CTX_get(ctx);
		bn_res = BN_CTX_get(ctx);

		/*RSA first round*/
		BN_bin2bn(mod, modbytes, bn_mod); // rsa modulus
		BN_bin2bn(exp, expbytes, bn_exp); // exponent
		BN_bin2bn(msg + pre_size, modbytes, bn_data);
		BN_mod_exp(bn_res, bn_data, bn_exp, bn_mod, ctx);

		n = BN_bn2bin(bn_res, data);

		size -= modbytes; // 3
		pre_size += modbytes;

		/*Check if second round is needed*/
		if(0 < size)
		{
			/*check if length of data from first RSA round will be enough to padding rest of data*/
			if((n + size) >= modbytes)
			{
				/*RSA second round*/
				/*move the remaining data at the beginning of the buffer*/
				memcpy(msg, msg + pre_size, size);
				/*padding buffer with data from first round*/
				memcpy(msg + size, data + (n - (modbytes - size)), modbytes - size);

				BN_bin2bn(msg, modbytes, bn_data);
				BN_mod_exp(bn_res, bn_data, bn_exp, bn_mod, ctx);
				n = BN_bn2bin(bn_res, data);
				if(0x25 != data[0])
					{ ret = -1; } /*RSA key is probably wrong*/
			}
			else
				{ ret = -3; } /*wrong size of data for second round*/
		}

		if(0 == ret)
			{ memcpy(msg, data, n); }

		BN_CTX_end(ctx);
		BN_CTX_free(ctx);
	}
	else
		{ ret = -2; } /*wrong size of data*/

	return ret;
}

static time_t chid_date(const uint8_t *ptr, char *buf, int32_t l)
{
	time_t rc = 0;
	struct tm timeinfo;
	memset(&timeinfo, 0, sizeof(struct tm));

	if(buf)
	{
		timeinfo.tm_year = 90 + (ptr[1] >> 4) + (((ptr[0] >> 5) & 7) * 10);
		timeinfo.tm_mon = (ptr[1] & 0xf) - 1;
		timeinfo.tm_mday = ptr[0] & 0x1f;
		timeinfo.tm_isdst = -1;
		rc = mktime(&timeinfo);
		strftime(buf, l, "%Y/%m/%d", &timeinfo);
	}
	return (rc);
}

static int32_t read_record(struct s_reader *reader, const uint8_t *cmd, const uint8_t *data, uint8_t *cta_res)
{
	uint16_t cta_lr;
	uint8_t insCA[] = { 0xDD, 0xCA, 0x00, 0x00, 0x00 };

	write_cmd(cmd, data); // select record
	if(cta_res[0] != 0x98)
		{ return (-1); }

	insCA[4] = cta_res[1]; // get len
	write_cmd(insCA, NULL); // read record

	if((cta_res[cta_lr - 2] != 0x90) || (cta_res[cta_lr - 1]))
		{ return (-1); }

	return (cta_lr - 2);
}

static int32_t check_pairing(struct s_reader *reader, const uint8_t *cmd, const uint8_t *data, uint8_t *cta_res)
{
	uint16_t cta_lr;

	if(reader->cwpk_mod_length)
	{
		write_cmd(cmd, data);
		rdr_log(reader, "CWPK Pairing is active");
	}
	else if(reader->rsa_mod_length)
	{
		rdr_log(reader, "RSA Pairing is active");
	}
	else
	{
		rdr_log(reader, "Pairing is not active");
	}
	return OK;
}

static uint8_t PairingECMRotation(struct s_reader *reader, const ECM_REQUEST *er, int32_t n)
{
	uint8_t cta_res[CTA_RES_LEN] = { 0x00 };
	uint8_t ins26[] = { 0xDD, 0x26, 0x00, 0x00, 0x03, 0x10, 0x01, 0x00 };
	uint8_t cnxcurrecm = 0;

	if(0x0 != reader->rsa_mod[0] && n > 3 &&
			0x54 == er->ecm[n - 3] &&
			0x02 == er->ecm[n - 2] &&
			0x00 == er->ecm[n - 1])
	{
		cnxcurrecm = 1;
	}

	if((0 == reader->cnxlastecm) != (0 == cnxcurrecm))
	{
		if(0 == cnxcurrecm) // not paired
			{ ins26[7] = 0x30; }
		else
			{ ins26[7] = 0x40; }

		if(read_record(reader, ins26, ins26 + 5, cta_res) <= 0)
			{ rdr_log(reader, "PairingECMRotation - ERROR"); }
	}
	reader->cnxlastecm = cnxcurrecm;
	return cnxcurrecm;
}

static int32_t conax_card_init(struct s_reader *reader, ATR *newatr)
{
	uint8_t cta_res[CTA_RES_LEN];
	int32_t i, j, n;
	static const uint8_t ins26[] = { 0xDD, 0x26, 0x00, 0x00, 0x03, 0x10, 0x01, 0x40 };
	static const uint8_t inscp[] = { 0xDD, 0x26, 0x00, 0x00, 0x04, 0x6C, 0x02, 0x10,0x00 };
	uint8_t ins82[] = { 0xDD, 0x82, 0x00, 0x00, 0x11, 0x11, 0x0f, 0x01, 0xb0, 0x0f, 0xff,
						0xff, 0xfb, 0x00, 0x00, 0x09, 0x04, 0x0b, 0x00, 0xe0, 0x30, 0x2b };

	uint8_t cardver = 0;

	get_hist;
	if((hist_size < 4) || (memcmp(hist, "0B00", 4)))
		{ return ERROR; }

	reader->caid = 0xB00;

	if((n = read_record(reader, ins26, ins26 + 5, cta_res)) <= 0) { return ERROR; } // read caid, card-version

	for(i = 0; i < n; i += cta_res[i + 1] + 2)
	{
		switch(cta_res[i])
		{
			case 0x20:
				cardver = cta_res[i + 2];
				break;

			case 0x28:
				reader->caid = (cta_res[i + 2] << 8) | cta_res[i + 3];
		}
	}

	// Ins82 command needs to use the correct CAID reported in nano 0x28
	ins82[17] = (reader->caid >> 8) & 0xFF;
	ins82[18] = (reader->caid) & 0xFF;

	if((n = read_record(reader, ins82, ins82 + 5, cta_res)) <= 0) { return ERROR; } // read serial

	reader->nprov = 0;

	for(j = 0, i = 2; i < n; i += cta_res[i + 1] + 2)
	{
		switch(cta_res[i])
		{
			case 0x23:
				if(cta_res[i + 5] != 0x00)
				{
					memcpy(reader->hexserial, &cta_res[i + 3], 6);
				}
				else
				{
					memcpy(reader->sa[j], &cta_res[i + 5], 4);
					j++;
					reader->nprov++;
				}
				break;
		}
	}

	memset(reader->prid, 0x00, sizeof(reader->prid));

	rdr_log_sensitive(reader, "type: Conax, caid: %04X, serial: {%llu}, hex serial: {%02x%02x%02x%02x}, card: v%d",
						reader->caid, (unsigned long long) b2ll(6, reader->hexserial), reader->hexserial[2],
						reader->hexserial[3], reader->hexserial[4], reader->hexserial[5], cardver);

	rdr_log(reader, "Providers: %d", reader->nprov);

	for(j = 0; j < reader->nprov; j++)
	{
		rdr_log(reader, "Provider: %d Provider-Id: %06X", j + 1, b2i(4, reader->prid[j]));
		rdr_log_sensitive(reader, "Provider: %d SharedAddress: {%08X}", j + 1, b2i(4, reader->sa[j]));
	}
	check_pairing(reader, inscp, inscp + 5, cta_res);

	return OK;
}

static int32_t conax_send_pin(struct s_reader *reader)
{
	def_resp;
	uint8_t insPIN[] = { 0xDD, 0xC8, 0x00, 0x00, 0x07, 0x1D, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00 }; // Last four are the Pin-Code
	memcpy(insPIN + 8, reader->pincode, 4);

	write_cmd(insPIN, insPIN + 5);
	rdr_log_dbg(reader, D_READER, "Sent pincode to card.");

	return OK;
}

static int32_t conax_do_ecm(struct s_reader *reader, const ECM_REQUEST *er, struct s_ecm_answer *ea)
{
	def_resp;
	int32_t i, j, n, num_dw = 0, rc = 0;
	uint8_t insA2[] = { 0xDD, 0xA2, 0x00, 0x00, 0x00 };
	uint8_t insCA[] = { 0xDD, 0xCA, 0x00, 0x00, 0x00 };

	uint8_t exp[] = { 0x01, 0x00, 0x01 };
	uint8_t buf[256];

	char ppp = 0x00;

	if((n = check_sct_len(er->ecm, 3)) < 0)
		{ return ERROR; }

	buf[0] = 0x14;
	buf[1] = n + 1;

	if(reader->cwpk_mod_length)
	{
		buf[2] = 4;
		ppp = 0x01;
	}
	else if(0x0 != reader->rsa_mod[0])
	{
		if(0x0 != PairingECMRotation(reader, er, n))
		{
			buf[2] = 2;
			ppp = 0x03;
		}
		else
		{
			buf[2] = 0;
			ppp = 0x02;
		}
	}
	else
	{
		buf[2] = 0;
		ppp = 0x02;
	}

	memcpy(buf + 3, er->ecm, n);
	insA2[4] = n + 3;

	write_cmd(insA2, buf); // write Header + ECM

	while((cta_res[cta_lr - 2] == 0x98) && // Antwort
		((insCA[4] = cta_res[cta_lr - 1]) > 0) && (insCA[4] != 0xFF))
	{
		write_cmd(insCA, NULL); // Codeword auslesen

		if((cta_res[cta_lr - 2] == 0x98) || ((cta_res[cta_lr - 2] == 0x90)))
		{
			/*checks if answer is encrypted with RSA algo and decrypts it if needed*/
			if(0x81 == cta_res[0] && 2 == cta_res[2] >> 5 && 0x03 == ppp) /*81 XX 5X*/
			{
				if(0x00 == cta_res[cta_lr - 1])
					{ rc = RSA_CNX(reader, cta_res, reader->rsa_mod, exp, cta_lr, 64u, 3u); }
				else
					{ rc = -4; } /*card has no right to decode this channel*/
			}
			else if(0x01 == ppp)
			{
				if(0x00 == cta_res[cta_lr - 1])
				{
					/*trying to decode using CWPK*/
					rc = CWPK_CNX(reader, cta_res);		/*enabled when no loging needed*/
				}
				else
				{
					rc = -4;
				}
			}

			if(0 == rc)
			{
				for(i = 0; i < cta_lr - 2 && num_dw < 2; i += cta_res[i + 1] + 2)
				{
					switch(cta_res[i])
					{
						case 0x25:
							if((cta_res[i + 1] >= 0xD) && !((n = cta_res[i + 4]) & 0xFE))
							{
								rc |= (1 << n);
								memcpy(ea->cw + (n << 3), cta_res + i + 7, 8);
								++num_dw;
							}
							break;

						case 0x31:
							if((cta_res[i + 1] == 0x02 && cta_res[i + 2] == 0x00 && cta_res[i + 3] == 0x00) || \
									(cta_res[i + 1] == 0x02 && cta_res[i + 2] == 0x40 && cta_res[i + 3] == 0x00))
								{ break; }
							else if(strcmp(reader->pincode, "none"))
							{
								conax_send_pin(reader);
								write_cmd(insA2, buf); // write Header + ECM

								while((cta_res[cta_lr - 2] == 0x98) && // Antwort
									((insCA[4] = cta_res[cta_lr - 1]) > 0) && (insCA[4] != 0xFF))
								{
									write_cmd(insCA, NULL); // Codeword auslesen

									if((cta_res[cta_lr - 2] == 0x98) ||
											((cta_res[cta_lr - 2] == 0x90) && (!cta_res[cta_lr - 1])))
									{
										for(j = 0; j < cta_lr - 2; j += cta_res[j + 1] + 2)
										{
											if((cta_res[j] == 0x25) && // access: is cw
													(cta_res[j + 1] >= 0xD) && // 0xD: 5 header + 8 cw
													!((n = cta_res[j + 4]) & 0xFE)) // cw idx must be 0 or 1
											{
												rc |= (1 << n);
												memcpy(ea->cw + (n << 3), cta_res + j + 7, 8);
												++num_dw;
											}
										}
									}
								}
							}
							break;
					}
				}
			}
		}
	}

	switch(rc)
	{
		case -1:
			rdr_log(reader, "conax decode ECM problem - RSA key is probably faulty");
			break;

		case -2:
			rdr_log(reader, "conax RSA pairing - wrong size of data");
			break;

		case -3:
			rdr_log(reader, "conax RSA pairing- wrong size of data for second round");
			/* fallthrough */

		case -4:
			rdr_log(reader, "card has no right to decode this channel");
			break;

		case -8:
			rdr_log(reader, "CWPK is faulty");
			break;
	}

	/* answer 9011 - conax smart card need reset */
	if(2 <= cta_lr && 0x90 == cta_res[cta_lr - 2] && 0x11 == cta_res[cta_lr - 1])
	{
		rdr_log(reader, "conax card hangs - reset is required");
		reader->card_status = UNKNOWN;
	}

	if(rc == 3)
		{ return OK; }
	else
		{ return ERROR; }
}

static int32_t conax_get_emm_type(EMM_PACKET *ep, struct s_reader *rdr)
{
	int32_t i, ok = 0;
	char tmp_dbg[17];

	rdr_log_dbg(rdr, D_EMM, "Entered conax_get_emm_type ep->emm[2]=%02x", ep->emm[2]);

	for(i = 0; i < rdr->nprov; i++)
	{
		ok = (!memcmp(&ep->emm[6], rdr->sa[i], 4));
		if(ok) { break; }
	}

	if(ok)
	{
		ep->type = SHARED;
		memset(ep->hexserial, 0, 8);
		memcpy(ep->hexserial, &ep->emm[6], 4);
		rdr_log_dbg_sensitive(rdr, D_EMM, "SHARED, ep->hexserial = {%s}",
						cs_hexdump(1, ep->hexserial, 8, tmp_dbg, sizeof(tmp_dbg)));
		return 1;
	}
	else
	{
		if(!memcmp(&ep->emm[6], rdr->hexserial + 2, 4))
		{
			ep->type = UNIQUE;
			memset(ep->hexserial, 0, 8);
			memcpy(ep->hexserial + 2, &ep->emm[6], 4);
			rdr_log_dbg_sensitive(rdr, D_EMM, "UNIQUE, ep->hexserial = {%s}",
							cs_hexdump(1, ep->hexserial, 8, tmp_dbg, sizeof(tmp_dbg)));
			return 1;
		}
		else
		{
			ep->type = GLOBAL;
			rdr_log_dbg(rdr, D_EMM, "GLOBAL");
			memset(ep->hexserial, 0, 8);
			return 1;
		}
	}
}

static int32_t conax_get_emm_filter(struct s_reader *rdr, struct s_csystem_emm_filter **emm_filters, unsigned int *filter_count)
{
	if(*emm_filters == NULL)
	{
		const unsigned int max_filter_count = 2 + rdr->nprov;
		if(!cs_malloc(emm_filters, max_filter_count * sizeof(struct s_csystem_emm_filter)))
			{ return ERROR; }

		struct s_csystem_emm_filter *filters = *emm_filters;
		*filter_count = 0;

		int idx = 0, prov;

		filters[idx].type = EMM_GLOBAL;
		filters[idx].enabled = 0; // FIXME: dont see any conax global EMM yet
		filters[idx].filter[0] = 0x82;
		filters[idx].mask[0] = 0xFF;
		filters[idx].filter[8] = 0x70;
		filters[idx].mask[8] = 0xFF;
		idx++;

		for(prov = 0; prov < rdr->nprov; prov++)
		{
			filters[idx].type = EMM_SHARED;
			filters[idx].enabled = 1;
			filters[idx].filter[0] = 0x82;
			filters[idx].mask[0] = 0xFF;
			memcpy(&filters[idx].filter[4], rdr->sa[prov], 4);
			memset(&filters[idx].mask[4], 0xFF, 4);
			idx++;
		}

		filters[idx].type = EMM_UNIQUE;
		filters[idx].enabled = 1;
		filters[idx].filter[0] = 0x82;
		filters[idx].mask[0] = 0xFF;
		memcpy(&filters[idx].filter[4], rdr->hexserial + 2, 4);
		memset(&filters[idx].mask[4], 0xFF, 4);
		idx++;

		*filter_count = idx;
	}

	return OK;
}

static int32_t conax_do_emm(struct s_reader *reader, EMM_PACKET *ep)
{
	def_resp;
	uint8_t insCA[] = { 0xDD, 0xCA, 0x00, 0x00, 0x00 };
	uint8_t insEMM[] = { 0xDD, 0x84, 0x00, 0x00, 0x00 };
	uint8_t buf[255];
	int32_t rc = 0;

	const int32_t l = ep->emm[2];

	insEMM[4] = l + 5;
	buf[0] = 0x12;
	buf[1] = l + 3;
	memcpy(buf + 2, ep->emm, buf[1]);
	write_cmd(insEMM, buf);

	if(cta_res[0] == 0x98)
	{
		insCA[4] = cta_res[1];
		write_cmd(insCA, NULL);
	}

	rc = ((cta_res[0] == 0x90) && (cta_res[1] == 0x00));

	if(rc)
		{ return OK; }
	else
		{ return ERROR; }
}

static int32_t conax_card_info(struct s_reader *reader)
{
	def_resp;
	int32_t type, i, j, k = 0, n = 0, l;
	uint16_t provid = 0;
	char provname[32], pdate[32], chid[32];
	static const uint8_t insC6[] = { 0xDD, 0xC6, 0x00, 0x00, 0x03, 0x1C, 0x01, 0x00 };
	static const uint8_t ins26[] = { 0xDD, 0x26, 0x00, 0x00, 0x03, 0x1C, 0x01, 0x01 };
	uint8_t insCA[] = { 0xDD, 0xCA, 0x00, 0x00, 0x00 };
	char *txt[] = { "Package", "PPV-Event" };
	static const uint8_t *cmd[] = { insC6, ins26 };
	time_t start_t = 0, end_t = 0;
	uint32_t cxclass = 0;

	cs_clear_entitlement(reader); // reset the entitlements

	for(type = 0; type < 2; type++)
	{
		n = 0;
		write_cmd(cmd[type], cmd[type] + 5);
		while(cta_res[cta_lr - 2] == 0x98)
		{
			insCA[4] = cta_res[cta_lr - 1]; // get len
			write_cmd(insCA, NULL); // read

			if((cta_res[cta_lr - 2] == 0x90) || (cta_res[cta_lr - 2] == 0x98))
			{
				for(j = 0; j < cta_lr - 2; j += cta_res[j + 1] + 2)
				{
					provid = (cta_res[j + 2 + type] << 8) | cta_res[j + 3 + type];
					chid[0] = '\0';

					for(k = 0, i = j + 4 + type; (i < j + cta_res[j + 1]); i += cta_res[i + 1] + 2)
					{
						switch(cta_res[i])
						{
							case 0x01:
								l = (cta_res[i + 1] < (sizeof(provname) - 1)) ? cta_res[i + 1] : sizeof(provname) - 1;
								memcpy(provname, cta_res + i + 2, l);
								provname[l] = '\0';
								break;

							case 0x30:
								if(k > 1)
								{
									rdr_log(reader, "%s: %d, id: %04X%s, date: %s - %s, name: %s",
											txt[type], ++n, provid, chid, pdate, pdate + 16, trim(provname));

									// add entitlements to list
									cs_add_entitlement(reader, reader->caid, b2ll(4, reader->prid[0]),
											provid, cxclass, start_t, end_t, type + 1, 1);

									k = 0;
									chid[0] = '\0';
								}
								if(k == 0) { start_t = chid_date(cta_res + i + 2, pdate, 15); }
								else { end_t = chid_date(cta_res + i + 2, pdate + 16, 15) /* add 23:59:59 here: */ + 0x1517F; }
								++k;
								break;

							case 0x20: // Provider classes
							case 0x90: // (?) not sure what this is, saw it once in log
								snprintf(chid, sizeof(chid), ", classes: %02X%02X%02X%02X",
										cta_res[i + 2], cta_res[i + 3], cta_res[i + 4] , cta_res[i + 5]);
								cxclass = b2ll(4, &cta_res[i + 2]);
								break;
						}
					}
					rdr_log(reader, "%s: %d, id: %04X%s, date: %s - %s, name: %s",
							txt[type], ++n, provid, chid, pdate, pdate + 16, trim(provname));

					// add entitlements to list
					cs_add_entitlement(reader, reader->caid, b2ll(4, reader->prid[0]),
							provid, cxclass, start_t, end_t, type + 1, 1);
				}
			}
		}
	}
	rdr_log(reader, "ready for requests");
	return OK;
}

const struct s_cardsystem reader_conax =
{
	.desc           = "conax",
	.caids          = (uint16_t[]){ 0x0B, 0 },
	.do_emm         = conax_do_emm,
	.do_ecm         = conax_do_ecm,
	.card_info      = conax_card_info,
	.card_init      = conax_card_init,
	.get_emm_type   = conax_get_emm_type,
	.get_emm_filter = conax_get_emm_filter,
};

#endif
