Opened 14 years ago

Closed 14 years ago

#48 closed enhancement (duplicate)

HD+ Support

Reported by: madman Owned by:
Priority: critical Component:
Severity: high Keywords:
Cc: Sensitive: no

Description

From Streamboard. Could you please add this to make a public test version for hd+ ?

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include <openssl/rand.h>

#include "common.h"
#include "system.h"
#include "system-common.h"
#include "smartcard.h"
#include "crypto.h"
#include "data.h"
#include "misc.h"
#include "parse.h"

//DEBUG_CAMCRYPT
#ifdef DEBUG_CAMCRYPT
#define dcc(x) { x; }
#else
#define dcc(x) ;
#endif

#define SYSTEM_NAME          "SC-Nagra2"
#define SYSTEM_PRI           -5

#define SC_NAME "Nagra2"
#define SC_ID   MAKE_SC_ID('N','g','r','2')



// -- cSystemScNagra2 ----------------------------------------------------------

class cSystemScNagra2 : public cSystemScCore 
{
public:
	cSystemScNagra2(void);
};

cSystemScNagra2::cSystemScNagra2(void)
:cSystemScCore(SYSTEM_NAME,SYSTEM_PRI,SC_ID,"SC Nagra2")
{
	hasLogger=true;
}

// -- cSystemLinkScNagra2 ------------------------------------------------------

class cSystemLinkScNagra2 : public cSystemLink 
{
public:
	cSystemLinkScNagra2(void);
	virtual bool CanHandle(unsigned short SysId);
	virtual cSystem *Create(void) { return new cSystemScNagra2; }
};

static cSystemLinkScNagra2 staticInit;

cSystemLinkScNagra2::cSystemLinkScNagra2(void) : cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
{
	Feature.NeedsSmartCard();
}

bool cSystemLinkScNagra2::CanHandle(unsigned short SysId)
{
	bool res=false;
	cSmartCard *card=smartcards.LockCard(SC_ID);
	if(card) 
	{
		res=card->CanHandle(SysId);
		smartcards.ReleaseCard(card);
	}
	return res;
}


// -- cSmartCardDataNagra2 -----------------------------------------------------

class cSmartCardDataNagra2 : public cSmartCardData 
{
public:
	int caid;
	unsigned char camid[4];
	unsigned char boxke[8];
	unsigned char negok[64];
	//
	cSmartCardDataNagra2(void);
	cSmartCardDataNagra2(unsigned char camid[4]);
	virtual bool Parse(const char *line);
	virtual bool Matches(cSmartCardData *param);
};

cSmartCardDataNagra2::cSmartCardDataNagra2(void) : cSmartCardData(SC_ID) {}

cSmartCardDataNagra2::cSmartCardDataNagra2(unsigned char CAMid[4]) : cSmartCardData(SC_ID)
{
	memcpy(camid,CAMid,4);
}

bool cSmartCardDataNagra2::Matches(cSmartCardData *param)
{
	cSmartCardDataNagra2 *cd=(cSmartCardDataNagra2 *)param;
	return !memcmp(cd->camid,camid,4);
}

bool cSmartCardDataNagra2::Parse(const char *line)
{
	unsigned char buff[512];
	memset(camid,0,4);memset(boxke,0,8);memset(negok,0,64);
	caid=1802; // default
	line=skipspace(line);
	if(*line=='[') 
	{ // parse camid & caid
		line++;
		if(GetHex(line,buff,4)!=4) 
		{
			d(printf("smartcarddatanagra2: format error: camid\n"))
			return false;
		}
		memcpy(camid,buff,4);

		line=skipspace(line);
		if(*line=='/') {
			line++;
			if(GetHex(line,buff,2)!=2) 
			{
				d(printf("smartcarddatanagra2: format error: caid\n"))
				return false;
			}
			caid=buff[0]*256+buff[1];
			
			line=skipspace(line);
		}

		if(!*line==']') 
		{
			d(printf("smartcarddatanagra2: format error: closing ]\n"))
			return false;
		}
		line++;
	}

	line=skipspace(line);

	int l;

	if((l=GetHex(line,buff,sizeof(buff),false))!=8) 
	{
		d(printf("smartcarddatanagra2: format error: boxkey\n"))
		return false;
	}
	memcpy(boxke,buff,8);

	if((l=GetHex(line,buff,sizeof(buff),false))!=64) 
	{
		d(printf("smartcarddatanagra2: format error: negok\n"))
		return false;
	}
	memcpy(negok,buff,64);

	return true;
}

// -- cSmartCardNagra2 ---------------------------------------------------------

#define ADDRLEN      4    // Address length in EMM commands
#define MAX_PROV     16
#define RECOVER_TIME 100  // Time in ms which the card needs to recover after
// a failed command

static const struct StatusMsg msgs[] = 
{
	{ { 0x00,0x00 }, "Instruction executed without error", true },
	{ { 0x55,0x00 }, "Instruction executed without error", true },
	{ { 0x57,0x00 }, "CAM string rejected", false },
	{ { 0x58,0x00 }, "Instruction executed without error", true },
	{ { 0x9D,0x00 }, "Decoding successfull", true },
	{ { 0x90,0x00 }, "ChID missing. Not subscribed?", false },
	{ { 0x93,0x00 }, "ChID out of date. Subscription expired?", false },
	{ { 0x9C,0x00 }, "Master key error", false },
	{ { 0x9E,0x00 }, "Wrong decryption key", false },
	{ { 0x9F,0x00 }, "Missing key", false },
	{ { 0x70,0x00 }, "Wrong hex serial", false },
	{ { 0x71,0x00 }, "Wrong provider", false },
	{ { 0x72,0x00 }, "Wrong provider group", false },
	{ { 0x73,0x00 }, "Wrong provider group", false },
	{ { 0x7C,0x00 }, "Wrong signature", false },
	{ { 0x7D,0x00 }, "Masterkey missing", false },
	{ { 0x7E,0x00 }, "Wrong provider identifier", false },
	{ { 0x7F,0x00 }, "Invalid nano", false },
	{ { 0x54,0x00 }, "No more ChID's", true },
	{ { 0xFF,0xFF }, 0, false }
};

static const struct CardConfig cardCfg = 
{
	SM_8O2,3000,100
};


class cSmartCardNagra2 : public cSmartCard, private cIdSet 
{
private:
	unsigned char buff[MAX_LEN+1];
	unsigned char camid[4];
	unsigned char boxke[8];
	unsigned char negok[64];
	IDEA_KEY_SCHEDULE ksSession;
	int caid;
	cTimeMs recoverTime;
	//
	static void ReverseMem(unsigned char *vIn, int len);
	void Signature(unsigned char *sig, const unsigned char *vkey,const unsigned char *msg, int len);
	bool NegotiateSessionKey(void);
	int DoCmd(unsigned char *cmd);
public:
	cSmartCardNagra2(void);
	virtual bool Init(void);
	virtual bool DumpDatatypes(void);
	virtual bool DumpDatatype(unsigned char dtbyte);
	virtual bool Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw);
	virtual bool Update(int pid, int caid, const unsigned char *data);
	virtual bool CanHandle(unsigned short CaId);
};

cSmartCardNagra2::cSmartCardNagra2(void) : cSmartCard(&cardCfg,msgs)
{
	memset(camid,0,4);memset(boxke,0,8);memset(negok,0,64);
}

void cSmartCardNagra2::ReverseMem(unsigned char *vIn, int len)
{
	unsigned char temp;
	for(int i=0; i < (len/2); i++)
	{
		temp = vIn[i];
		vIn[i] = vIn[len-i-1];
		vIn[len-i-1] = temp;
	}
}

void cSmartCardNagra2::Signature(unsigned char *sig, const unsigned char *vkey,const unsigned char *msg, int len)
{
	IDEA_KEY_SCHEDULE ks;
	unsigned char v[8];
	unsigned char b200[16];
	unsigned char b0f0[8];
	memcpy(b200,vkey,sizeof(b200));
	for(int i=0; i<len; i+=8)
	{
		idea_set_encrypt_key(b200,&ks);
		memset(v,0,sizeof(v));
		idea_cbc_encrypt(msg+i,b0f0,8,&ks,v,IDEA_DECRYPT);
		for(int j=7; j>=0; j--) b0f0[j]^=msg[i+j];
		memcpy(b200+0,b0f0,8);
		memcpy(b200+8,b0f0,8);
	}
	memcpy(sig,b0f0,8);

	//printf("SIGNATURE: ");DUMP(vkey,16);DUMP(msg,len);DUMP(sig,8);
	return;
}

bool cSmartCardNagra2::NegotiateSessionKey(void)
{
	unsigned char cmd2a[] = {0x21, 0x00, 0x08, 0xA0, 0xCA, 0x00, 0x00, 0x02, 0x2A, 0x00, 0x42, 0x00};
	unsigned char cmd2b[] = {0x21, 0x40, 0x48, 0xA0, 0xCA, 0x00, 0x00, 0x42, 0x2B, 0x40, 0x27, 0x54, 0xd1, 0x26, 0xe7, 0xe2, 0x40, 0x20, 0xd1, 0x66, 0xf4, 0x18, 0x97, 0x9d, 0x5f, 0x16, 0x8f, 0x7f, 0x7a, 0x55, 0x15, 0x82, 0x31, 0x14, 0x06, 0x57, 0x1a, 0x3f, 0xf0, 0x75, 0x62, 0x41, 0xc2, 0x84, 0xda, 0x4c, 0x2e, 0x84, 0xe9, 0x29, 0x13, 0x81, 0xee, 0xd6, 0xa9, 0xf5, 0xe9, 0xdb, 0xaf, 0x22, 0x51, 0x3d, 0x44, 0xb3, 0x20, 0x83, 0xde, 0xcb, 0x5f, 0x35, 0x2b, 0xb0, 0xce, 0x70, 0x02, 0x00};

	unsigned char negot[64];
	unsigned char idea1[16];
	unsigned char idea2[16];
	unsigned char sign1[8];
	unsigned char sign2[8];
	unsigned char sessi[16];

	int r;

	if((r=DoCmd(cmd2a))<=0 || !Status()) 
	{
		di(printf("smartcardnagra2: CMD$2A failed\n"))
		return false;
	}

	{
		ReverseMem(buff+5, 64);
		unsigned char vFixed[] = {0,1,2,3};
		BN_CTX *ctx = BN_CTX_new();
		BIGNUM *bnN = BN_CTX_get(ctx);
		BIGNUM *bnE = BN_CTX_get(ctx);
		BIGNUM *bnCT = BN_CTX_get(ctx);
		BIGNUM *bnPT = BN_CTX_get(ctx);
		BN_bin2bn(negok, 64, bnN);
		BN_bin2bn(vFixed+3, 1, bnE);
		BN_bin2bn(buff+5, 64, bnCT);
		BN_mod_exp(bnPT, bnCT, bnE, bnN, ctx);
		memset(negot, 0, 64);
		BN_bn2bin(bnPT, negot + (64-BN_num_bytes(bnPT)));

		BN_bin2bn(negot, 64, bnCT);
		BN_mod_exp(bnPT, bnCT, bnE, bnN, ctx);
		memset(cmd2b+10, 0, 64);
		BN_bn2bin(bnPT, cmd2b+10 + (64-BN_num_bytes(bnPT)));
		BN_CTX_end(ctx);
		ReverseMem(cmd2b+10, 64);
		ReverseMem(negot, 64);

		memcpy(idea1, boxke, 8); memcpy(idea1+8, camid, 4); idea1[12] = ~camid[0]; idea1[13] = ~camid[1]; idea1[14] = ~camid[2]; idea1[15] = ~camid[3];
		Signature(sign1, idea1, negot, 32);
		memcpy(idea2,sign1,8); memcpy(idea2+8,sign1,8); 
		Signature(sign2, idea2, negot, 32);
		memcpy(sessi,sign1,8); memcpy(sessi+8,sign2,8);
		IDEA_KEY_SCHEDULE ks;
		idea_set_encrypt_key(sessi,&ks);
		idea_set_decrypt_key(&ks,&ksSession);
	}

	if((r=DoCmd(cmd2b))<=0 || !Status()) 
	{
		di(printf("smartcardnagra2: CMD$2B failed\n"))
		return false;
	}

	di(printf("smartcardnagra2: Session Key "))
	DUMP(sessi, 16);
	return true;
}

bool cSmartCardNagra2::DumpDatatype(unsigned char dtbyte)
{
	static unsigned char cmd22[] = {0x21, 0x00, 0x09, 0xA0, 0xCA, 0x00, 0x00, 0x03, 0x22, 0x01, 0x00, 0x07, 0x3E};
	int r;
	unsigned char dtactualsize;
	cmd22[0x0A] = dtbyte;

	while(true)
	{
		cmd22[0x0B] = 7;

        	if((r=DoCmd(cmd22))<=0 || !Status() || r!=13)
        	{
			di(printf("smartcardnagra2: DT read failed\n"))
                	return false;
		}

		dtactualsize = buff[5];
		if(!dtactualsize) break;

		cmd22[0x0A] = dtbyte | 0xC0;
		cmd22[0x0B] = dtactualsize + 3;

        	if((r=DoCmd(cmd22))<=0 || !Status() || r!=(dtactualsize+9))
        	{
			di(printf("smartcardnagra2: DT read failed\n"))
			return false;
		}

		cmd22[0x0A] = dtbyte | 0x80;
	}

	return true;
}

bool cSmartCardNagra2::DumpDatatypes(void)
{
	static unsigned char cmdc7[] = {0x21, 0x00, 0x08, 0xA0, 0xCA, 0x00, 0x00, 0x02, 0xC7, 0x00, 0x04, 0x82};
	int r;
	unsigned char dtbyte = 0;
	unsigned short dtflags;

	while(true)
	{
	        if((r=DoCmd(cmdc7))<=0 || !Status() || r!=10)
        	{
                	di(printf("smartcardnagra2: Determine updated DTs failed\n"))
                	return false;
        	}

		memcpy(&dtflags,buff+5,2);
		if(!dtflags)
			break;
	
		for(dtbyte = 0; dtbyte < 16; dtbyte++)
			if(dtflags & (0x01 << dtbyte))
				DumpDatatype(dtbyte);
	}
	return true;
}

bool cSmartCardNagra2::Init(void)
{
	ResetIdSet();
	recoverTime.Set(-RECOVER_TIME);
	if(atr->histLen<5 || memcmp(atr->hist,"DNASP",5)) 
	{
		di(printf("smartcardnagra2: doesn't looks like a Nagra2 card\n"))
		return false;
	}

	infoStr.Begin();
	infoStr.Strcat("Nagra2 smartcard\n");
	int r;

	static unsigned char cmdsz[] = {0x21, 0xC1, 0x01, 0xA0, 0x41};
	if((r=DoCmd(cmdsz))<=0 || !Status() || r!=5) 
	{
		di(printf("smartcardnagra2: Set CMD maximum lenght failed\n"))
		return false;
	}

	static unsigned char cmdc0[] = {0x21, 0x00, 0x08, 0xA0, 0xCA, 0x00, 0x00, 0x02, 0xC0, 0x00, 0x06, 0x87};
	if((r=DoCmd(cmdc0))<=0 || !Status() || r!=12) 
	{
		di(printf("smartcardnagra2: Query CAM status failed\n"))
		return false;
	}

	DumpDatatypes();

	static unsigned char cmd12[] = {0x21, 0x40, 0x08, 0xA0, 0xCA, 0x00, 0x00, 0x02, 0x12, 0x00, 0x06, 0x15};
	if((r=DoCmd(cmd12))<=0 || !Status() || r!=12) 
	{
		di(printf("smartcardnagra2: Query CAMID failed\n"))
		return false;
	}
	memcpy(camid, buff+5, 4);

	cSmartCardDataNagra2 *entry=0;
	cSmartCardDataNagra2 cd(camid);
	if(entry=(cSmartCardDataNagra2 *)smartcards.FindCardData(&cd))
	{
		//printf("smartcardnagra2: CAID %X\n",entry->caid);
		caid = entry->caid;
		memcpy(boxke,entry->boxke,8);
		memcpy(negok,entry->negok,64);
	}
	else
	{
		di(printf("smartcardnagra2: No configuration for this card found\n"))
		return false;
	}

	if(!NegotiateSessionKey())
		return false;

	DumpDatatypes();

	infoStr.Finish();
	return true;
}

bool cSmartCardNagra2::CanHandle(unsigned short CaId)
{
	return (CaId==caid);
}

bool cSmartCardNagra2::Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
{
	int r = 0;

	if(!NegotiateSessionKey())
		return false;

	unsigned char cmd07[8+0x8F+1+1];
	static const unsigned char cmd07Head[] = {0x21, 0x00, 0x95, 0xA0, 0xCA, 0x00, 0x00, 0x8F};
	memcpy(cmd07,cmd07Head,sizeof(cmd07Head));
	memcpy(cmd07+8, data+3, 0x8F);
	cmd07[8+0x95] = 0x02;
	if( (r=DoCmd(cmd07))<=0 || !Status() || r!=8) return false;
	recoverTime.Set();

	static unsigned char cmdc0[] = {0x21, 0x00, 0x08, 0xA0, 0xCA, 0x00, 0x00, 0x02, 0xC0, 0x00, 0x06, 0x87};
	for(int retry = 0; ; retry++)
	{
		if( (r=DoCmd(cmdc0))>0 && Status() && r==12) break;
		if( retry == 7 ) return false;
	}

	if( (buff[8] & 0x06) != 0x06 )
		return false;

	unsigned char cmd1c[] = {0x21, 0x00, 0x08, 0xA0, 0xCA, 0x00, 0x00, 0x02, 0x1C, 0x00, 0x36, 0x6B};
	if( (r=DoCmd(cmd1c))<=0 || !Status() || r!=60) return false;

	unsigned char v[8];
	memset(v,0,sizeof(v));
	idea_cbc_encrypt(buff+7 ,cw+8,8,&ksSession,v,IDEA_DECRYPT);
	memset(v,0,sizeof(v));
	idea_cbc_encrypt(buff+33,cw+0,8,&ksSession,v,IDEA_DECRYPT);

	return true;
}

bool cSmartCardNagra2::Update(int pid, int caid, const unsigned char *data)
{
	int r = 0;
	unsigned char cmd04[113] = {0x21, 0x00, 0x6D, 0xA0, 0xCA, 0x00, 0x00, 0x67};
	cmd04[111] = 0x02;

	if( (data[1]!=0x70) || (data[2]!=0x6C) || (data[9]!=0x65) )
		return false;

	int emmtype = -1;
	//if( (data[0]==0x82) && ((data[3] | data[4] | data [5] | data[6]) == 0x00) ) emmtype = 0;
	//if( (data[0]==0x83) && ((data[3] | data[4] | data [5]) != 0x00) && (data[6] == 0) && (data[7]==0x10) ) emmtype = 1;
	//if( (data[0]==0x83) && ((data[3] | data[4] | data [5]) != 0x00) && (data[6] != 0) && (data[7]==0x00) ) emmtype = 2;
	if( (data[0]==0x82) ) emmtype = 0;
	if( (data[0]==0x83) && (data[7]==0x10) ) emmtype = 1;
	if( (data[0]==0x83) && (data[7]==0x00) ) emmtype = 2;

	if( emmtype < 0)
	{
		di(printf("\nNAGRA2: EMM %i\n", emmtype))
		DUMP(data,111);
		return false;
	}
	if( (emmtype > 0) && ( (camid[0] != data[5]) || (camid[1] != data[4]) || (camid[2] != data[3]) ) )
		return false;
	if( (emmtype == 2) && (camid[3] != data[6]) )
		return false;

	//di(printf("\nNAGRA2: EMM %i\n", emmtype))

	memcpy(cmd04+8, data+8, 0x67);


	// Beg Hardcoded blockers
	//
	//if(emmtype == 2) return false;	// No "U" commands
	//if(emmtype == 1) return false;	// No "S" commands
	//if(emmtype == 0) return false;	// No "G" commands
	//
	// End Hardcoded blockers


	if( (r=DoCmd(cmd04))>0 && Status() && r==8) return true;

	return false;
}

int cSmartCardNagra2::DoCmd(unsigned char *cmd)
{
	int len=cmd[2]+3;
	cmd[len]=XorSum(cmd,len);
	// wait until recover time is over
	int r=RECOVER_TIME-recoverTime.Elapsed();
	if(r>0) 
	{
		di(printf("NAGRA2: recover time, waiting %d ms\n",r))
		cCondWait::SleepMs(r+1);
	}
	di(printf("NAGRA2: CMD ->"))
	DUMP(cmd,len+1);
	r=-1;
	if( SerWrite(cmd,len+1)>0 && (len = SerRead(buff,3,cardCfg.workTO))==3 ) 
	{
		if(SerRead(buff+len,buff[2]+1)<=0) 
		{
			recoverTime.Set();
			di(printf("NAGRA2: setting %d ms recover time\n",RECOVER_TIME))
			return -1;
		}
		len+=buff[2]+1;

		if( (XorSum(buff,len)==0x00) && (buff[0] == 0x12) )
		{
			r=len;
		}
	}

	di(printf("NAGRA2: RES <-"))
	DUMP(buff,len);

	if(r<=0) 
	{
		recoverTime.Set();
		di(printf("NAGRA2: setting %d ms recover time\n",RECOVER_TIME))
	}

	return r;
}

// -- cSmartCardLinkNagra2 -----------------------------------------------------

class cSmartCardLinkNagra2 : public cSmartCardLink 
{
public:
	cSmartCardLinkNagra2(void):cSmartCardLink(SC_NAME,SC_ID) {}
	virtual cSmartCard *Create(void) { return new cSmartCardNagra2(); }
	virtual cSmartCardData *CreateData(void) { return new cSmartCardDataNagra2; }
};

static cSmartCardLinkNagra2 staticScInit;




Change History (1)

comment:1 by landlord, 14 years ago

Resolution: duplicate
Status: newclosed

Same ticket as 46. HD+ is based on Nagra N3.

Note: See TracTickets for help on using tickets.