]> www.vanbest.org Git - sasc-ng.git/commitdiff
sc-nagra: code rework based on OSCAM rev 933
authorleslie <unknown>
Mon, 21 Dec 2009 14:04:37 +0000 (15:04 +0100)
committerleslie <unknown>
Mon, 21 Dec 2009 14:04:37 +0000 (15:04 +0100)
examples/smartcard.conf.example
po/de_DE.po
po/fi_FI.po
po/fr_FR.po
po/hu_HU.po
po/it_IT.po
po/nl_NL.po
po/pl_PL.po
po/ru_RU.po
po/sv_SE.po
systems/sc-nagra/sc-nagra.c

index 92a4f692a962891b1e04a9c95a248a26e4a2429f..a659d0fe6d7e1a64be1d0a8d474c5969c3cbb570 100644 (file)
@@ -20,18 +20,18 @@ videoguard2: BOXID 11223344
 ;
 ; boxkey and modulus for camcrypt
 ;
-; prvid  - provider ID (2 byte = 4 chars)
+; cardid - card ID / serial (4 byte = 8 chars)
 ; boxkey - boxkey (8 byte = 16 chars)
-; mod    - modulus (64 byte = 128 chars)
+; mod    - modulus (big endian) (64 byte = 128 chars)
 ;
-; If IRDMOD is present, the given mod is used to decrypt the cammod from DT08
-; data. If IRDMOD is obmited, the given mod is used as cammod directly
+; For a card with dt08 you should give the IRDMOD to decrypt the dt08.
+; Otherwise the CARDMOD is used directly for session key negotiation.
 ;
-; nagra: [prvid] IRDMOD boxkey mod
-nagra: [0501] IRDMOD 0011223344556677 00112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233
+; nagra: cardid boxkey IRDMOD mod
+nagra: 12345678 0011223344556677 IRDMOD 00112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233
 ;
-; nagra: [prvid] boxkey mod
-nagra: [1201] 0011223344556677 00112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233
+; nagra: cardid boxkey CARDMOD mod
+nagra: 12345678 0011223344556677 CARDMOD 00112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233
 
 ; Cryptoworks
 ;
index 95350dbb4bede86b5f8f36284928eaf28a8ffdd7..77030a9eb6b8ac5af2a2756cb520d9e6890b515e 100644 (file)
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 1.5.9\n"
 "Report-Msgid-Bugs-To: <noone@nowhere.org>\n"
-"POT-Creation-Date: 2009-09-18 08:17-0400\n"
+"POT-Creation-Date: 2009-12-21 08:38-0500\n"
 "PO-Revision-Date: 2007-08-27 12:45+0200\n"
 "Last-Translator: somebody\n"
 "Language-Team: somebody\n"
@@ -205,6 +205,9 @@ msgstr "ausschalten"
 msgid "SC-Cryptoworks: Parental rating"
 msgstr "SC-Cryptoworks: Altersbeschränkung"
 
+msgid "SC-Nagra: use T14 Nagra mode"
+msgstr "SC-Nagra: T14 Nagra Modus benutzen"
+
 msgid "allow ALL"
 msgstr "alle erlauben"
 
index 69ea68b275b33bcb5b5b5c87b796902b581b74e3..1b79490df357cafcc849154a9a65aff80b9702c6 100644 (file)
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 1.5.9\n"
 "Report-Msgid-Bugs-To: <noone@nowhere.org>\n"
-"POT-Creation-Date: 2009-09-18 08:17-0400\n"
+"POT-Creation-Date: 2009-12-21 08:38-0500\n"
 "PO-Revision-Date: 2008-11-19 13:45+0200\n"
 "Last-Translator: somebody\n"
 "Language-Team: somebody\n"
@@ -205,6 +205,9 @@ msgstr "poista"
 msgid "SC-Cryptoworks: Parental rating"
 msgstr "SC-Cryptoworks: Ikäraja"
 
+msgid "SC-Nagra: use T14 Nagra mode"
+msgstr ""
+
 msgid "allow ALL"
 msgstr "salli kaikki"
 
index adf4681ff9e125c3025e732e71ebfbcbaae3d899..89011d963025bbc4d639637b675f8b48f2c35364 100644 (file)
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 1.5.9\n"
 "Report-Msgid-Bugs-To: <noone@nowhere.org>\n"
-"POT-Creation-Date: 2009-09-18 08:17-0400\n"
+"POT-Creation-Date: 2009-12-21 08:38-0500\n"
 "PO-Revision-Date: 2007-08-27 12:45+0200\n"
 "Last-Translator: somebody\n"
 "Language-Team: somebody\n"
@@ -205,6 +205,9 @@ msgstr "d
 msgid "SC-Cryptoworks: Parental rating"
 msgstr "SC-Cryptoworks: Contrôle parentale"
 
+msgid "SC-Nagra: use T14 Nagra mode"
+msgstr ""
+
 msgid "allow ALL"
 msgstr "permettre TOUS"
 
index f03382a91896a9b81abd29ac918685bd74f2e562..56d8506ff96640ce7c09d333d5446bb6c9cdbad1 100644 (file)
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 1.5.11\n"
 "Report-Msgid-Bugs-To: <noone@nowhere.org>\n"
-"POT-Creation-Date: 2009-09-18 08:17-0400\n"
+"POT-Creation-Date: 2009-12-21 08:38-0500\n"
 "PO-Revision-Date: 2009-05-17 11:09+0800\n"
 "Last-Translator: jv\n"
 "Language-Team: somebody\n"
@@ -205,6 +205,9 @@ msgstr "kikapcsolva"
 msgid "SC-Cryptoworks: Parental rating"
 msgstr "SC-Cryptoworks: Szüloi zár"
 
+msgid "SC-Nagra: use T14 Nagra mode"
+msgstr ""
+
 msgid "allow ALL"
 msgstr "engedélyezd MIND"
 
index 3d7a66dbf83b250c40bc40b712399e00d2996038..a2d8fce591cfddd660f9cf553941cb36b9710a20 100644 (file)
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 1.5.9\n"
 "Report-Msgid-Bugs-To: <noone@nowhere.org>\n"
-"POT-Creation-Date: 2009-09-18 08:17-0400\n"
+"POT-Creation-Date: 2009-12-21 08:38-0500\n"
 "PO-Revision-Date: 2008-11-19 12:23+0200\n"
 "Last-Translator: somebody\n"
 "Language-Team: somebody\n"
@@ -205,6 +205,9 @@ msgstr "disabilita"
 msgid "SC-Cryptoworks: Parental rating"
 msgstr "SC-Cryptoworks: Filtro famiglia"
 
+msgid "SC-Nagra: use T14 Nagra mode"
+msgstr ""
+
 msgid "allow ALL"
 msgstr "permetti TUTTI"
 
index eec152266154e4b1ab0af41f131bd7ca9fb0092e..0bec0c80d99a51e26acb1dca08b56938d5c7bcb8 100644 (file)
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 1.5.9\n"
 "Report-Msgid-Bugs-To: <noone@nowhere.org>\n"
-"POT-Creation-Date: 2009-09-18 08:17-0400\n"
+"POT-Creation-Date: 2009-12-21 08:38-0500\n"
 "PO-Revision-Date: 2007-08-27 12:45+0200\n"
 "Last-Translator: somebody\n"
 "Language-Team: somebody\n"
@@ -205,6 +205,9 @@ msgstr "uitschakelen"
 msgid "SC-Cryptoworks: Parental rating"
 msgstr "SC-Cryptoworks: Leeftijdsadvies"
 
+msgid "SC-Nagra: use T14 Nagra mode"
+msgstr ""
+
 msgid "allow ALL"
 msgstr "ALLES toestaan"
 
index 8dfe30a431f2e0f51311b8723ff2e245f937b286..dbd3afb9ea84324d32eefc69a4657df6ac906d0d 100644 (file)
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 1.5.9\n"
 "Report-Msgid-Bugs-To: <noone@nowhere.org>\n"
-"POT-Creation-Date: 2009-09-18 08:17-0400\n"
+"POT-Creation-Date: 2009-12-21 08:38-0500\n"
 "PO-Revision-Date: 2007-08-27 12:45+0200\n"
 "Last-Translator: somebody\n"
 "Language-Team: somebody\n"
@@ -205,6 +205,9 @@ msgstr "wy
 msgid "SC-Cryptoworks: Parental rating"
 msgstr "SC-Cryptoworks: wska¼nik rodzica"
 
+msgid "SC-Nagra: use T14 Nagra mode"
+msgstr ""
+
 msgid "allow ALL"
 msgstr "dopu¶æ wszystkie"
 
index 5a9c37f6b0195fd71e0006e392710bf13e55ea1c..6e2ab430c3779aee88e80f1d2ed22b6ddbc31485 100644 (file)
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 1.5.9\n"
 "Report-Msgid-Bugs-To: <noone@nowhere.org>\n"
-"POT-Creation-Date: 2009-09-18 08:17-0400\n"
+"POT-Creation-Date: 2009-12-21 08:38-0500\n"
 "PO-Revision-Date: 2009-05-14 19:33+0100\n"
 "Last-Translator: somebody\n"
 "Language-Team: ru\n"
@@ -206,6 +206,9 @@ msgstr "
 msgid "SC-Cryptoworks: Parental rating"
 msgstr "SC-Cryptoworks: ÒÞ×àÐáâÝÞÕ ÞÓàÐÝØçÕÝØÕ"
 
+msgid "SC-Nagra: use T14 Nagra mode"
+msgstr ""
+
 msgid "allow ALL"
 msgstr "ÒáÕ àÐ×àÕèØâì"
 
index 08d2567ce03b5e7ac8aeefa584873346ab513b02..e3d6dfd6036e8a93cb3c70a2cde14c58eb6b0ce3 100644 (file)
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: VDR 1.5.9\n"
 "Report-Msgid-Bugs-To: <noone@nowhere.org>\n"
-"POT-Creation-Date: 2009-09-18 08:17-0400\n"
+"POT-Creation-Date: 2009-12-21 08:38-0500\n"
 "PO-Revision-Date: 2007-08-27 12:45+0200\n"
 "Last-Translator: somebody\n"
 "Language-Team: somebody\n"
@@ -205,6 +205,9 @@ msgstr ""
 msgid "SC-Cryptoworks: Parental rating"
 msgstr ""
 
+msgid "SC-Nagra: use T14 Nagra mode"
+msgstr ""
+
 msgid "allow ALL"
 msgstr ""
 
index 3ce16c521232f8f1e3993416ff1b2b2ec99d5f9d..d464fc395f1610d3bb044b9d8cefce9d5b01b4b1 100644 (file)
 
 #include "system-common.h"
 #include "smartcard.h"
+#include "parse.h"
 #include "crypto.h"
 #include "helper.h"
+#include "opts.h"
 #include "log-sc.h"
 #include "log-core.h"
 #include "version.h"
@@ -52,6 +54,8 @@ static const struct LogModule lm_sc = {
   };
 ADD_MODULE(L_SC,lm_sc)
 
+static int allowT14=0;
+
 // -- cSystemScNagra ---------------------------------------------------------------------
 
 class cSystemScNagra : public cSystemScCore {
@@ -79,12 +83,20 @@ static cSystemLinkScNagra staticInit;
 cSystemLinkScNagra::cSystemLinkScNagra(void)
 :cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
 {
+  opts=new cOpts(SYSTEM_NAME,1);
+  opts->Add(new cOptBool("AllowT14",trNOOP("SC-Nagra: use T14 Nagra mode"),&allowT14));
   Feature.NeedsSmartCard();
 }
 
 bool cSystemLinkScNagra::CanHandle(unsigned short SysId)
 {
-  return smartcards.HaveCard(SC_ID) && ((SysId&SYSTEM_MASK)==SYSTEM_NAGRA && (SysId&0xFF)>0);
+  bool res=false;
+  cSmartCard *card=smartcards.LockCard(SC_ID);
+  if(card) {
+    res=card->CanHandle(SysId);
+    smartcards.ReleaseCard(card);
+    }
+  return res;
 }
 
 // -- cCamCryptNagra ------------------------------------------------------------------
@@ -94,17 +106,20 @@ private:
   cIDEA idea;
   cRSA rsa;
   cBN camMod, camExp;
+  IdeaKS sessKey;
+  unsigned char cardid[4], boxkey[8], signature[8];
   bool hasMod;
-  unsigned char sk[16];
   //
+  void InitKey(unsigned char *key, const unsigned char *bk, const unsigned char *data);
   void Signature(unsigned char *sig, const unsigned char *key, const unsigned char *msg, int len);
 public:
   cCamCryptNagra(void);
-  void InitCamKey(unsigned char *key, const unsigned char *bk, const unsigned char *data);
+  void SetCardData(const unsigned char *id, const unsigned char *bk);
   void SetCamMod(BIGNUM *m);
-  bool DecryptCamMod(const unsigned char *dt08, const unsigned char *key, const unsigned char *key2, BIGNUM *m);
-  bool DecryptCamData(unsigned char *out, const unsigned char *key, const unsigned char *in);
-  bool DecryptCW(unsigned char *dcw, const unsigned char *in);
+  bool HasCamMod(void) { return hasMod; }
+  bool DecryptDT08(const unsigned char *dt08, const unsigned char *irdid, BIGNUM *irdmod);
+  bool MakeSessionKey(unsigned char *out, const unsigned char *in);
+  void DecryptCW(unsigned char *cw, const unsigned char *ecw1, const unsigned char *ecw2);
   };
 
 cCamCryptNagra::cCamCryptNagra(void)
@@ -113,22 +128,20 @@ cCamCryptNagra::cCamCryptNagra(void)
   BN_set_word(camExp,3);
 }
 
-void cCamCryptNagra::Signature(unsigned char *sig, const unsigned char *key, const unsigned char *msg, int len)
+void cCamCryptNagra::SetCardData(const unsigned char *id, const unsigned char *bk)
 {
-  unsigned char buff[16];
-  memcpy(buff,key,16);
-  for(int i=0; i<len; i+=8) {
-    IdeaKS ks;
-    idea.SetEncKey(buff,&ks);
-    idea.Encrypt(msg+i,8,buff,&ks,0);
-    xxor(buff,8,buff,&msg[i]);
-    //for(int j=7; j>=0; j--) buff[j]^=msg[i+j];
-    memcpy(&buff[8],buff,8);
-    }
-  memcpy(sig,buff,8);
+  memcpy(cardid,id,sizeof(cardid));
+  memcpy(boxkey,bk,sizeof(boxkey));
 }
 
-void cCamCryptNagra::InitCamKey(unsigned char *key, const unsigned char *bk, const unsigned char *data)
+void cCamCryptNagra::SetCamMod(BIGNUM *m)
+{
+  BN_copy(camMod,m);
+  memcpy(signature,boxkey,sizeof(signature));
+  hasMod=true;
+}
+
+void cCamCryptNagra::InitKey(unsigned char *key, const unsigned char *bk, const unsigned char *data)
 {
   memcpy(key,bk,8);
   int d=UINT32_LE(data);
@@ -138,74 +151,90 @@ void cCamCryptNagra::InitCamKey(unsigned char *key, const unsigned char *bk, con
   //for(int i=0; i<4; i++) key[i+12]=~data[i];
 }
 
-void cCamCryptNagra::SetCamMod(BIGNUM *m)
+void cCamCryptNagra::Signature(unsigned char *sig, const unsigned char *key, const unsigned char *msg, int len)
 {
-  BN_copy(camMod,m);
-  hasMod=true;
+  unsigned char buff[16];
+  memcpy(buff,key,16);
+  for(int i=0; i<len; i+=8) {
+    idea.Decrypt(msg+i,8,buff,buff,0);
+    xxor(buff,8,buff,&msg[i]);
+    memcpy(&buff[8],buff,8);
+    }
+  memcpy(sig,buff,8);
 }
 
-bool cCamCryptNagra::DecryptCamMod(const unsigned char *dt08, const unsigned char *key, const unsigned char *cardid, BIGNUM *m)
+bool cCamCryptNagra::DecryptDT08(const unsigned char *dt08, const unsigned char *irdid, BIGNUM *irdmod)
 {
   unsigned char buff[72];
-  if(rsa.RSA(buff,dt08+13,64,camExp,m)!=64) return false;
-  memcpy(buff+64,dt08+13+64,8);
-  buff[63]|=dt08[12]&0x80;
+  if(rsa.RSA(buff,dt08+1,64,camExp,irdmod)!=64) return false;
+  memcpy(buff+64,dt08+1+64,8);
+  buff[63]|=dt08[0]&0x80;
+  LDUMP(L_SC_PROC,buff,72,"DT08 after RSA");
+
+  unsigned char key[16];
+  InitKey(key,boxkey,irdid);
+  LDUMP(L_SC_PROC,key,16,"DT08 IDEA key");
   IdeaKS dks;
   idea.SetDecKey(key,&dks);
   idea.Decrypt(buff,72,&dks,0);
-  unsigned char sig[8];
-  memcpy(sig,buff,8);
+  LDUMP(L_SC_PROC,buff,72,"DT08 after IDEA");
 
+  memcpy(signature,buff,8);
+  LDUMP(L_SC_PROC,signature,8,"signature");
   memset(buff+0,0,4);
   memcpy(buff+4,cardid,4);
   Signature(buff,key,buff,72);
-  BN_bin2bn(buff+8,64,camMod);
-  if(memcmp(sig,buff,8)) {
-    PRINTF(L_SC_PROC,"DT08 signature failed. Check boxkey/IRD modulus");
+  LDUMP(L_SC_PROC,buff,8,"check sig");
+  if(memcmp(signature,buff,8)) {
+    PRINTF(L_SC_PROC,"DT08 signature failed");
     return false;
     }
+  BN_bin2bn(buff+8,64,camMod);
   hasMod=true;
   return true;
 }
 
-bool cCamCryptNagra::DecryptCamData(unsigned char *out, const unsigned char *key, const unsigned char *camdata)
+bool cCamCryptNagra::MakeSessionKey(unsigned char *out, const unsigned char *camdata)
 {
   if(!hasMod) return false;
   //decrypt $2A data here and prepare $2B reply
   if(rsa.RSA(out,camdata,64,camExp,camMod)!=64) return false;
-  Signature(sk,key,out,32);
-  memcpy(sk+8,sk,8);
-  Signature(sk+8,sk,out,32);;
+  LDUMP(L_SC_PROC,out,64,"CMD 2A after RSA");
+
+  unsigned char key[16], sess[16];
+  InitKey(key,signature,cardid);
+  LDUMP(L_SC_PROC,key,16,"first IDEA key");
+  Signature(sess,key,out,32);
+  memcpy(key,sess,8);
+  memcpy(key+8,sess,8);
+  LDUMP(L_SC_PROC,key,16,"second IDEA key");
+  Signature(sess+8,key,out,32);
+  LDUMP(L_SC_PROC,sess,16,"SESSION KEY");
+  idea.SetDecKey(sess,&sessKey);
+
   if(rsa.RSA(out,out,64,camExp,camMod)!=64) return false;
-  LDUMP(L_SC_PROC,sk,16,"established session key ->");
+  LDUMP(L_SC_PROC,out,64,"CMD 2B data");
   return true;
 }
 
-bool cCamCryptNagra::DecryptCW(unsigned char *dcw, const unsigned char *ecw)
+void cCamCryptNagra::DecryptCW(unsigned char *cw, const unsigned char *ecw1, const unsigned char *ecw2)
 {
-  const int nCw=ecw[4]/2;
-  if(nCw<10) return false;
-  IdeaKS ks;
-  ecw+=5;
-  idea.SetDecKey(sk,&ks);
-  memcpy(dcw+8,ecw    +2,8);
-  memcpy(dcw+0,ecw+nCw+2,8);
-  idea.Decrypt(dcw+0,8,&ks,0);
-  idea.Decrypt(dcw+8,8,&ks,0);
-  return true;
+  memcpy(cw+0,ecw1,8);
+  idea.Decrypt(cw+0,8,&sessKey,0);
+  memcpy(cw+8,ecw2,8);
+  idea.Decrypt(cw+8,8,&sessKey,0);
 }
 
 // -- cSmartCardDataNagra ------------------------------------------------------
 
 class cSmartCardDataNagra : public cSmartCardData {
 public:
-  bool global;
-  int provId;
-  unsigned char bk[8];
+  unsigned char cardid[4], bk[8];
   cBN mod;
+  bool isIrdMod;
   //
   cSmartCardDataNagra(void);
-  cSmartCardDataNagra(int ProvId, bool Global);
+  cSmartCardDataNagra(const unsigned char *id, bool irdmod);
   virtual bool Parse(const char *line);
   virtual bool Matches(cSmartCardData *param);
   };
@@ -213,89 +242,79 @@ public:
 cSmartCardDataNagra::cSmartCardDataNagra(void)
 :cSmartCardData(SC_ID) {}
 
-cSmartCardDataNagra::cSmartCardDataNagra(int ProvId, bool Global=false)
+cSmartCardDataNagra::cSmartCardDataNagra(const unsigned char *id, bool irdmod)
 :cSmartCardData(SC_ID)
 {
-  provId=ProvId; global=Global;
+  memcpy(cardid,id,sizeof(cardid));
+  isIrdMod=irdmod;
 }
 
 bool cSmartCardDataNagra::Matches(cSmartCardData *param)
 {
   cSmartCardDataNagra *cd=(cSmartCardDataNagra *)param;
-  return (cd->provId==provId) && (cd->global==global);
+  return !memcmp(cd->cardid,cardid,sizeof(cardid) && cd->isIrdMod==isIrdMod);
 }
 
 bool cSmartCardDataNagra::Parse(const char *line)
 {
-  unsigned char buff[512];
   line=skipspace(line);
-  if(*line++!='[' || GetHex(line,buff,2)!=2 || *(line=skipspace(line))!=']' ) {
-    PRINTF(L_CORE_LOAD,"smartcarddatanagra: format error: provider id");
+  if(GetHex(line,cardid,sizeof(cardid))!=sizeof(cardid)) {
+    PRINTF(L_CORE_LOAD,"smartcarddatanagra: format error: card id");
     return false;
     }
-  provId=buff[0]*256+buff[1];
-  line=skipspace(line+1);
-  if(!strncasecmp(line,"IRDMOD",6)) {
-    global=true;
-    line+=6;
-    }
-  if(GetHex(line,bk,sizeof(bk),false)<=0) {
+  if(GetHex(line,bk,sizeof(bk))!=sizeof(bk)) {
+  line=skipspace(line);
     PRINTF(L_CORE_LOAD,"smartcarddatanagra: format error: boxkey");
     return false;
     }
-  int l=GetHex(line,buff,sizeof(buff),false);
-  if(l!=64) {
-    PRINTF(L_CORE_LOAD,"smartcarddatanagra: format error: %s",global?"IRDMOD":"CAMMOD");
+  line=skipspace(line);
+  if(!strncasecmp(line,"IRDMOD",6)) { isIrdMod=true; line+=6; }
+  else if(!strncasecmp(line,"CARDMOD",7)) { isIrdMod=false; line+=7; }
+  else {
+    PRINTF(L_CORE_LOAD,"smartcarddatanagra: format error: IRDMOD/CARDMOD");
     return false;
     }
-  BN_bin2bn(buff,l,mod);
+  line=skipspace(line);
+  unsigned char buff[72];
+  if(GetHex(line,buff,64)!=64) {
+    PRINTF(L_CORE_LOAD,"smartcarddatanagra: format error: modulus");
+    return false;
+    }
+  BN_bin2bn(buff,64,mod);
   return true;
 }
 
 // -- cSmartCardNagra -----------------------------------------------------------------
 
-// ISO 7816 T=1 defines
-#define NAD(a)      (a[0])
-#define PCB(a)      (a[1])
-#define LEN(a)      (a[2])
-#define CLA(a)      (a[3])
-#define INS(a)      (a[4])
-#define P1(a)       (a[5])
-#define P2(a)       (a[6])
-#define L(a)        (a[7])
-
-// Nagra NAD and reply NAD
-#define N2_NAD      0x21
-#define N2_R_NAD    sn8(N2_NAD)
-#define N2_CLA      0xA0
-#define N2_INS      0xCA
-#define N2_P1       0x00
-#define N2_P2       0x00
-#define N2_CMD(a)   (a[8])
-#define N2_CLEN(a)  (a[9]) 
-#define N2_DATA(a)  (a[10]) 
-
 #define SETIFS      0xC1
-#define CAMDATA     0x08
 
 // Datatypes
 #define IRDINFO     0x00
+#define DT01        0x01
+#define DT04        0x04
 #define TIERS       0x05
+#define DT06        0x06
+#define CAMDATA     0x08
+
 #define MAX_REC     20
 
 // Card Status checks
-#define HAS_CW      ((status[1]&1)&&((status[3]&6)==6))
+#define HAS_CW()           ((state[2]&6)==6)
+#define RENEW_SESSIONKEY() (state[0]&224)
+#define SENDDATETIME()     (state[0]&16)
 
-class cSmartCardNagra : public cSmartCard, private cCamCryptNagra {
+class cSmartCardNagra : public cSmartCard, public cIdSet, private cCamCryptNagra {
 private:
-  unsigned char buff[MAX_LEN+1], status[4], cardId[4], irdId[4], block;
-  unsigned short provId[MAX_REC], irdProvId;
+  unsigned char buff[MAX_LEN+16], state[3], cardId[4], irdId[4], rominfo[15];
+  int caid, provId, block;
+  bool isTiger, isT14Nagra, swapCW;
   //
   bool GetCardStatus(void);
   bool GetDataType(unsigned char dt, int len, int shots=MAX_REC);
   bool ParseDataType(unsigned char dt);
-  bool DoCamExchange(const unsigned char *key);
-  void Date(const unsigned char *date, char *dt, char *ti);
+  bool DoCamExchange(void);
+  bool SendDateTimeCmd(void);
+  time_t Date(const unsigned char *data, char *buf, int len);
   bool DoBlkCmd(unsigned char cmd, int ilen, unsigned char res, int rlen, const unsigned char *data=0);
   bool SetIFS(unsigned char len);
 public:
@@ -303,6 +322,7 @@ public:
   virtual bool Init(void);
   virtual bool Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw);
   virtual bool Update(int pid, int caid, const unsigned char *data);
+  virtual bool CanHandle(unsigned short CaId);
   };
 
 static const struct StatusMsg msgs[] = {
@@ -317,15 +337,13 @@ static const struct CardConfig cardCfg = {
 
 cSmartCardNagra::cSmartCardNagra(void)
 :cSmartCard(&cardCfg,msgs)
-{
-  block=0;
-}
+{}
 
 bool cSmartCardNagra::SetIFS(unsigned char size)
 {
   unsigned char buf[5];
   // NAD, PCB, LEN
-  NAD(buf)=N2_NAD; PCB(buf)=SETIFS; LEN(buf)=1;
+  buf[0]=0x21; buf[1]=0xC1; buf[2]=0x01;
   // Information Field size
   buf[3]=size; buf[4]=XorSum(buf,4);
   if(SerWrite(buf,5)!=5) {
@@ -345,81 +363,150 @@ bool cSmartCardNagra::GetCardStatus(void)
     PRINTF(L_SC_ERROR,"failed to read card status");
     return false;
     }
-  memcpy(status,buff+5,4);
+  memcpy(state,buff+6,3);
   return true;
 }
 
+bool cSmartCardNagra::CanHandle(unsigned short CaId)
+{
+  return CaId==caid;
+}
+
 bool cSmartCardNagra::Init(void)
 {
-  static const unsigned char verifyBytes[] = { 'D','N','A','S','P','1' };
-  if(atr->T!=1 || (atr->histLen<8 && memcmp(atr->hist,verifyBytes,sizeof(verifyBytes)))) {
-    PRINTF(L_SC_INIT,"doesn't look like a nagra V2 card");
+  block=0; isTiger=isT14Nagra=swapCW=false;
+  caid=SYSTEM_NAGRA;
+  ResetIdSet();
+
+  static const unsigned char atrDNASP[] = { 'D','N','A','S','P' };
+  static const unsigned char atrTIGER[] = { 'T','I','G','E','R' };
+  static const unsigned char atrIRDET[] = { 'I','R','D','E','T','O' };
+  if(!memcmp(atr->hist,atrDNASP,sizeof(atrDNASP))) {
+    PRINTF(L_SC_INIT,"detected native T1 nagra card");
+    //if(!SetIFS(0xFE)) return false;
+    memcpy(rominfo,atr->hist,sizeof(rominfo));
+    }
+  else if(!memcmp(atr->hist,atrTIGER,sizeof(atrTIGER))) {
+    PRINTF(L_SC_INIT,"detected nagra tiger card");
+    //if(!SetIFS(0xFE)) return false;
+    memcpy(rominfo,atr->hist,sizeof(rominfo));
+    memset(cardId,0xFF,sizeof(cardId));
+    isTiger=true;
+    }
+  else if(!memcmp(atr->hist,atrIRDET,sizeof(atrIRDET))) {
+    PRINTF(L_SC_INIT,"detected tunneled T14 nagra card");
+    if(!allowT14) {
+      PRINTF(L_SC_INIT,"Nagra mode for T14 card disabled in setup");
+      return false;
+      }
+    PRINTF(L_SC_INIT,"using nagra mode");
+    isT14Nagra=true;
+    if(!DoBlkCmd(0x10,0x02,0x90,0x11)) {
+      PRINTF(L_SC_ERROR,"get rom version failed");
+      return false;
+      }
+    memcpy(rominfo,&buff[2],15);
+    }
+  else {
+    PRINTF(L_SC_INIT,"doesn't look like a nagra card");
     return false;
     }
 
   infoStr.Begin();
   infoStr.Strcat("Nagra smartcard\n");
-
   char rom[12], rev[12];
-  snprintf(rom,sizeof(rom),"%.3s",(char*)&atr->hist[5]);
-  snprintf(rev,sizeof(rev),"%.3s",(char*)&atr->hist[12]);
-
-  PRINTF(L_SC_INIT,"card version: %s revision: %s",rom,rev);
-  infoStr.Printf("Card v.%s Rev.%s\n",rom,rev);
-
-  if(!GetCardStatus() || !SetIFS(0x80)) return false;
-  // get UA/CardID here
-  if(!DoBlkCmd(0x12,0x02,0x92,0x06) || !Status()) return false;
-
-  memcpy(cardId,buff+5,4);
-  if(!GetDataType(IRDINFO,0x39) || !GetDataType(0x04,0x44)) return false;
-  infoStr.Printf("Tiers\n");
-  infoStr.Printf("|id  |tier low|tier high| dates    |\n");
-  infoStr.Printf("+----+--------+---------+----------+\n");
-
-  if(!GetDataType(TIERS,0x57))
-    PRINTF(L_SC_ERROR,"failed to get tiers");
-    
-  if(!GetDataType(CAMDATA,0x55,1)) return false;
+  snprintf(rom,sizeof(rom),"%c%c%c%c%c%c%c%c",rominfo[0],rominfo[1],rominfo[2],rominfo[3],rominfo[4],rominfo[5],rominfo[6],rominfo[7]);
+  snprintf(rev,sizeof(rev),"%c%c%c%c%c%c",rominfo[9],rominfo[10],rominfo[11],rominfo[12],rominfo[13],rominfo[14]);
+  PRINTF(L_SC_INIT,"rom version: %s revision: %s",rom,rev);
+  infoStr.Printf("Rom %s Rev %s\n",rom,rev);
+
+  if(!isTiger) {
+    GetCardStatus();
+    if(!DoBlkCmd(0x12,0x02,0x92,0x06) || !Status()) return false;
+    memcpy(cardId,buff+5,4);
+
+    if(!GetDataType(DT01,0x0E)) return false;
+    GetCardStatus();
+    if(!GetDataType(IRDINFO,0x39)) return false;
+    GetCardStatus();
+    if(!GetDataType(CAMDATA,0x55,0x10)) return false;
+    GetCardStatus();
+    if(!GetDataType(DT04,0x44)) return false;
+    GetCardStatus();
+    if(!memcmp(rominfo+5,"181",3)) {
+      infoStr.Printf("Tiers\n");
+      infoStr.Printf("|id  |chid| dates               |\n");
+      infoStr.Printf("+----+----+---------------------+\n");
+      if(!GetDataType(TIERS,0x57)) return false;
+      GetCardStatus();
+      }
+    if(!GetDataType(DT06,0x16)) return false;
+    GetCardStatus();
+    }
+  SetCard(new cCardNagra2(cardId));
 
-  unsigned char key[16];
-  cSmartCardDataNagra *entry=0;
-  bool sessOk=false;
-  if(buff[5]!=0 && irdProvId==((buff[10]*256)|buff[11])) { // prepare DT08 data
-    cSmartCardDataNagra cd(irdProvId,true);
-    if(!(entry=(cSmartCardDataNagra *)smartcards.FindCardData(&cd))) {
-      PRINTF(L_GEN_WARN,"didn't find smartcard Nagra IRD modulus");
+  if(!HasCamMod()) {
+    cSmartCardDataNagra cd(cardId,false);
+    cSmartCardDataNagra *entry=(cSmartCardDataNagra *)smartcards.FindCardData(&cd);
+    if(entry) {
+      SetCardData(cardId,entry->bk);
+      SetCamMod(entry->mod);
       }
     else {
-      InitCamKey(key,entry->bk,irdId);
-      if(DecryptCamMod(buff+3,key,cardId,entry->mod)) sessOk=true;
+      PRINTF(L_SC_ERROR,"can't find CARD modulus");
+      return false;
       }
     }
+  if(!DoCamExchange()) return false;
+  infoStr.Finish();
+  return true;
+}
+
+bool cSmartCardNagra::DoCamExchange(void)
+{
+  if(!isTiger) {
+    if(!DoBlkCmd(0x2A,0x02,0xAA,0x42) || !Status()) return false;
+    }
   else {
-    cSmartCardDataNagra cd(irdProvId);
-    if(!(entry=(cSmartCardDataNagra *)smartcards.FindCardData(&cd))) {
-      PRINTF(L_GEN_WARN,"didn't find smartcard Nagra CAM modulus");
-      }
-    else {
-      InitCamKey(key,entry->bk,cardId);
-      SetCamMod(entry->mod);
-      if(GetDataType(0x08,0x03,1)) sessOk=true;;
+    static const unsigned char d1[] = { 0xd2 };
+    if(!DoBlkCmd(0xD1,0x03,0x51,0x42,d1) || !Status()) return false;
+    }
+  unsigned char res[64];
+  if(!MakeSessionKey(res,buff+5)) return false;
+  if(!isTiger) {
+    if(!DoBlkCmd(0x2B,0x42,0xAB,0x02,res) ||
+       !Status() ||
+       !GetCardStatus()) return false;
+    if(SENDDATETIME())
+       SendDateTimeCmd();
+    if(RENEW_SESSIONKEY()) {
+      PRINTF(L_SC_ERROR,"Session key negotiation failed!");
+      return false;
       }
     }
-  if(sessOk) DoCamExchange(key);
-  infoStr.Finish();
+  else {
+    if(!DoBlkCmd(0xD2,0x42,0x52,0x03,res) || !Status()) return false;
+    }
+  return true;
+}
+
+bool cSmartCardNagra::SendDateTimeCmd(void)
+{
+  DoBlkCmd(0xC8,0x02,0xB8,0x06);
   return true;
 }
 
 bool cSmartCardNagra::GetDataType(unsigned char dt, int len, int shots)
 {
+  bool isdt8 = (dt==0x08);
   for(int i=0; i<shots; i++) {
     if(!DoBlkCmd(0x22,0x03,0xA2,len,&dt) || !Status()) {
      PRINTF(L_SC_ERROR,"failed to get datatype %02X",dt);
      return false;
      }
-    if(buff[5]==0) return true;
+    if(buff[5]==0 && !isdt8) break;
     if(!ParseDataType(dt&0x0F)) return false;
+    if(isdt8 && buff[14]==0x49) break;
     dt|=0x80; // get next item
     }
   return true;
@@ -428,108 +515,149 @@ bool cSmartCardNagra::GetDataType(unsigned char dt, int len, int shots)
 bool cSmartCardNagra::ParseDataType(unsigned char dt)
 {
   switch(dt) {
-   case IRDINFO:
-     {
-     irdProvId=(buff[10]*256)|buff[11];
-     char id[12];
-     memcpy(irdId,buff+17,4);
-     LDUMP(L_SC_INIT,irdId,4,"IRD system-ID: %04X, IRD ID:",irdProvId);
-     HexStr(id,irdId,sizeof(irdId));
-     infoStr.Printf("IRD system-ID: %04X\nIRD ID: %s\n",irdProvId,id);
-     return true;
-     }
-   case TIERS:
-     if(buff[7]==0x88 || buff[7]==0x08  || buff[7]==0x0C) {
-       int id=(buff[10]*256)|buff[11];
-       int tLowId=(buff[20]*256)|buff[21];
-       int tHiId=(buff[31]*256)|buff[32];
-       char date1[12], time1[12], date2[12], time2[12];
-       Date(buff+23,date1,time1);
-       Date(buff+27,date2,time2);
-
-       infoStr.Printf("|%04X|%04X    |%04X     |%s|\n",id,tLowId,tHiId,date1);
-       infoStr.Printf("|    |        |         |%s|\n",date2);
-       PRINTF(L_SC_INIT,"|%04X|%04X|%04X|%s|%s|",id,tLowId,tHiId,date1,time1);
-       PRINTF(L_SC_INIT,"|    |    |    |%s|%s|",date2,time2);
-       }
-   default:
-     return true;
-   }
-  return false;
-}
-
-bool cSmartCardNagra::DoCamExchange(const unsigned char *key)
-{
-  if(!GetCardStatus()) return false;
-  if(!DoBlkCmd(0x2A,0x02,0xAA,0x42) || !Status()) return false;
-  if(buff[4]!=64) {
-    PRINTF(L_SC_ERROR,"reading CAM 2A data failed");
-    return false;
+    case IRDINFO:
+      {
+      provId=(buff[10]*256)|buff[11];
+      caid=SYSTEM_NAGRA+buff[14];
+      memcpy(irdId,buff+17,4);
+      char id[12];
+      LDUMP(L_SC_INIT,irdId,4,"CAID: %04X PROV: %04X, IRD ID:",caid,provId);
+      HexStr(id,irdId,sizeof(irdId));
+      infoStr.Printf("CAID: %04X PROV: %04X\nIRD ID: %s\n",caid,provId,id);
+      break;
+      }
+    case TIERS:
+      {
+      int chid=(buff[14]*256)|buff[15];
+      if(chid) {
+        int id=(buff[10]*256)|buff[11];
+        char ds[16], de[16];
+        Date(buff+23,ds,sizeof(ds));
+        Date(buff+16,de,sizeof(de));
+        infoStr.Printf("|%04X|%04X|%s-%s|\n",id,chid,ds,de);
+        PRINTF(L_SC_INIT,"|%04X|%04X|%s-%s|",id,chid,ds,de);
+        }
+      break;
+      }
+    case CAMDATA:
+      if(buff[14]==0x49) {
+        cSmartCardDataNagra cd(cardId,true);
+        cSmartCardDataNagra *entry=(cSmartCardDataNagra *)smartcards.FindCardData(&cd);
+        if(entry) {
+          SetCardData(cardId,entry->bk);
+          return DecryptDT08(buff+15,irdId,entry->mod);
+          }
+        else {
+          PRINTF(L_SC_ERROR,"can't find IRD modulus");
+          return false;
+          }
+        }
+      break;
     }
-  unsigned char res[64];
-  if(!DecryptCamData(res,key,buff+5)) return false;
-  if(!DoBlkCmd(0x2B,0x42,0xAB,0x02,res) || !Status()) return false;
   return true;
 }
 
-void cSmartCardNagra::Date(const unsigned char *data, char *dt, char *ti)
+time_t cSmartCardNagra::Date(const unsigned char *data, char *buf, int len)
 {
-  int date=(data[0]<<8)|data[1];
-  int time=(data[2]<<8)|data[3];
-  struct tm t;
-  memset(&t,0,sizeof(struct tm));
-  t.tm_min =0;//-300;
-  t.tm_year=92;
-  t.tm_mday=date + 1;
-  t.tm_sec =(time - 1) * 2;
-  mktime(&t);
-  snprintf(dt,11,"%.2d.%.2d.%.4d",t.tm_mon+1,t.tm_mday,t.tm_year+1900);
-  snprintf(ti,9,"%.2d:%.2d:%.2d",t.tm_hour,t.tm_min,t.tm_sec);
+  int day=((data[0]<<8)|data[1])-0x7f7;
+  time_t ut=870393600L+day*(24*3600);
+  if(buf) {
+    struct tm rt;
+    struct tm *t=gmtime_r(&ut,&rt);
+    if(t) snprintf(buf,len,"%04d/%02d/%02d",t->tm_year+1900,t->tm_mon+1,t->tm_mday);
+    else buf[0]=0;
+    }
+  return ut;
 }
 
 bool cSmartCardNagra::Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
 {
-  if(DoBlkCmd(data[3],data[4]+2,0x87,0x02,data+3+2) && Status()) {
-    cCondWait::SleepMs(100);
-    if(GetCardStatus() && HAS_CW &&
-       DoBlkCmd(0x1C,0x02,0x9C,0x36) && Status() &&
-       DecryptCW(cw,buff)) return true;
+  if(!isTiger) {
+    if(!DoBlkCmd(data[3],data[4]+2,0x87,0x02,data+3+2) || !Status()) {
+      PRINTF(L_SC_ERROR,"ECM cmd failed, retrying");
+      if(!DoBlkCmd(data[3],data[4]+2,0x87,0x02,data+3+2) || !Status()) {
+        PRINTF(L_SC_ERROR,"ECM cmd failed");
+        return false;
+        }
+      }
+    cCondWait::SleepMs(5);
+    for(int retry=0; !GetCardStatus() && retry<5; retry++) cCondWait::SleepMs(5);
+    cCondWait::SleepMs(5);
+    if(HAS_CW() && DoBlkCmd(0x1C,0x02,0x9C,0x36) && Status()) {
+      DecryptCW(cw,buff+33,buff+7);
+      if(swapCW) {
+        unsigned char tt[8];
+        memcpy(&tt[0],&cw[0],8);
+        memcpy(&cw[0],&cw[8],8);
+        memcpy(&cw[8],&tt[0],8);
+        }
+      return true;
+      }
+    }
+  else {
+    if(DoBlkCmd(0xD3,data[4]+2,0x53,0x16,data+3+2)) {
+      DecryptCW(cw,buff+17,buff+9);
+      return true;
+      }
     }
   return false;
 }
 
 bool cSmartCardNagra::Update(int pid, int caid, const unsigned char *data)
 {
-  if(DoBlkCmd(data[8],data[9]+2,0x84,0x02,data+8+2) && Status()) {
-    cCondWait::SleepMs(500);
-    if(GetCardStatus()) return true;
+  if(MatchEMM(data)) {
+    if(DoBlkCmd(data[8],data[9]+2,0x84,0x02,data+8+2) && Status()) {
+      cCondWait::SleepMs(300);
+      GetCardStatus();
+      cCondWait::SleepMs(10);
+      if(RENEW_SESSIONKEY()) DoCamExchange();
+      if(SENDDATETIME()) SendDateTimeCmd();
+      }
+    return true;
     }
   return false;
 }
 
 bool cSmartCardNagra::DoBlkCmd(unsigned char cmd, int ilen, unsigned char res, int rlen, const unsigned char *data)
 {
-  unsigned char msg[MAX_LEN+3+1];
-
-  NAD(msg)=N2_NAD; PCB(msg)=0; PCB(msg)^=block; block^=0x40; LEN(msg)=ilen+6;
-  CLA(msg)=N2_CLA; INS(msg)=N2_INS; P1(msg)=N2_P1; P2(msg)=N2_P2; L(msg)=ilen;
-
+  /*
+  here we build the command related to the protocol T1 for ROM142 or T14 for ROM181
+  the only different that i know is the command length byte msg[4], this msg[4]+=1 by a ROM181 smartcard (_nighti_)
+  one example for the cmd$C0
+  T14 protocol:       01 A0 CA 00 00 03 C0 00 06 91
+  T1  protocol: 21 00 08 A0 CA 00 00 02 C0 00 06 87
+  */
+  unsigned char msg[MAX_LEN+16];
+  static char nagra_head[] = { 0xA0,0xCA,0x00,0x00 };
+
+  memset(msg,0,sizeof(msg));
+  int c=0;
+  if(!isT14Nagra) {
+    msg[c++]=0x21;
+    msg[c++]=block; block^=0x40;
+    msg[c++]=ilen+6;
+    }
+  else {
+    msg[c++]=0x01;
+    }
+  memcpy(msg+c,nagra_head,sizeof(nagra_head)); c+=sizeof(nagra_head);
+  msg[c]=ilen;
+  msg[c+1]=cmd;
   int dlen=ilen-2;
   if(dlen<0) {
     PRINTF(L_SC_ERROR,"invalid data length encountered");
     return false;
     }
-
-  N2_CMD(msg)=cmd; N2_CLEN(msg)=dlen;
-
-  if(data && dlen>0) memcpy(&N2_DATA(msg),data,dlen);
-  int msglen=LEN(msg)+3;
-  msg[msglen-1]=rlen;
-  msg[msglen]=XorSum(msg,msglen);
-  msglen++;
-
-  if(SerWrite(msg,msglen)==msglen) {
-    LDUMP(L_CORE_SC,msg,msglen,"NAGRA: <-");
+  msg[c+2]=dlen;
+  if(isT14Nagra) msg[c]++;
+  if(isTiger) { msg[c]--; msg[c+2]--; }
+  c+=3;
+  if(data && dlen>0) { memcpy(msg+c,data,dlen); c+=dlen; }
+  msg[c++]=rlen;
+  msg[c]=XorSum(msg,c); c++;
+
+  if(SerWrite(msg,c)==c) {
+    LDUMP(L_CORE_SC,msg,c,"NAGRA: <-");
     cCondWait::SleepMs(10);
     if(SerRead(buff,3,cardCfg.workTO)!=3) {
       PRINTF(L_SC_ERROR,"reading back reply failed");
@@ -540,16 +668,21 @@ bool cSmartCardNagra::DoBlkCmd(unsigned char cmd, int ilen, unsigned char res, i
       PRINTF(L_SC_ERROR,"reading back information block failed");
       return false;
       }
-    if(XorSum(buff,reslen+3)) {
+    reslen+=3;
+    if(XorSum(buff,reslen)) {
       PRINTF(L_SC_ERROR,"checksum failed");
       return false;
       }
-    LDUMP(L_CORE_SC,buff,3+reslen,"NAGRA: ->");
+    LDUMP(L_CORE_SC,buff,reslen,"NAGRA: ->");
 
     if(buff[3]!=res) {
       PRINTF(L_SC_ERROR,"result not expected (%02X != %02X)",buff[3],res);
       return false;
       }
+    if(reslen-2!=rlen) {
+      PRINTF(L_SC_ERROR,"result length expected (%d != %d)",reslen-2,rlen);
+      return false;
+      }
     memcpy(sb,&buff[3+rlen],2);
     LDUMP(L_CORE_SC,sb,2,"NAGRA: ->");
     cCondWait::SleepMs(10);