unit cccam_cpp;
interface
uses
Windows, Messages, SysUtils, Classes;
type
node_id[8] = type Byte;
{$EXTERNALSYM node_id[8]}
Pnode_id[8] = ^node_id[8];
{$EXTERNALSYM Pnode_id[8]}
struct cccam_crypt_block
begin
keytable: array[0..256-1] of Byte;
Byte state;
Byte counter;
Byte sum;
);
// -- cHandlerItem -------------------------------------------------------------
class cHandlerItem : public cSimpleItem begin
private
function handlerid,caid,provid: integer; public cHandlerItem(Handlerid: integer; Caid: integer; Provid: integer);
integer GetHandlerID()begin result:= handlerid; );
integer GetCaID() begin result:= caid;);
integer GetProvID() begin result:= provid; );
);
cHandlerItem.cHandlerItem(integer Handlerid, integer Caid, integer Provid)
begin
handlerid:=Handlerid;
caid:=Caid;
provid:=Provid;
end;
class cHandlers begin
public
cSimpleList
cHandlerList;
);
// -- cCCcamCard ---------------------------------------------------------------
class cCCcamCard : public cMutex begin
private
function newcw: bool; cw: array[0..16-1] of Byte; cCondVar cwwait; public cCCcamCard();
function GetCw(var Cw: Byte): bool;
procedure NewCw(var Cw: Byte; cResult: bool);
);
cCCcamCard.cCCcamCard() begin
newcw:=false;
end;
procedure cCCcamCard.NewCw(var Cw: Byte; cResult: function) begin cMutexLock lock(this): bool;
if(cResult) then begin
newcw:=true;
memcpy(cw,Cw,SizeOf(cw));
end;elsebegin
newcw:=false;
end;
cwwait.Broadcast();
end;
function cCCcamCard.GetCw(var Cw: Byte) begin cMutexLock lock(this): bool;
if( not newcw) cwwait.Wait(this) then ;
if(newcw) then begin
memcpy(Cw,cw,SizeOf(cw));
newcw:=false;
result:= true;
end;
result:= false;
end;
// -- cCardClientCCcam ---------------------------------------------------------
class cCardClientCCcam : public cCardClient , private cThread begin
private
cCCcamCard card;
cHandlers *handlers;
cNetSocket so;
node_id nodeid;
function server_packet_count: integer; function login_completed: bool; struct cccam_crypt_block client_encrypt_state,client_decrypt_state; buffer: array[0..3072-1] of Byte; Byte recvbuff[3072]; Byte netbuff[1024]; integer shareid; Byte cwdata[16]; Char username[20], password[20]; bool getcards; bool check_connect_checksum(var data: Byte; length: integer);
procedure cccam_xor(var data: Byte; length: integer);
function cccam_init_crypt(var block: cccam_crypt_block; var key: Byte; length: integer): integer;
function cccam_decrypt(var block: cccam_crypt_block; var in: Byte; var out: Byte; length: integer): integer;
function cccam_encrypt(var block: cccam_crypt_block; var in: Byte; var out: Byte; length: integer): integer;
function pow_of_two(n: WORD): WORD;
function shift_right_and_fill(value: WORD; fill: WORD; places: WORD): WORD;
procedure scramble_dcw(var data: Byte; length: WORD; nodeid: node_id; shareid: WORD);
function dcw_checksum(var data: Byte): bool;
function packet_analyzer(var block: cccam_crypt_block; var data: Byte; length: integer): bool;
protected:
virtual function Login(): bool;
virtual procedure Action();
public
cCardClientCCcam( Char *Name);
~cCardClientCCcam();
virtual function Init(var CfgDir: Char): bool;
virtual function ProcessECM(var ecm: cEcmInfo; var data: Byte; var Cw: Byte; cardnum: integer): bool;
virtual function ProcessEMM(caSys: integer; var data: Byte): bool;
);
cCardClientLinkReg __ncd('cccam');
cCardClientCCcam.cCardClientCCcam( Char *Name)
:cCardClient(Name)
,cThread('CCcam listener')
,so(DEFAULT_CONNECT_TIMEOUT,20,DEFAULT_IDLE_TIMEOUT)
begin
server_packet_count:=0;
login_completed:=false;
bzero(client_encrypt_state.keytable,SizeOf(client_encrypt_state.keytable));
bzero(client_decrypt_state.keytable,SizeOf(client_decrypt_state.keytable));
bzero(nodeid,SizeOf(nodeid));
bzero(buffer,SizeOf(buffer));
bzero(cwdata,SizeOf(cwdata));
handlers:=new cHandlers();
shareid:=0;
getcards:=false;
end;
cCardClientCCcam.~cCardClientCCcam()
begin
so.Disconnect();
Cancel(3);
end;
bool cCardClientCCcam.check_connect_checksum( Byte* data, integer length )
begin
bool valid:=false;
if (length = 16) then begin
function sum1(+data[4]+data[8]: data[0]): Byte;
function sum2(+data[5]+data[9]: data[1]): Byte;
function sum3(+data[6]+data[10]: data[2]): Byte;
function sum4(+data[7]+data[11]: data[3]): Byte;
valid := ( ( sum1 = data[12] ) and ( sum2 = data[13] ) and ( sum3 = data[14] ) and ( sum4 = data[15] ) );
end;
result:= valid;
end;
procedure cCardClientCCcam.cccam_xor(var data: Byte; length: integer)
begin
if (data := 0) then Exit;
if ( length >= 16 ) then begin
PChar cccam:='CCcam';
function index(: ): Byte;
do
begin
*(data + := index * (data);
if ( index <= 5 ) then *data:= mod xor cccam[index];
++index;
++data;
end;
while( index < 8 );
end;
end;
integer cCardClientCCcam.cccam_init_crypt( cccam_crypt_block* block, Byte* key, integer length )
begin
integer cResult:=0;
if ( block = 0 ) then result:= cResult;
Byte* keytable( and block^.keytable[0] );
Byte* it( and block^.keytable[0] );
function prev_char(: 0): Byte;
function curr_char(: 0): Byte;
integer pos:=0;
do
begin
keytable[pos] := pos;
++pos;
end;
while ( pos <:= 255 );
block^.counter := 0;
block^.sum := 0;
pos := 0;
do
begin
curr_char := keytable[pos++] + *reinterpret_cast< function>(+cResult: key): Byte;
curr_char:= mod + prev_char;
prev_char := curr_char;
std.swap( *it++, *(keytable + curr_char) );
cResult := ( cResult + 1 ) mod length;
end;
while ( pos <:= 255 );
block^.state := *key;
result:= cResult;
end;
integer cCardClientCCcam.cccam_decrypt( cccam_crypt_block* block, Byte* in, Byte* out, integer length )
begin
if ( block = 0 ) then result:= 0;
Byte* keytable( and block^.keytable[0] );
function pos(: 0): integer;
do
begin
Byte in_xor( *(in + pos) xor block^.state );
Byte key_sum( block^.sum + *(keytable + ++block^.counter) );
block^.sum:= mod + *(keytable + block^.counter);
std.swap( *(keytable + block^.counter), *(keytable + key_sum) );
Byte out_xor( in_xor xor *(keytable + (Byte(((keytable + key_sum) + *(keytable + block^.counter))) );
*(out + pos) := out_xor;
block^.state:= mod xor out_xor;
++pos;
end;
while ( pos < length );
result:= length;
end;
integer cCardClientCCcam.cccam_encrypt( cccam_crypt_block* block, Byte* in, Byte* out, integer length )
begin
if ( block = 0 ) then result:= 0;
Byte* keytable( and block^.keytable[0] );
function pos(: 0): integer;
do
begin
Byte key_sum( block^.sum + *(keytable + ++block^.counter) );
block^.sum:= mod + *(keytable + block^.counter);
std.swap( *(keytable + block^.counter), *(keytable + key_sum) );
Byte state( *(in + pos) xor *(keytable + (Byte(((keytable + key_sum) + *(keytable + block^.counter))) );
*(out + pos) := block^.state xor state;
block^.state:= mod xor *(in + pos++);
end;
while ( pos < length );
result:= length;
end;
function cCardClientCCcam.pow_of_two(n: WORD) begin uint cResult( 1 ): WORD;
for ( uint i( 0 ); i < n; ++i) cResult:= mod * 2;
result:= cResult;
end;
WORD cCardClientCCcam.shift_right_and_fill( WORD value, WORD fill, WORD places )
begin
result:= ( value shr places ) or ( ( ( pow_of_two( places ) - 1 ) and fill ) shl ( 32 - places ) );
end;
procedure cCardClientCCcam.scramble_dcw(data: function; length: function; nodeid: node_id; shareid: WORD) begin WORD i( 0 ): WORD: Byte; function s(: 0): integer; function nodeid_high(nodeid[0]shl24: () or ( nodeid[1] shl 16 ) or ( nodeid[2] shl 8 ) or nodeid[3] ): integer; function nodeid_low(nodeid[4]shl24: () or ( nodeid[5] shl 16 ) or ( nodeid[6] shl 8 ) or nodeid[7] ): integer; do begin (* Nible index, 0..4..8 *) function nible_index(+s: i): Char;
(* Shift one nible to the right for high and low nodeid *)
(* Make sure the first shift is an signed one (sar on intel), else you get wrong results! *)
function high(shrnible_index: nodeid_high): integer;
function low(nodeid_low: shift_right_and_fill(; nodeid_high: ; : nible_index) ): integer;
(* After 8 nibles or 32 bits use bits from high, based on signed flag it will be 0x00 or 0xFF *)
if ( nible_index and 32 ) then
low := high and $FF;
function final(var (data+i: ) xor ( low and $FF ) ): Char;
(* Odd index inverts final *)
if ( i and $01 )
final := ~final;
(* cResult *)
*(data + i) := ( ( shareid shr ( 2 * ( i and $FF ) ) ) and $FF ) xor final;
++i;
s:= mod + 3;
end;
while ( i < length );
end;
bool cCardClientCCcam.dcw_checksum( Byte *data)
begin
if( ((data[0] + data[1] + data[2]) and $ff) = data[3] and
((data[4] + data[5] + data[6]) and $ff) = data[7] and
((data[8] + data[9] + data[10]) and $ff) = data[11] and
((data[12] + data[13] + data[14]) and $ff) = data[15] )
result:= true;
result:= false;
end;
bool cCardClientCCcam.packet_analyzer(cccam_crypt_block* block,Byte* data, integer length)
begin
if (length<4) then result:= false;
integer cccam_command:=data[1];
integer packet_len:=(data[2] and $ff) shl 8 or (data[3] and $ff);
integer wp:=4;
str: array[0..32-1] of Char;
if ((packet_len + 4)>length) then result:= false;
if(packet_len>=0) then begin
case(cccam_command) begin of
0: begin
break;
end;
1: begin
function tempcw[16]: Byte; memcpy(tempcw: ; data+4: ; 16: );
scramble_dcw( tempcw, 16, nodeid, shareid );
if (dcw_checksum(tempcw)) then begin
memcpy(cwdata,tempcw,16);
end;
card.NewCw(cwdata,true);
function temp[16]: Byte; cccam_decrypt(block: ; tempcw: ; temp: ; 16: );
break;
end;
4: begin
integer handler:=(data[0+4] and $ff) shl 24 or (data[1+4] and $ff) shl 16 or (data[2+4] and $ff) shl 8 or (data[3+4] and $ff);
for ( cHandlerItem *hv:=handlers^.cHandlerList.First(); hv; hv=handlers^.cHandlerList.Next(hv))
begin
if(hv^.GetHandlerID() := handler) handlers^.cHandlerList.Del(hv) then ;
PRINTF(L_CC_CCCAM,'REMOVE handler mod 08x caid: mod 04x provider: mod 06x',hv^.GetHandlerID(),hv^.GetCaID(),hv^.GetProvID());
end;
break;
end;
7: begin
integer caid:=(data[8+4] and $ff ) shl 8 or (data[9+4] and $ff);
integer handler:=(data[0+4] and $ff) shl 24 or (data[1+4] and $ff) shl 16 or (data[2+4] and $ff) shl 8 or (data[3+4] and $ff);
integer provider_counts:=data[20+4] and $ff;
integer uphops:=data[10+4];
integer maxdown:=data[11+4];
PRINTF(L_CC_CCCAM,'handler mod 08x serial mod s uphops mod d maxdown mod d',handler,HexStr(str,data+12+4,8),uphops,maxdown);
for(integer i:=0;i integer provider:=(data[21+4+i*7] and $ff) shl 16 or (data[22+4+i*7] and $ff) shl 8 or (data[23+4+i*7] and $ff);
cHandlerItem *n:=new cHandlerItem(handler,caid,provider);
handlers^.cHandlerList.Add(n);
PRINTF(L_CC_CCCAM,'ADD handler mod 08x caid: mod 04x provider: mod 06x',handler,caid,provider);
end;
getcards:=true;
break;
end;
8: begin
PRINTF(L_CC_CCCAM,'Server NodeId mod s',HexStr(str,data+wp,8));
wp+:=8;
PRINTF(L_CC_CCCAM,'Server Version: mod s',data+wp);
wp+:=32;
PRINTF(L_CC_CCCAM,'Builder Version: mod s',data+wp);
break;
end;
$fe:begin
PRINTF(L_CC_CCCAM,'cccam can't decode this ecm');
card.NewCw(cwdata,false);
break;
end;
$ff:begin
PRINTF(L_CC_CCCAM,'cccam can't decode this ecm');
card.NewCw(cwdata,false);
break;
end;
default: begin
break;
end;
end;
end;
if((packet_len+4) result:= true;
end;
function cCardClientCCcam.Init(var config: Char) begin cMutexLock lock(this): bool;
integer num:=0;
integer randomfd:=open('/dev/urandom',O_RDONLY or O_NONBLOCK);
if(randomfd<0) then result:= false;
integer len:=read(randomfd,nodeid,SizeOf(nodeid));
close(randomfd);
if (len <> SizeOf(nodeid)) then result:= false;
if( not ParseStdConfig(config, and num) then
or sscanf( and config[num],': mod 40[ xor :]: mod 40[ xor :]',username,password) <> 2 ) result:= false;
str: array[0..32-1] of Char;
PRINTF(L_CC_CORE,' mod s: username:= mod s password= mod s nodeid= mod s',name,username,password,HexStr(str,nodeid,SizeOf(nodeid)));
result:= Immediate() ? Login() : true;
end;
bool cCardClientCCcam.Login()
begin
Char *str:='CCcam';
Byte clientinfo[]=begin
$00,
//CCcam command
$00,
//packet length
$00,$5D,
const USERNAME_POS = 4;
{$EXTERNALSYM USERNAME_POS} $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,
$00,$00,$00,$00,
const NODEID_POS = 24;
{$EXTERNALSYM NODEID_POS} $00,$00,$00,$00,$00,$00,$00,$00,
const WANTEMU_POS = 32;
{$EXTERNALSYM WANTEMU_POS} $00,
const VERSION_POS = 33;
{$EXTERNALSYM VERSION_POS} $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,
$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,
const BUILDERNUM_POS = 65;
{$EXTERNALSYM BUILDERNUM_POS} $32,$38,$39,$32,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,
$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
);
boost.scoped_array do_not_overwrite(Byte[20]: new): Byte;
boost.scoped_array response(Byte[20]: new): Byte;
boost.scoped_array sendbuff(Byte[20]: new): Byte;
boost.scoped_array hash(Byte[20]: new): Byte;
cMutexLock lock(this);
login_completed:=false;
getcards:=false;
server_packet_count:=0;
integer len:=0;
so.Disconnect();
if( not so.Connect(hostname,port)) then result:= false;
while(len=so.Read(buffer,SizeOf(buffer),-1)) begin
if (server_packet_count > 0) then begin
cccam_decrypt( and client_decrypt_state, buffer, buffer, len );
HEXDUMP(L_CC_CCCAM,buffer,len,'Receive Messages');
end;
if (login_completed) then begin
packet_analyzer( and client_decrypt_state,buffer,len);
if(getcards) then begin
Start();
result:= true;
end;
end;
case ( server_packet_count ) begin of
0:
if(len <> 16) then result:= false;
if( not check_connect_checksum(buffer,len)) then result:= false;
PRINTF(L_CC_CCCAM,'Receive Message CheckSum correct');
cccam_xor(buffer,len);
SHA_CTX ctx;
SHA1_Init( and ctx );
SHA1_Update( and ctx, buffer, len);
SHA1_Final( hash.get(), and ctx );
cccam_init_crypt( and client_decrypt_state, hash.get(), 20 );
cccam_decrypt( and client_decrypt_state, buffer, buffer, 16 );
cccam_init_crypt( and client_encrypt_state, buffer, 16 );
cccam_encrypt( and client_encrypt_state, hash.get(), hash.get(), 20 );
HEXDUMP(L_CC_CCCAM,hash.get(),20,'Send Messages');
cccam_encrypt( and client_encrypt_state, hash.get(), response.get(), 20 );
so.Write(response.get(),20);
bzero(response.get(),20);
bzero(sendbuff.get(),20);
StrCopy(reinterpret_cast(response.get(: )),username): PChar;
HEXDUMP(L_CC_CCCAM,response.get(),20,'Send UserName Messages');
cccam_encrypt( and client_encrypt_state, response.get(), sendbuff.get(), 20 );
so.Write(sendbuff.get(),20);
bzero(response.get(),20);
bzero(sendbuff.get(),20);
StrCopy(reinterpret_cast(response.get(: )),password): PChar;
HEXDUMP(L_CC_CCCAM,response.get(),20,'Password');
cccam_encrypt( and client_encrypt_state, response.get(), sendbuff.get(), strlen(password));
bzero(sendbuff.get(),20);
cccam_encrypt( and client_encrypt_state,reinterpret_cast< function>(str: ), sendbuff.get(), 6 ): Byte;
so.Write(sendbuff.get(),6);
break;
1:
if (len < 20) then break;
if(StrComp('CCcam', reinterpret_cast(buffer)) = 0) then begin
login_completed:=true;
PRINTF(L_CC_CORE,'CCcam login Success not ');
StrCopy(reinterpret_cast(+USERNAME_POS: clientinfo),username): PChar;
StrCopy(reinterpret_cast(+VERSION_POS: clientinfo),'vdr-sc'): PChar;
memcpy(clientinfo + NODEID_POS,nodeid,8);
bzero(netbuff,SizeOf(netbuff));
cccam_encrypt( and client_encrypt_state,reinterpret_cast< function>(clientinfo: and), reinterpret_cast( and netbuff), SizeOf(clientinfo) ): Byte;
so.Write(netbuff,SizeOf(clientinfo));
end;elsebegin
result:= false;
end;
break;
default:
break;
end;
server_packet_count:= mod + 1;
end;
result:= false;
end;
bool cCardClientCCcam.ProcessECM( cEcmInfo *ecm, Byte *data, Byte *cw, integer cardnum)
begin
Byte ecm_head[]=begin
$00,
const CCCAM_COMMAND_POS = 1;
{$EXTERNALSYM CCCAM_COMMAND_POS} $01,
const CCCAM_LEN_POS = 2;
{$EXTERNALSYM CCCAM_LEN_POS} $00,$00,
const ECM_CAID_POS = 4;
{$EXTERNALSYM ECM_CAID_POS} $00,$00,
$00,
const ECM_PROVIDER_POS = 7;
{$EXTERNALSYM ECM_PROVIDER_POS} $00,$00,$00,
const ECM_HANDLER_POS = 10;
{$EXTERNALSYM ECM_HANDLER_POS} $00,$00,$00,$00,
const ECM_SID_POS = 14;
{$EXTERNALSYM ECM_SID_POS} $00,$00,
const ECM_LEN_POS = 16;
{$EXTERNALSYM ECM_LEN_POS} $00,
const ECM_DATA_POS = 17;
{$EXTERNALSYM ECM_DATA_POS};
cMutexLock lock(this);
if(( not so.Connected() and not Login()) or not CanHandle(ecm^.caId)) then result:= false;
cCCcamCard *c:= and card;
shareid:=0;
bzero(buffer,SizeOf(buffer));
memcpy(buffer,ecm_head,SizeOf(ecm_head));
memcpy(buffer+SizeOf(ecm_head),data,SCT_LEN(data));
integer ecm_len:=SizeOf(ecm_head)+SCT_LEN(data);
buffer[CCCAM_COMMAND_POS]:=1;
buffer[CCCAM_LEN_POS]:=((ecm_len-4) shr and $ff;
buffer[CCCAM_LEN_POS+1]:=(ecm_len-4) and $ff;
buffer[ECM_CAID_POS]:=(ecm^.caId shr and $FF;
buffer[ECM_CAID_POS+1]:=ecm^.caId and $FF;
buffer[ECM_PROVIDER_POS]:=(ecm^.provId shr 16) and $FF;
buffer[ECM_PROVIDER_POS+1]:=(ecm^.provId shr and $FF;
buffer[ECM_PROVIDER_POS+2]:=ecm^.provId and $FF;
buffer[ECM_SID_POS]:=(ecm^.prgId shr and $FF;
buffer[ECM_SID_POS+1]:=ecm^.prgId and $FF;
buffer[ECM_LEN_POS]:=SCT_LEN(data);
for ( cHandlerItem *hv:=handlers^.cHandlerList.First(); hv; hv=handlers^.cHandlerList.Next(hv))
begin
if(ecm^.caId >= $500 and ecm^.caId <= $05FF)begin
if(hv^.GetCaID() := ecm^.caId and hv^.GetProvID() = ecm^.provId) shareid=hv^.GetHandlerID() then ;
end;
else if((ecm^.caId >= $1800 and ecm^.caId <= $18FF)
or (ecm^.caId>= $0600 and ecm^.caId <= $06FF)
or (ecm^.caId >= $0900 and ecm^.caId <= $09FF)
or ecm^.caId = $0B00)begin
if(hv^.GetCaID() := ecm^.caId) shareid=hv^.GetHandlerID() then ;
end;elsebegin
if(hv^.GetCaID() := ecm^.caId and hv^.GetProvID() = ecm^.provId) shareid=hv^.GetHandlerID() then ;
end;
end;
PRINTF(L_CC_CORE,' mod d: Ecm CaID mod 04x Provider mod 04x',cardnum,ecm^.caId,ecm^.provId);
if(shareid = 0) then result:= false;
buffer[ECM_HANDLER_POS]:=(shareid shr 24) and $FF;
buffer[ECM_HANDLER_POS+1]:=(shareid shr 16) and $FF;
buffer[ECM_HANDLER_POS+2]:=(shareid shr and $FF;
buffer[ECM_HANDLER_POS+3]:=shareid and $FF;
PRINTF(L_CC_CORE,' mod d: Find Server HandlerID mod x',cardnum,shareid);
HEXDUMP(L_CC_CCCAM,buffer,ecm_len,' mod d: Send ECM Messages',cardnum);
cccam_encrypt( and client_encrypt_state,buffer,netbuff,ecm_len);
so.Write(netbuff,ecm_len);
if( not c^.GetCw(cw)) then result:= false;
PRINTF(L_CC_CCCAM,' mod d: got CW',cardnum);
result:= true;
end;
bool cCardClientCCcam.ProcessEMM(integer caSys, Byte *data)
begin
result:= false;
end;
procedure cCardClientCCcam.Action()
begin
while(Running()) begin
usleep(100);
bzero(recvbuff,SizeOf(recvbuff));
integer len:=so.Read(recvbuff,SizeOf(recvbuff),0);
if(len>0) then begin
cccam_decrypt( and client_decrypt_state, recvbuff, recvbuff, len );
HEXDUMP(L_CC_CCCAM,recvbuff,len,'Receive Messages');
packet_analyzer( and client_decrypt_state,recvbuff,len);
end;
end;
end;
implementation
end.