|
SASC-NG
DVBSoftCAM
|
00001 /* 00002 * Softcam plugin to VDR (C++) 00003 * 00004 * This code is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU General Public License 00006 * as published by the Free Software Foundation; either version 2 00007 * of the License, or (at your option) any later version. 00008 * 00009 * This code is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License 00015 * along with this program; if not, write to the Free Software 00016 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 00017 * Or, point your browser to http://www.gnu.org/copyleft/gpl.html 00018 */ 00019 00020 #include <stdlib.h> 00021 #include <string.h> 00022 #include <ctype.h> 00023 00024 #include "system-common.h" 00025 #include "opts.h" 00026 #include "misc.h" 00027 #include "parse.h" 00028 #include "log-core.h" 00029 00030 #include "viaccess.h" 00031 #include "tps.h" 00032 #include "log-viaccess.h" 00033 #include "version.h" 00034 00035 SCAPIVERSTAG(); 00036 00037 #define SYSTEM_NAME "Viaccess" 00038 #define SYSTEM_PRI -10 00039 #define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_VIACCESS) 00040 00041 static const struct LogModule lm_sys = { 00042 (LMOD_ENABLE|L_SYS_ALL)&LOPT_MASK, 00043 (LMOD_ENABLE|L_SYS_DEFDEF|L_SYS_TPS|L_SYS_TPSAU|L_SYS_TIME|L_SYS_ST20)&LOPT_MASK, 00044 "viaccess", 00045 { L_SYS_DEFNAMES,"tps","tpsau","time","st20","disasm" } 00046 }; 00047 ADD_MODULE(L_SYS,lm_sys) 00048 00049 // -- cPlainKeyVia ------------------------------------------------------------- 00050 00051 #define VIA1_KEYLEN 8 00052 #define VIA2_KEYLEN 16 00053 #define VIATPS_KEYLEN 16 00054 00055 class cPlainKeyVia : public cPlainKeyStd { 00056 protected: 00057 virtual int IdSize(void) { return 6; } 00058 virtual cString PrintKeyNr(void); 00059 public: 00060 cPlainKeyVia(bool Super); 00061 virtual bool Parse(const char *line); 00062 virtual bool SetKey(void *Key, int Keylen); 00063 virtual bool SetBinKey(unsigned char *Mem, int Keylen); 00064 }; 00065 00066 static cPlainKeyTypeReg<cPlainKeyVia,'V'> KeyReg; 00067 00068 cPlainKeyVia::cPlainKeyVia(bool Super) 00069 :cPlainKeyStd(Super) 00070 {} 00071 00072 bool cPlainKeyVia::SetKey(void *Key, int Keylen) 00073 { 00074 if(keynr==MBC3('T','P','S')) SetSupersede(false); 00075 return cPlainKeyStd::SetKey(Key,Keylen); 00076 } 00077 00078 bool cPlainKeyVia::SetBinKey(unsigned char *Mem, int Keylen) 00079 { 00080 if(keynr==MBC3('T','P','S')) SetSupersede(false); 00081 return cPlainKeyStd::SetBinKey(Mem,Keylen); 00082 } 00083 00084 bool cPlainKeyVia::Parse(const char *line) 00085 { 00086 unsigned char sid[3], skeynr, skey[VIA2_KEYLEN]; 00087 int len; 00088 if(GetChar(line,&type,1) && (len=GetHex(line,sid,3,false))) { 00089 type=toupper(type); id=Bin2Int(sid,len); 00090 line=skipspace(line); 00091 bool ok=false; 00092 if(!strncasecmp(line,"TPSMK",5) && line[5]>='0' && line[5]<='9') { 00093 keynr=MBC3('M','K',line[5]-'0'); 00094 line+=6; 00095 ok=(len=GetHex(line,skey,VIATPS_KEYLEN)); 00096 } 00097 else if(!strncasecmp(line,"TPS ",4)) { 00098 line+=4; 00099 keynr=MBC3('T','P','S'); 00100 ok=(len=GetHex(line,skey,VIATPS_KEYLEN)); 00101 } 00102 else if(GetHex(line,&skeynr,1)) { 00103 keynr=skeynr; 00104 ok=((len=GetHex(line,skey,VIA2_KEYLEN,false)) && (len==VIA1_KEYLEN || len==VIA2_KEYLEN)); 00105 } 00106 if(ok) { 00107 SetBinKey(skey,len); 00108 return true; 00109 } 00110 } 00111 return false; 00112 } 00113 00114 cString cPlainKeyVia::PrintKeyNr(void) 00115 { 00116 char tmp[12]; 00117 const char *kn=tmp; 00118 switch(keynr) { 00119 case MBC3('T','P','S'): 00120 kn="TPS"; break; 00121 case MBC3('M','K',0): case MBC3('M','K',1): case MBC3('M','K',2): 00122 case MBC3('M','K',3): case MBC3('M','K',4): case MBC3('M','K',5): 00123 case MBC3('M','K',6): case MBC3('M','K',7): case MBC3('M','K',8): 00124 case MBC3('M','K',9): 00125 snprintf(tmp,sizeof(tmp),"TPSMK%d",C3(keynr)); break; 00126 default: 00127 snprintf(tmp,sizeof(tmp),"%02X",keynr); break; 00128 } 00129 return kn; 00130 } 00131 00132 // -- cViaccessCardInfo -------------------------------------------------------- 00133 00134 class cViaccessCardInfo : public cStructItem, public cProviderViaccess, public cCardViaccess { 00135 public: 00136 unsigned char keyno, key[8]; 00137 // 00138 bool Parse(const char *line); 00139 }; 00140 00141 bool cViaccessCardInfo::Parse(const char *line) 00142 { 00143 return GetHex(line,ident,sizeof(ident)) && 00144 GetHex(line,ua,sizeof(ua)) && 00145 GetHex(line,sa,sizeof(sa)) && 00146 GetHex(line,&keyno,1) && 00147 GetHex(line,key,sizeof(key)); 00148 } 00149 00150 // -- cViaccessCardInfos ------------------------------------------------------- 00151 00152 class cViaccessCardInfos : public cCardInfos<cViaccessCardInfo> { 00153 public: 00154 cViaccessCardInfos(void):cCardInfos<cViaccessCardInfo>("Viaccess cards","Viaccess.KID",0) {} 00155 }; 00156 00157 static cViaccessCardInfos Vcards; 00158 00159 // -- cViaccess ---------------------------------------------------------------- 00160 00161 class cViaccess : protected cDes { 00162 private: 00163 unsigned char v2key[8]; 00164 bool v2mode; 00165 // 00166 int HashNanos(const unsigned char *data, int len); 00167 void Via2Mod(const unsigned char *key2, unsigned char *data); 00168 protected: 00169 unsigned char hbuff[8], hkey[8]; 00170 int pH; 00171 // 00172 void SetV2Mode(const unsigned char *key2); 00173 void SetHashKey(const unsigned char *key); 00174 void HashByte(unsigned char c); 00175 void HashClear(void); 00176 void Hash(void); 00177 // 00178 void Decode(unsigned char *data, const unsigned char *key); 00179 bool Decrypt(const unsigned char *work_key, const unsigned char *data, int len, unsigned char *des_data1, unsigned char *des_data2); 00180 // 00181 virtual unsigned int Mod(unsigned int R, unsigned int key7) const; 00182 public: 00183 cViaccess(void); 00184 }; 00185 00186 cViaccess::cViaccess(void) 00187 :cDes() 00188 { 00189 v2mode=false; 00190 } 00191 00192 /* viaccess DES modification */ 00193 00194 unsigned int cViaccess::Mod(unsigned int R, unsigned int key7) const 00195 { 00196 if(key7!=0) { 00197 const unsigned int key5=(R>>24)&0xff; 00198 unsigned int al=key7*key5 + key7 + key5; 00199 al=(al&0xff)-((al>>8)&0xff); 00200 if(al&0x100) al++; 00201 R=(R&0x00ffffffL) + (al<<24); 00202 } 00203 return R; 00204 } 00205 00206 /* viaccess2 modification. Extracted from "Russian wafer" card. 00207 A lot of thanks to it's author :) */ 00208 00209 void cViaccess::Via2Mod(const unsigned char *key2, unsigned char *data) 00210 { 00211 int kb, db; 00212 for(db=7; db>=0; db--) { 00213 for(kb=7; kb>3; kb--) { 00214 int a0=kb^db; 00215 int pos=7; 00216 if(a0&4) { a0^=7; pos^=7; } 00217 a0=(a0^(kb&3)) + (kb&3); 00218 if(!(a0&4)) data[db]^=(key2[kb] ^ ((data[kb^pos]*key2[kb^4]) & 0xFF)); 00219 } 00220 } 00221 for(db=0; db<8; db++) { 00222 for(kb=0; kb<4; kb++) { 00223 int a0=kb^db; 00224 int pos=7; 00225 if(a0&4) { a0^=7; pos^=7; } 00226 a0=(a0^(kb&3)) + (kb&3); 00227 if(!(a0&4)) data[db]^=(key2[kb] ^ ((data[kb^pos]*key2[kb^4]) & 0xFF)); 00228 } 00229 } 00230 } 00231 00232 void cViaccess::Decode(unsigned char *data, const unsigned char *key) 00233 { 00234 if(v2mode) Via2Mod(v2key,data); 00235 Des(data,key,VIA_DES); 00236 if(v2mode) Via2Mod(v2key,data); 00237 } 00238 00239 void cViaccess::SetV2Mode(const unsigned char *key2) 00240 { 00241 if(key2) { 00242 memcpy(v2key,key2,sizeof(v2key)); 00243 v2mode=true; 00244 } 00245 else v2mode=false; 00246 } 00247 00248 00249 void cViaccess::SetHashKey(const unsigned char *key) 00250 { 00251 memcpy(hkey,key,sizeof(hkey)); 00252 } 00253 00254 void cViaccess::HashByte(unsigned char c) 00255 { 00256 hbuff[pH++]^=c; 00257 if(pH==8) { pH=0; Hash(); } 00258 } 00259 00260 void cViaccess::HashClear(void) 00261 { 00262 memset(hbuff,0,sizeof(hbuff)); 00263 pH=0; 00264 } 00265 00266 void cViaccess::Hash(void) 00267 { 00268 if(v2mode) Via2Mod(v2key,hbuff); 00269 Des(hbuff,hkey,VIA_DES_HASH); 00270 if(v2mode) Via2Mod(v2key,hbuff); 00271 } 00272 00273 int cViaccess::HashNanos(const unsigned char *data, int len) 00274 { 00275 int i=0; 00276 pH=0; 00277 if(data[0]==0x9f) { 00278 HashByte(data[i++]); 00279 HashByte(data[i++]); 00280 for(int j=0; j<data[1]; j++) HashByte(data[i++]); 00281 while(pH!=0) HashByte(0); 00282 } 00283 for(; i<len; i++) HashByte(data[i]); 00284 return i; 00285 } 00286 00287 bool cViaccess::Decrypt(const unsigned char *work_key, const unsigned char *data, int len, unsigned char *des_data1, unsigned char *des_data2) 00288 { 00289 int pos=0, encStart=0; 00290 unsigned char signatur[8]; 00291 while(pos<len) { 00292 switch(data[pos]) { 00293 case 0xea: // encrypted bytes 00294 encStart = pos + 2; 00295 memcpy(des_data1,&data[pos+2],8); 00296 memcpy(des_data2,&data[pos+2+8],8); 00297 break; 00298 case 0xf0: // signature 00299 memcpy(signatur,&data[pos+2],8); 00300 break; 00301 } 00302 pos += data[pos+1]+2; 00303 } 00304 HashClear(); 00305 SetHashKey(work_key); 00306 // key preparation 00307 unsigned char prepared_key[8]; 00308 if(work_key[7]==0) { 00309 // 8th key-byte = 0 then like Eurocrypt-M but with viaccess mods 00310 HashNanos(data,encStart+16); 00311 memcpy(prepared_key,work_key,sizeof(prepared_key)); 00312 } 00313 else { // key8 not zero 00314 // rotate the key 2x left 00315 prepared_key[0]=work_key[2]; 00316 prepared_key[1]=work_key[3]; 00317 prepared_key[2]=work_key[4]; 00318 prepared_key[3]=work_key[5]; 00319 prepared_key[4]=work_key[6]; 00320 prepared_key[5]=work_key[0]; 00321 prepared_key[6]=work_key[1]; 00322 prepared_key[7]=work_key[7]; 00323 // test if key8 odd 00324 if(work_key[7]&1) { 00325 HashNanos(data,encStart); 00326 // test if low nibble zero 00327 unsigned char k = ((work_key[7] & 0xf0) == 0) ? 0x5a : 0xa5; 00328 for(int i=0; i<8; i++) { 00329 unsigned char tmp=des_data1[i]; 00330 des_data1[i]=(k & hbuff[pH]) ^ tmp; 00331 HashByte(tmp); 00332 } 00333 for(int i=0; i<8; i++) { 00334 unsigned char tmp=des_data2[i]; 00335 des_data2[i]=(k & hbuff[pH]) ^ tmp; 00336 HashByte(tmp); 00337 } 00338 } 00339 else { 00340 HashNanos(data,encStart+16); 00341 } 00342 } 00343 Decode(des_data1,prepared_key); 00344 Decode(des_data2,prepared_key); 00345 Hash(); 00346 return (memcmp(signatur,hbuff,8)==0); 00347 } 00348 00349 // -- cSystemViaccess ---------------------------------------------------------- 00350 00351 #define MAX_NEW_KEYS 5 00352 00353 class cSystemViaccess : public cSystem, private cViaccess { 00354 private: 00355 cTPS tps; 00356 public: 00357 cSystemViaccess(void); 00358 virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *data); 00359 virtual void ProcessEMM(int pid, int caid, const unsigned char *data); 00360 virtual void ParseCADescriptor(cSimpleList<cEcmInfo> *ecms, unsigned short sysId, int source, const unsigned char *data, int len); 00361 }; 00362 00363 cSystemViaccess::cSystemViaccess(void) 00364 :cSystem(SYSTEM_NAME,SYSTEM_PRI) 00365 { 00366 hasLogger=true; 00367 } 00368 00369 void cSystemViaccess::ParseCADescriptor(cSimpleList<cEcmInfo> *ecms, unsigned short sysId, int source, const unsigned char *data, int len) 00370 { 00371 const int pid=WORD(data,2,0x1FFF); 00372 if(pid>=0xAA && pid<=0xCF) { 00373 PRINTF(L_CORE_ECMPROC,"viaccess: dropped \"fake\" ecm pid 0x%04x",pid); 00374 return; 00375 } 00376 cSystem::ParseCADescriptor(ecms,sysId,source,data,len); 00377 } 00378 00379 bool cSystemViaccess::ProcessECM(const cEcmInfo *ecm, unsigned char *data) 00380 { 00381 unsigned char *nanos=(unsigned char *)cParseViaccess::PayloadStart(data); 00382 int len=SCT_LEN(data)-(nanos-data); 00383 00384 bool mayHaveTps=false; 00385 if(ecm->provId==0x007c00) { // TPS 00386 maxEcmTry=4; 00387 mayHaveTps=true; 00388 int num=tps.Decrypt(this,ecm->source,ecm->transponder,nanos,len); 00389 if(num<0) return false; 00390 nanos+=num; len-=num; 00391 } 00392 00393 if(cParseViaccess::CheckNano90FromNano(nanos)) { 00394 int keynr=cParseViaccess::KeyNrFromNano(nanos); 00395 cKeySnoop ks(this,'V',ecm->provId,keynr); 00396 cPlainKey *pk=0; 00397 while((pk=keys.FindKey('V',ecm->provId,keynr,-1,pk))) { 00398 unsigned char key[16]; 00399 if(pk->Size()<=(int)sizeof(key)) { 00400 pk->Get(key); 00401 SetV2Mode(pk->Size()==VIA2_KEYLEN ? &key[VIA1_KEYLEN] : 0); 00402 if(cViaccess::Decrypt(key,&nanos[5],len-5,&cw[0],&cw[8])) { 00403 if(mayHaveTps) tps.PostProc(cw); 00404 ks.OK(pk); 00405 return true; 00406 } 00407 } 00408 } 00409 } 00410 return false; 00411 } 00412 00413 void cSystemViaccess::ProcessEMM(int pid, int caid, const unsigned char *data) 00414 { 00415 for(cViaccessCardInfo *mkey=Vcards.First(); mkey; mkey=Vcards.Next(mkey)) { 00416 int updtype; 00417 cAssembleData ad(data); 00418 if(mkey->cCardViaccess::MatchEMM(data)) { 00419 updtype=3; 00420 HashClear(); 00421 memcpy(hbuff+3,mkey->ua,sizeof(mkey->ua)); 00422 } 00423 else if(mkey->cProviderViaccess::MatchEMM(data)) { 00424 if(mkey->cProviderViaccess::Assemble(&ad)<0) continue; 00425 updtype=2; 00426 HashClear(); 00427 memcpy(hbuff+5,mkey->sa,sizeof(mkey->sa)-1); 00428 } 00429 else continue; 00430 00431 const unsigned char *buff; 00432 if((buff=ad.Assembled())) { 00433 const unsigned char *scan=cParseViaccess::NanoStart(buff); 00434 unsigned int scanlen=SCT_LEN(buff)-(scan-buff); 00435 00436 if(scanlen>=5 && mkey->cProviderViaccess::MatchID(buff) && 00437 cParseViaccess::KeyNrFromNano(scan)==mkey->keyno) { 00438 scan+=5; scanlen-=5; 00439 SetHashKey(mkey->key); 00440 Hash(); 00441 00442 unsigned int n; 00443 if(scan[0]==0x9e && scanlen>=(n=scan[1]+2)) { 00444 for(unsigned int i=0; i<n; i++) HashByte(scan[i]); 00445 Hash(); pH=0; 00446 scan+=n; scanlen-=5; 00447 } 00448 if(scanlen>0) { 00449 unsigned char newKey[MAX_NEW_KEYS][8]; 00450 int numKeys=0, updPrv[MAX_NEW_KEYS]={}, updKey[MAX_NEW_KEYS]={}; 00451 00452 for(unsigned int cnt=0; cnt<scanlen && numKeys<MAX_NEW_KEYS;) { 00453 const unsigned int parm=scan[cnt++]; 00454 unsigned int plen=scan[cnt++]; 00455 00456 switch(parm) { 00457 case 0x90: 00458 case 0x9E: 00459 cnt+=plen; 00460 break; 00461 case 0xA1: // keyupdate 00462 updPrv[numKeys]=(scan[cnt]<<16)+(scan[cnt+1]<<8)+(scan[cnt+2]&0xF0); 00463 updKey[numKeys]=scan[cnt+2]&0x0F; 00464 // fall through 00465 default: 00466 HashByte(parm); HashByte(plen); 00467 while(plen--) HashByte(scan[cnt++]); 00468 break; 00469 case 0xEF: // crypted key(s) 00470 HashByte(parm); HashByte(plen); 00471 if(plen==sizeof(newKey[0])) { 00472 const unsigned char k7=mkey->key[7]; 00473 for(unsigned int kc=0 ; kc<sizeof(newKey[0]) ; kc++) { 00474 const unsigned char b=scan[cnt++]; 00475 if(k7&1) newKey[numKeys][kc]=b^(hbuff[pH]&(k7<0x10 ? 0x5a : 0xa5)); 00476 else newKey[numKeys][kc]=b; 00477 HashByte(b); 00478 } 00479 numKeys++; 00480 } 00481 else { 00482 PRINTF(L_SYS_EMM,"key length mismatch %d!=%d",plen,(int)sizeof(newKey[0])); 00483 cnt=scanlen; 00484 } 00485 break; 00486 case 0xF0: // signature 00487 { 00488 char str[20], str2[20]; 00489 static const char *ptext[] = { 0,0,"SHARED","UNIQUE" }; 00490 const char *addr = (updtype==2) ? HexStr(str,mkey->sa,sizeof(mkey->sa)) : HexStr(str,mkey->ua,sizeof(mkey->ua)); 00491 00492 Hash(); 00493 if(!memcmp(&scan[cnt],hbuff,sizeof(hbuff))) { 00494 unsigned char key[8]; 00495 memcpy(key,mkey->key,sizeof(key)); 00496 if(key[7]) { // Rotate key 00497 const unsigned char t1=key[0], t2=key[1]; 00498 key[0]=key[2]; key[1]=key[3]; key[2]=key[4]; key[3]=key[5]; key[4]=key[6]; 00499 key[5]=t1; key[6]=t2; 00500 } 00501 00502 while(numKeys--) { 00503 Decode(newKey[numKeys],key); 00504 PRINTF(L_SYS_EMM,"%02X%02X %02X %s %s - KEY %06X.%02X -> %s", 00505 mkey->ident[0],mkey->ident[1],mkey->keyno,addr, 00506 ptext[updtype],updPrv[numKeys],updKey[numKeys], 00507 HexStr(str2,newKey[numKeys],sizeof(newKey[numKeys]))); 00508 FoundKey(); 00509 if(keys.NewKey('V',updPrv[numKeys],updKey[numKeys],newKey[numKeys],8)) NewKey(); 00510 } 00511 } 00512 else 00513 PRINTF(L_SYS_EMM,"%02X%02X %02X %s %s - FAIL",mkey->ident[0],mkey->ident[1],mkey->keyno,addr,ptext[updtype]); 00514 cnt=scanlen; 00515 break; 00516 } 00517 } 00518 } 00519 } 00520 } 00521 } 00522 } 00523 } 00524 00525 // -- cSystemLinkViaccess ------------------------------------------------------ 00526 00527 static const char *tpsau[] = { 00528 trNOOP("stream"), 00529 trNOOP("tps.bin"), 00530 }; 00531 00532 class cSystemLinkViaccess : public cSystemLink { 00533 public: 00534 cSystemLinkViaccess(void); 00535 virtual bool CanHandle(unsigned short SysId); 00536 virtual cSystem *Create(void) { return new cSystemViaccess; } 00537 }; 00538 00539 static cSystemLinkViaccess staticInit; 00540 00541 cSystemLinkViaccess::cSystemLinkViaccess(void) 00542 :cSystemLink(SYSTEM_NAME,SYSTEM_PRI) 00543 { 00544 opts=new cOpts(SYSTEM_NAME,1); 00545 opts->Add(new cOptSel("TpsAU",trNOOP("Viaccess: TPS updates from"),&tpsAuMode,sizeof(tpsau)/sizeof(char *),tpsau)); 00546 Feature.NeedsKeyFile(); 00547 } 00548 00549 bool cSystemLinkViaccess::CanHandle(unsigned short SysId) 00550 { 00551 SysId&=SYSTEM_MASK; 00552 return SYSTEM_CAN_HANDLE(SysId); 00553 }