|
SASC
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 <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;