SASC
DVBSoftCAM

viaccess.c

Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines