/*
 * Softcam plugin to VDR (C++)
 *
 * This code is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
 */

#include "stdafx.h"
#include <stdlib.h>

#include <string.h>

#include <openssl/bn.h>

#include "common.h"
#include "system-common.h"
#include "smartcard.h"
#include "misc.h"

//#define DEBUG_MSG
#ifdef DEBUG_MSG
#define dm(x) { (x); }
#include <unistd.h>
#else
#define dm(x) ; 
#endif

// -- cHexKey ------------------------------------------------------------------

cHexKey::cHexKey(bool Super)
:cPlainKey(Super)
{
	key=0;
}

cHexKey::~cHexKey()
{
	free(key);
}

bool cHexKey::SetKey(void *Key, int Keylen)
{
	keylen=Keylen;
	free(key); key=malloc(keylen);
	if(key) memcpy(key,Key,keylen);
	return key!=0;
}

bool cHexKey::Cmp(void *Key, int Keylen)
{
	return keylen==Keylen && memcmp(key,Key,keylen)==0;
}

bool cHexKey::Cmp(cPlainKey *k)
{
	cHexKey *hk=dynamic_cast<cHexKey *>(k); // downcast
	return hk && Cmp(hk->key,hk->keylen);
}

void cHexKey::Get(void *mem)
{
	memcpy(mem,key,keylen);
}

cString cHexKey::Print(void)
{
	char *str=(char *)malloc(keylen*2+2);
	if(str) HexStr(str,(unsigned char *)key,keylen);
	return cString(str,true);
}

// -- cBNKey -------------------------------------------------------------------

cBNKey::cBNKey(bool Super)
:cPlainKey(Super)
{
	key=0;
}

cBNKey::~cBNKey()
{
	if(key) BN_free((BIGNUM *)key);
}

bool cBNKey::SetKey(void *Key ,int Keylen)
{
	Rotate(Key,Keylen);
	keylen=Keylen;
	if(key) BN_free((BIGNUM *)key);
	key=BN_bin2bn((unsigned char *)Key,Keylen,0);
	return key!=0;
}

bool cBNKey::Cmp(void *Key, int Keylen)
{
	cBN tmp;
	BN_bin2bn((unsigned char*)Key,Keylen,tmp);
	return (BN_cmp((BIGNUM *)key,tmp)==0);
}

bool cBNKey::Cmp(cPlainKey *k)
{
	cBNKey *bk=dynamic_cast<cBNKey *>(k); // downcast
	return bk && BN_cmp((BIGNUM *)key,(BIGNUM *)bk->key)==0;
}

void cBNKey::Get(void *mem)
{
	BN_copy((BIGNUM *)mem,(BIGNUM *)key);
}

cString cBNKey::Print(void)
{
	//unsigned char mem[keylen*2];
	unsigned char *mem = new unsigned char[keylen];
	int len=BN_bn2bin((BIGNUM *)key,mem);
	if(len<keylen) { // handle short BN
		const int r=keylen-len;
		memmove(mem+r,mem,len);
		memset(mem,0,r);
	}
	Rotate(mem,keylen);
	char *str=(char *)malloc(keylen*2+2);
	if(str) HexStr(str,mem,keylen);
	delete mem;
	return cString(str,true);
}

// -- cBNRKey ------------------------------------------------------------------

cBNRKey::cBNRKey(bool Super)
:cBNKey(Super)
{}

void cBNRKey::Rotate(void *mem, int len)
{
	RotateBytes((unsigned char *)mem,len);
}

// -- cDualKey -----------------------------------------------------------------

cDualKey::cDualKey(bool Super, bool Rotate)
:cMutableKey(Super)
{
	rotate=Rotate;
}

cPlainKey *cDualKey::Alloc(void) const
{
	if(IsBNKey()) {
		if(rotate)	return new cBNRKey(CanSupersede());
		else		return new cBNKey(CanSupersede());
	}
	else			return new cHexKey(CanSupersede());
}

// -- cPlainKeyStd ---------------------------------------------------------------

#define PLAINLEN_STD 8

cPlainKeyStd::cPlainKeyStd(bool Super)
:cHexKey(Super)
{}

bool cPlainKeyStd::Parse(const char *line)
{
	unsigned char sid[3], skeynr, skey[PLAINLEN_STD];
	const char *sline=line;
	int len;
	if(GetChar(line,&type,1) && (len=GetHex(line,sid,3,false)) &&
		GetHex(line,&skeynr,1) && GetHex(line,skey,PLAINLEN_STD)) {
		type=toupper(type); id=Bin2Int(sid,len); keynr=skeynr;
		SetKey(skey,PLAINLEN_STD);
		return true;
	}
	d(printf("plainkeystd: bad key format '%s'\n",sline))
	return false;
}

// -- cSystemScCore ------------------------------------------------------------

cSystemScCore::cSystemScCore(const char *Name, int Pri, int ScId, const char *ScName)
:cSystem(Name,Pri)
{
  scId=ScId; scName=ScName;
}

bool cSystemScCore::ProcessECM(const cEcmInfo *ecm, unsigned char *source)
{
	bool res=false;
	cSmartCard *card=smartcards.LockCard(scId);
	if(card) {
		res=card->Decode(ecm,source,cw);
		smartcards.ReleaseCard(card);
		if(res) KeyOK(scName);
	}
	return res;
}

void cSystemScCore::ProcessEMM(int pid, int caid, unsigned char *buffer)
{
	cSmartCard *card=smartcards.LockCard(scId);
	if(card) {
//		card->Update(pid,caid,buffer);
		smartcards.ReleaseCard(card);
	}
}
