SASC-NG
DVBSoftCAM

sc-seca.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 <stdio.h>
00021 #include <stdlib.h>
00022 #include <string.h>
00023 
00024 #include "system-common.h"
00025 #include "smartcard.h"
00026 #include "data.h"
00027 #include "misc.h"
00028 #include "opts.h"
00029 #include "parse.h"
00030 #include "log-sc.h"
00031 #include "version.h"
00032 
00033 SCAPIVERSTAG();
00034 
00035 #define SYSTEM_SECA          0x0100
00036 
00037 #define SYSTEM_NAME          "SC-Seca"
00038 #define SYSTEM_PRI           -5
00039 #define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_SECA)
00040 
00041 #define SC_NAME "Seca"
00042 #define SC_ID   MAKE_SC_ID('S','e','c','a')
00043 
00044 #define SECADATE(buff,len,odate) { \
00045                                  const unsigned char *dd=(odate); \
00046                                  snprintf(buff,len,"%04d/%02d/%02d", \
00047                                    ((dd[0]&0xFE)>>1)+1990, \
00048                                    ((dd[0]&0x01)<<3) + ((dd[1]&0xE0)>>5), \
00049                                    dd[1]&0x1F); \
00050                                  }
00051 
00052 #define L_SC        12
00053 #define L_SC_PROC   LCLASS(L_SC,L_SC_LASTDEF<<1)
00054 #define L_SC_ALL    LALL(L_SC_PROC)
00055 
00056 static const struct LogModule lm_sc = {
00057   (LMOD_ENABLE|L_SC_ALL)&LOPT_MASK,
00058   (LMOD_ENABLE|L_SC_DEFDEF)&LOPT_MASK,
00059   "sc-seca",
00060   { L_SC_DEFNAMES,"process" }
00061   };
00062 ADD_MODULE(L_SC,lm_sc)
00063 
00064 static int blocker=0;
00065 
00066 // -- cSystemScSeca ---------------------------------------------------------------
00067 
00068 class cSystemScSeca : public cSystemScCore {
00069 public:
00070   cSystemScSeca(void);
00071   };
00072 
00073 cSystemScSeca::cSystemScSeca(void)
00074 :cSystemScCore(SYSTEM_NAME,SYSTEM_PRI,SC_ID,"SC Seca")
00075 {
00076   hasLogger=true;
00077   needsDescrData=true;
00078 }
00079 
00080 // -- cSystemLinkScSeca --------------------------------------------------------
00081 
00082 static const char *block[] = {
00083   trNOOP("allow ALL"),
00084   trNOOP("block UNIQUE"),
00085   trNOOP("block SHARED"),
00086   trNOOP("block ALL")
00087   };
00088 
00089 class cSystemLinkScSeca : public cSystemLink {
00090 public:
00091   cSystemLinkScSeca(void);
00092   virtual bool CanHandle(unsigned short SysId);
00093   virtual cSystem *Create(void) { return new cSystemScSeca; }
00094   };
00095 
00096 static cSystemLinkScSeca staticInit;
00097 
00098 cSystemLinkScSeca::cSystemLinkScSeca(void)
00099 :cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
00100 {
00101   opts=new cOpts(SYSTEM_NAME,1);
00102   opts->Add(new cOptSel("Blocker",trNOOP("SC-Seca: EMM updates"),&blocker,sizeof(block)/sizeof(char *),block));
00103   Feature.NeedsSmartCard();
00104 }
00105 
00106 bool cSystemLinkScSeca::CanHandle(unsigned short SysId)
00107 {
00108   SysId&=SYSTEM_MASK;
00109   return smartcards.HaveCard(SC_ID) && SYSTEM_CAN_HANDLE(SysId);
00110 }
00111 
00112 // -- cProviderScSeca ----------------------------------------------------------
00113 
00114 class cProviderScSeca : public cProviderSeca {
00115 public:
00116   int index;
00117   unsigned char date[2], pbm[8];
00118   char name[17];
00119   //
00120   cProviderScSeca(const unsigned char *pi, const unsigned char *s):cProviderSeca(pi,s) {}
00121   };
00122 
00123 // -- cSmartCardSeca -----------------------------------------------------------
00124 
00125 struct SecaProvInfo {
00126   unsigned char prov[2];
00127   char name[16];
00128   unsigned char sa[3];
00129   unsigned char cb;
00130   unsigned char date[2];
00131   unsigned char rr;
00132   unsigned char rstart;
00133   unsigned char pbm[8];
00134   unsigned char rend;
00135   };
00136 
00137 struct SecaChannelInfo {
00138   unsigned char pbm[8];
00139   unsigned char date[2];
00140   };
00141 
00142 class cSmartCardSeca : public cSmartCard, private cIdSet {
00143 private:
00144   char datebuff[16];
00145   //
00146   const char *Date(const unsigned char *date);
00147   bool CheckAccess(const unsigned char *data, const cProviderScSeca *p);
00148 public:
00149   cSmartCardSeca(void);
00150   virtual bool Init(void);
00151   virtual bool Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw);
00152   virtual bool Update(int pid, int caid, const unsigned char *data);
00153   };
00154 
00155 static const struct StatusMsg msgs[] = {
00156   // ECM status messages
00157   { { 0x90,0x00 }, "Instruction executed without errors", true },
00158   { { 0x90,0x02 }, "Signature failed", false },
00159   { { 0x90,0x27 }, "Decoding the preview (non-error)", true },
00160   { { 0x93,0x02 }, "No access, check your subscription", false },
00161   { { 0x96,0x00 }, "Chain of nonvalid entrance or null event or all the nanos process, none decoding", false },
00162   // EMM update status messages
00163   { { 0x90,0x09 }, "Card update was not meant for this card", false },
00164   { { 0x90,0x19 }, "Card update successfull, PPUA updated", true },
00165   { { 0x97,0x00 }, "Card update was successful", true },
00166   { { 0x97,0x40 }, "Card update was successful", true },
00167   { { 0x97,0x50 }, "Card update was successful", true },
00168   { { 0x97,0x78 }, "Card update was not necessary", false },
00169   { { 0x97,0xe0 }, "EEPROM update was not necessary", false },
00170   // unknown message
00171   { { 0xFF,0xFF }, 0, false }
00172   };
00173 
00174 static const struct CardConfig cardCfg = {
00175   SM_8E2,2000,300
00176   };
00177 
00178 cSmartCardSeca::cSmartCardSeca(void)
00179 :cSmartCard(&cardCfg,msgs)
00180 {}
00181 
00182 bool cSmartCardSeca::Init(void)
00183 {
00184   static unsigned char ins0e[] = { 0xC1,0x0e,0x00,0x00,0x08 }; // get serial nr. (UA)
00185   static unsigned char ins16[] = { 0xC1,0x16,0x00,0x00,0x07 }; // get nr. of providers
00186   static unsigned char ins12[] = { 0xC1,0x12,0x00,0x00,0x19 }; // get provider info
00187   static unsigned char ins34[] = { 0xC1,0x34,0x00,0x00,0x03 }; // request provider pbm (data=0x00 0x00 0x00)
00188   static unsigned char ins32[] = { 0xC1,0x32,0x00,0x00,0x0A }; // get the pbm data (p1=provider)
00189 
00190   static const unsigned char atrTester[] = { 0x0E,0x6C,0xB6,0xD6 };
00191   if(atr->T!=0 || atr->histLen<7 || memcmp(&atr->hist[3],atrTester,4)) {
00192     PRINTF(L_SC_INIT,"doesn't looks like a Seca card");
00193     return false;
00194     }
00195 
00196   infoStr.Begin();
00197   infoStr.Strcat("Seca smartcard\n");
00198   const char *type;
00199   switch(atr->hist[0]*256+atr->hist[1]) {
00200     case 0x5084: type="Generic"; break;
00201     case 0x5384: type="Philips"; break;
00202     case 0x5130:
00203     case 0x5430:
00204     case 0x5760: type="Thompson"; break;
00205     case 0x5284:
00206     case 0x5842:
00207     case 0x6060: type="Siemens"; break;
00208     case 0x7070: type="Canal+ NL"; break;
00209     default:     type="Unknown"; break;
00210     }
00211   snprintf(idStr,sizeof(idStr),"%s (%s %d.%d)",SC_NAME,type,atr->hist[2]&0x0F,atr->hist[2]>>4);
00212   PRINTF(L_SC_INIT,"cardtype: %s %d.%d",type,atr->hist[2]&0x0F,atr->hist[2]>>4);
00213   
00214   ResetIdSet();
00215   unsigned char buff[MAX_LEN];
00216   if(!IsoRead(ins0e,buff) || !Status()) {
00217     PRINTF(L_SC_ERROR,"reading card serial failed");
00218     return false;
00219     }
00220   SetCard(new cCardSeca(&buff[2]));
00221   PRINTF(L_SC_INIT,"card serial number: %llu",Bin2LongLong(&buff[2],6));
00222   infoStr.Printf("Type: %s %d.%d  Serial: %llu\n",type,atr->hist[2]&0x0F,atr->hist[2]>>4,Bin2LongLong(&buff[2],6));
00223 
00224   if(!IsoRead(ins16,buff) || !Status()) {
00225     PRINTF(L_SC_ERROR,"reading provider map failed");
00226     return false;
00227     }
00228   int provMap=buff[2]*256+buff[3];
00229   if(LOG(L_SC_INIT)) {
00230     int n=0, i=provMap;
00231     do { n+=i&1; i>>=1; } while(i);
00232     PRINTF(L_SC_INIT,"card has %d providers (0x%04x)",n,provMap);
00233     }
00234 
00235   for(int i=0 ; i<16 ; i++) {
00236     if(provMap&(1<<i)) {
00237       PRINTF(L_SC_INIT,"reading info for provider index %d",i);
00238       ins12[2]=i;
00239       if(!IsoRead(ins12,buff) || !Status()) {
00240         PRINTF(L_SC_ERROR,"reading provider info failed");
00241         return false;
00242         }
00243       ins32[2]=i;
00244       static const unsigned char ins34data[] = { 0x00,0x00,0x00 };
00245       if(!IsoWrite(ins34,ins34data) || !Status() ||
00246          !IsoRead(ins32,&buff[ins12[4]]) || !Status())
00247         memset(&buff[ins12[4]],0xFF,ins32[4]); // fake PBM response if card doesn't support command
00248 
00249       struct SecaProvInfo *spi=(struct SecaProvInfo *)buff;
00250       cProviderScSeca *p=new cProviderScSeca(spi->prov,spi->sa);
00251       if(p) {
00252         AddProv(p);
00253         p->index=i;
00254         strn0cpy(p->name,spi->name,sizeof(p->name));
00255         memcpy(p->date,spi->date,sizeof(p->date));
00256         memcpy(p->pbm,spi->pbm,sizeof(p->pbm));
00257         }
00258       infoStr.Printf("Prov %x (%.16s) until %s\n",p->provId[0]*256+p->provId[1],p->name,Date(p->date));
00259       char str[20];
00260       PRINTF(L_SC_INIT,"provider 0x%02x%02x '%.16s' expires %s pbm %s",
00261              p->provId[0],p->provId[1],p->name,Date(p->date),HexStr(str,p->pbm,sizeof(p->pbm)));
00262       }
00263     }
00264 
00265   infoStr.Finish();
00266   return true;
00267 }
00268 
00269 const char *cSmartCardSeca::Date(const unsigned char *date)
00270 {
00271   SECADATE(datebuff,sizeof(datebuff),date);
00272   return datebuff;
00273 }
00274 
00275 bool cSmartCardSeca::CheckAccess(const unsigned char *data, const cProviderScSeca *p)
00276 {
00277   struct SecaChannelInfo *sci=(struct SecaChannelInfo*)data;
00278   if(sci->date[0]>p->date[0] || (sci->date[0]==p->date[0] && sci->date[1]>p->date[1]))
00279     return false;
00280   char str[20];  
00281   PRINTF(L_SC_PROC,"channelinfo date %s pbm %s",Date(sci->date),HexStr(str,sci->pbm,sizeof(sci->pbm)));
00282   int result=0;
00283   for(int i=7; i>=0; i--) // check pbm (is this right? seems to work though)
00284     result|=(sci->pbm[i]&p->pbm[i]);
00285   return (result!=0);
00286 }
00287 
00288 bool cSmartCardSeca::Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
00289 {
00290   static unsigned char ins3c[] = { 0xC1,0x3c,0x00,0x00,0x00 }; // coding cw
00291   static unsigned char ins3a[] = { 0xC1,0x3a,0x00,0x00,0x10 }; // decoding cw    
00292   static unsigned char ins30[] = { 0xC1,0x30,0x00,0x02,0x09 };
00293   static unsigned char ins30data[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF }; 
00294 
00295   cProviderScSeca *p=(cProviderScSeca *)FindProv(data);
00296   if(p && ecm->Data()) {
00297     PRINTF(L_SC_PROC,"provider 0x%04x index %d '%.16s' (expires %s)",cParseSeca::ProvId(data),p->index,p->name,Date(p->date));
00298     if(CheckAccess(ecm->Data(),p)) {
00299       const unsigned char *payload;
00300       ins3c[2]=p->index | (cParseSeca::SysMode(data) & 0xF0);
00301       ins3c[3]=cParseSeca::KeyNr(data);
00302       ins3c[4]=cParseSeca::Payload(data,&payload);
00303       if(IsoWrite(ins3c,payload)) {
00304         bool r;
00305         if(sb[0]==0x90 && sb[1]==0x1A) // need to use token
00306           r=IsoWrite(ins30,ins30data) && Status() && IsoWrite(ins3c,payload) && Status();
00307         else
00308           r=Status();
00309         if(r && IsoRead(ins3a,cw) && Status())
00310           return true;
00311         }
00312       }
00313     else PRINTF(L_SC_ERROR,"update your subscription to view this channel");
00314     }
00315   return false;
00316 }
00317 
00318 bool cSmartCardSeca::Update(int pid, int caid, const unsigned char *data)
00319 {
00320   static unsigned char ins40[] = { 0xC1,0x40,0x00,0x00,0x00 }; 
00321 
00322   if(blocker==0 || (data[0]==0x82 && blocker==2) || (data[0]==0x84 && blocker==1)) {
00323     cProviderScSeca *p=(cProviderScSeca *)FindProv(data);
00324     if(p && MatchEMM(data)) {
00325       PRINTF(L_SC_PROC,"got %s update",data[0]==0x82?"UNIQUE":"SHARED");
00326       const unsigned char *payload;
00327       ins40[2]=p->index | (cParseSeca::SysMode(data) & 0xF0);
00328       ins40[3]=cParseSeca::KeyNr(data);
00329       ins40[4]=cParseSeca::Payload(data,&payload);
00330       if(IsoWrite(ins40,payload) && Status()) return Init();
00331       }
00332     }
00333   return false;
00334 }
00335 
00336 // -- cSmartCardLinkSeca -------------------------------------------------------
00337 
00338 class cSmartCardLinkSeca : public cSmartCardLink {
00339 public:
00340   cSmartCardLinkSeca(void):cSmartCardLink(SC_NAME,SC_ID) {}
00341   virtual cSmartCard *Create(void) { return new cSmartCardSeca(); }
00342   };
00343 
00344 static cSmartCardLinkSeca staticScInit;
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines