/*
 * Softcam plugin to VDR (C++)
 *
 * This code is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
 */

#if defined(SECA) && defined(OPENSSL)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
//#include <byteswap.h>
#include <openssl/bn.h>
#include <openssl/sha.h>

#include "common.h"
//#include "sc.h"
#include "system.h"
#include "system-common.h"
#include "data.h"
#include "misc.h"
#include "helper.h"
#include "crypto.h"
#include "..\\main.h"

#define SYSTEM_SECA          0x0100

#define SYSTEM_NAME          "Seca"
#define SYSTEM_PRI           -10
#define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_SECA)

#define FILEMAP_DOMAIN       "seca"

//#define DEBUG_SECA
//#define DEBUG_SECA_EXTRA

#ifdef DEBUG_SECA
#define ds(x) { (x); }
#else
#define ds(x) ;
#endif

#ifdef DEBUG_SECA_EXTRA
#define dse(x) { x; }
#else
#define dse(x) ;
#endif
#define bswap_32(x) \
     ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) | \
      (((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24))
#define bswap_16(x) (((x) & 0x00ff) << 8 | ((x) & 0xff00) >> 8)
// -- cPlainKeySeca ------------------------------------------------------------

#define PLAINLEN_SECA_H 8
#define PLAINLEN_SECA_D 16
#define PLAINLEN_SECA_B 90
#define PLAINLEN_SECA_E 6  // exponent key len
#define EMM_MAGIC       1
#define N51_MAGIC       2


class cPlainKeySeca : public cDualKey {
protected:
  virtual void SetIsBNKey(void);
public:
  cPlainKeySeca(bool Super);
  virtual bool Parse(char *line);
  virtual bool Save(FILE *f);
  };

static cPlainKeyTypeReg<cPlainKeySeca,'S'> KeyReg;

cPlainKeySeca::cPlainKeySeca(bool Super)
:cDualKey(Super,true)
{}

void cPlainKeySeca::SetIsBNKey(void)
{
  keyIsBN=false;
  switch(keynr&C2MASK) {
    case MBC('E','1'):
    case MBC('M','1'):
    case MBC('E','3'):
    case MBC('M','3'):
    case MBC('E','5'):
    case MBC('M','5'):
    case MBC('E','9'):
    case MBC('M','9'): keyIsBN=true; break;
    }
}

bool cPlainKeySeca::Parse(char *line)
{
  unsigned char sid[2];
#ifdef DEBUG
  char *sline=line;
#endif
  int len;
  if(GetChar(line,&type,1) && (len=GetHex(line,sid,2,false))) {
    type=toupper(type); id=Bin2Int(sid,len);
    line=skipspace(line);
    int emmnr=0, keylen=PLAINLEN_SECA_B;
    if(!strncasecmp(line,"EMM",3)) { // EMM RSA key
      emmnr=EMM_MAGIC;
      line+=3;
      line=skipspace(line);
      }
    else if(!strncasecmp(line,"N51",3)) { // Nano 51 RSA key
      emmnr=N51_MAGIC;
      line+=3;
      line=skipspace(line);
      keylen=toupper(*line)=='E'?1:129;
      }
    bool ok;
    switch(toupper(*line)) {
      case 'E':
      case 'M': ok=GetChar(line,&keynr,2);
                keynr=ADDC3(keynr,emmnr);
                break;
      default:  ok=emmnr ? false : GetHex(line,sid,1);
                keynr=sid[0];
                break;
      }
    if(ok) {
      SetIsBNKey();
      unsigned char skey[keylen];
      len=GetHex(line,skey,keylen,false);
      if(keyIsBN) {
        if(C2(keynr)=='E' && len==PLAINLEN_SECA_E) {
          // support short exponent keys
          memset(&skey[len],0,keylen-len);
          len=keylen;
          }
        }
      else {
        if(len==PLAINLEN_SECA_H || len==PLAINLEN_SECA_D) {
          // support for 16 & 8 byte keys
          keylen=len;
          }
        }
      if(len==keylen) {
        SetKeyFromBin(skey,keylen);
        return true;
        }
      }
    }
  m_DLLInstance.Log("secakey: bad key format \n line:%.30s\n",line);
  
  return false;
}

bool cPlainKeySeca::Save(FILE *f)
{
  char nr[8];
  int k=keynr & C2MASK;
  if(keyIsBN) {
    int i=(keyIsBN ? 2:1);
    nr[i--]=0; while(i>=0) { nr[i]=k&0xff; k>>=8; i--; }
    }
  else snprintf(nr,sizeof(nr),"%.2X",keynr);

  char emm[10];
  emm[0]=0;
  switch(C3(keynr)) {
    case EMM_MAGIC: snprintf(emm,sizeof(emm),"EMM "); break;
    case N51_MAGIC: snprintf(emm,sizeof(emm),"N51 "); break;
    }
  char *str=Print();
  fprintf(f,"%c %.4X %s%s %s\n",type,id,emm,nr,str);
  free(str);
  return ferror(f)==0;
}

// -- cSecaCardInfo ------------------------------------------------------------

class cSecaCardInfo : public cListObject {
private:
  int len;
public:
  unsigned char ID[2], SA[4], KEY[16];
  //
  bool Parse(char *line);
  bool Save(FILE *f) { return true; }
  bool IsUpdated(void) { return false; }
  void Updated(void) {}
  bool Cmp(cSecaCardInfo *ci) { return false; }
  int KeySize(void) { return len; }
  };

bool cSecaCardInfo::Parse(char *line)
{
  while(*line && isspace(*line)) line++;
  if(*line!=';') {
    if(GetHex(line,ID,sizeof(ID)) &&
       GetHex(line,SA,sizeof(SA)) &&
       (len=GetHex(line,KEY,sizeof(KEY),false)) && 
       (len==PLAINLEN_SECA_H || len==PLAINLEN_SECA_D))
      return true;
    }
  return false;
}

// -- cSecaCardInfos -----------------------------------------------------------

class cSecaCardInfos : public cCardInfos<cSecaCardInfo> {
public:
  cSecaCardInfos(void):cCardInfos<cSecaCardInfo>(SYSTEM_NAME) {}
  };

static cSecaCardInfos Scards;

// -- cSecaProv ----------------------------------------------------------------

#define MAX_PROVID   10
#define MAX_PERMS    10
#define KEY2INDEX(x) (((x)>>5)&0x03)

struct Perm {
  unsigned char P1[8], P2[8], P3[8], P4[8];
  };

struct ProvData {
  unsigned short Ids[MAX_PROVID], LoadId, MTLoadSize, MTSize;
  unsigned char Add;
  void (*MTmod)(const unsigned char *in, unsigned char *out);
  unsigned char SHA1Pad, SHA1End;
  unsigned char CWmod, XT[16], PT[16];
  const struct Perm *perm;
  unsigned char FP[8];
  };

static const struct Perm pt64[MAX_PERMS] = {
    { // 1
    { 3, 2, 4, 3, 3, 4, 4, 2 },
    { 4, 4, 3, 3, 2, 2, 4, 4 },
    { 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0 }
  },{ // 2
    { 2, 3, 4, 3, 2, 4, 3, 2 },
    { 4, 3, 2, 3, 4, 2, 4, 3 },
    { 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0 },
  },{ // 3
  },{ // 4
  },{ // 5
  },{ // 6
  },{ // 7
  },{ // 8
  },{ // 9
    { 3, 3, 4, 4, 3, 3, 2, 2 },
    { 3, 3, 2, 2, 3, 3, 2, 2 },
    { 2, 3, 4, 1, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0 },
  },{ // 10
  } };

static const struct Perm pt65[MAX_PERMS] = {
    { // 1
    { 2, 4, 4, 3, 2, 2, 4, 4 },
    { 3, 2, 4, 3, 4, 4, 2, 3 },
    { 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0 }
  },{ // 2
    { 4, 4, 3, 2, 2, 3, 3, 4 },
    { 4, 2, 3, 4, 3, 2, 4, 2 },
    { 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0 }
  },{ // 3
  },{ // 4
  },{ // 5
  },{ // 6
  },{ // 7
  },{ // 8
  },{ // 9
    { 4, 4, 3, 2, 3, 4, 3, 2 },
    { 2, 4, 3, 2, 2, 4, 4, 3 },
    { 2, 3, 4, 1, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0 },
  },{ // 10
  } };

static const struct Perm pt70[MAX_PERMS] = {
    { // 1
    { 2, 3, 4, 4, 2, 2, 3, 4 },
    { 2, 3, 4, 4, 2, 2, 3, 4 },
    { 2, 3, 2, 3, 2, 2, 4, 3 },
    { 2, 2, 4, 4, 4, 3, 3, 3 }
  },{ // 2
    { 2, 3, 3, 2, 4, 2, 3, 3 },
    { 2, 3, 3, 2, 4, 2, 3, 3 },
    { 2, 3, 0, 4, 2, 2, 2, 0 },
    { 3, 4, 0, 2, 4, 4, 3, 0 }
  },{ // 3
  },{ // 4
  },{ // 5
  },{ // 6
  },{ // 7
  },{ // 8
  },{ // 9
    { 3, 4, 3, 4, 2, 2, 4, 3 },
    { 4, 3, 2, 4, 3, 3, 4, 2 },
    { 3, 4, 2, 1, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0 },
  },{ // 10
  } };

static void mt64(const unsigned char *in, unsigned char *out)
{
  memcpy(out+16384,in,511);
}

static const struct ProvData provData[] = { {
  { 0x64,0x66,0x67 },0x64,16384,16384+511,0x01,mt64,0x96,0x69,0x00,
    {},
    {},
    pt64,
    { 2, 2, 2, 2, 2, 2, 2, 2 } 
  },{
  { 0x65 },0x65,16895,16895,0x01,0,0x96,0x69,0x01,
    { 0xA0,0x12,0x23,0x35,0x46,0xB0,0xDF,0xCA,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
    { 0x05,0x04,0x06,0x07,0x03,0x00,0x01,0x02,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F },
    pt65,
    { 3, 0, 0, 0, 2, 4, 3, 0 }
  },{
  { 0x70,0x71,0x72,0x73 },0x70,16895,16895,0x02,0,0xA1,0x1A,0x01,
    { 0x07,0x17,0x3F,0x5E,0x6A,0xF7,0xB2,0x25,0xA6,0x97,0x6A,0xCA,0xE2,0x2F,0xC8,0x1F },
    { 0x01,0x00,0x02,0x04,0x03,0x07,0x05,0x06,0x0E,0x0B,0x0D,0x0A,0x0F,0x0C,0x09,0x08 },
    pt70,    
    { 0, 0, 3, 2, 0, 3, 0, 4 }
  } };

class cSecaProv {
private:
  cFileMap *hash, *mask;
  const struct ProvData *data;
  unsigned char *mt;
  bool mtFree;
  //
  bool Init(void);
  cFileMap *cSecaProv::GetMap(const char *type, int size);
  //
  static bool cSecaProv::MatchProv(const struct ProvData *d, unsigned short id);
public:
  cSecaProv(const struct ProvData *Data);
  ~cSecaProv();
  bool Matches(unsigned short id);
  //
  static cSecaProv *GetProv(unsigned short id);
  //
  const unsigned char *T1(int index);
  const unsigned char *T2(int index);
  const struct Perm *P(int index);
  inline const unsigned char *MT(void) { return mt; }
  inline int MTSize(void) { return data->MTSize; }
  inline unsigned char Add(void) { return data->Add; }
  inline unsigned char Sha1Pad(void) { return data->SHA1Pad; }
  inline unsigned char Sha1End(void) { return data->SHA1End; }
  inline const unsigned char *XT(void) { return data->CWmod ? data->XT : 0; }
  inline const unsigned char *PT(void) { return data->CWmod ? data->PT : 0; }
  inline const unsigned char *FP(void) { return data->FP; }
  };

cSecaProv::cSecaProv(const struct ProvData *Data)
{
  data=Data;
  hash=0; mask=0; mt=0; mtFree=false;
}

cSecaProv::~cSecaProv()
{
  if(hash) hash->Unmap();
  if(mask) mask->Unmap();
  if(mtFree) free(mt);
}

bool cSecaProv::Init(void)
{
  hash=GetMap("hash",1536);
  if(!hash) return false;
  mask=GetMap("mt",data->MTLoadSize);
  if(!mask) return false;
  if(data->MTmod || data->MTSize!=data->MTLoadSize) {
    mt=MALLOC(unsigned char,data->MTSize);
    mtFree=true;
    if(!mt) return false;
    memcpy(mt,mask->Addr(),data->MTLoadSize);
    data->MTmod(mask->Addr(),mt);
    }
  else {
    mt=mask->Addr();
    mtFree=false;
    }
  return true;
}

cFileMap *cSecaProv::GetMap(const char *type, int size)
{
  char name[32];
  snprintf(name,sizeof(name),"s2_%s_%04x.bin",type,data->LoadId);
  cFileMap *map=filemaps.GetFileMap(name,FILEMAP_DOMAIN,false);
  if(!map){
    m_DLLInstance.Log("[Seca]: no filemap for %s\n",name);
    

    }
  else if(!map->Map()) {
    m_DLLInstance.Log("[Seca]: mapping failed for %s\n",name);
    

    map=0;
    }
  else if(map->Size()<size) {
    m_DLLInstance.Log("[Seca]: %s (h=%d w=%d)\n",name,map->Size(),size);
    


    map->Unmap();
    map=0;
    }
  return map;
}

bool cSecaProv::Matches(unsigned short id)
{
  return MatchProv(data,id);
}

cSecaProv *cSecaProv::GetProv(unsigned short id)
{
  for(unsigned int i=0; i<sizeof(provData)/sizeof(struct ProvData); i++)
    if(MatchProv(&provData[i],id)) {
      cSecaProv *sp=new cSecaProv(&provData[i]);
      if(sp && sp->Init()) return sp;
      delete sp;
      }
  return 0;
}

bool cSecaProv::MatchProv(const struct ProvData *d, unsigned short id)
{
  for(int i=0; i<MAX_PROVID; i++)
    if(d->Ids[i]==id) return true;
  return false;
}

// Hash file layout is (Yankse style):
//  Table T1 for 9x, 
//  Table T1 for Bx,
//  Table T1 for FX,
//  Table T2 for 9x, 
//  Table T2 for Bx,
//  Table T2 for FX  (total: 1536 bytes)

const unsigned char *cSecaProv::T1(int index)
{
  static int idxTrans[] = { 0,1,2,2 };
  if(index>=0 && index<=3) {
    return hash->Addr()+(idxTrans[index]*256);
  }
  m_DLLInstance.Log("[Seca]: bad T1 table index %d\n",index);
  
  //ds(printf("[Seca]: bad T1 table index %d\n",index))
  return 0;
}

const unsigned char *cSecaProv::T2(int index)
{
  static int idxTrans[] = { 0,1,2,2 };
  if(index>=0 && index<=3) {
    return hash->Addr()+(idxTrans[index]*256)+768;
    }
  m_DLLInstance.Log("[Seca]: bad T2 table index %d\n",index);
  

  return 0;
}

const struct Perm *cSecaProv::P(int index)
{
  if(index>=0 && index<MAX_PERMS && data->perm) {
    return &data->perm[index];
    }
  m_DLLInstance.Log("[Seca]: no perm table or bad index %d\n",index);
  

  return 0;
}

// -- cSeca --------------------------------------------------------------------

static inline void __swap8_4(unsigned char *data)
{
  unsigned char temp[4];
  memcpy(temp,data,4);
  memcpy(data,&data[4],4);
  memcpy(&data[4],temp,4);
}
#define swap8_4(d) __swap8_4(d)

class cSeca {
private:
  static const unsigned char TD[];
  //
  void Fase(unsigned char *data, const unsigned char *key, const unsigned char *T1, const unsigned char *T2);
  unsigned short Sum(const unsigned char *data, int n);
  void ChainTableXor(unsigned char *data, const unsigned char *MT, unsigned char add, unsigned short index);
protected:
  static const unsigned char T1_S1[], T2_S1[];
  //
  void Decrypt(unsigned char *data, const unsigned char *key, const unsigned char *T1, const unsigned char *T2);
  void Encrypt(unsigned char *data, const unsigned char *key, const unsigned char *T1, const unsigned char *T2);
  void CalcSignature(const unsigned char *buff, int len, unsigned char *signature, const unsigned char *k, const unsigned char *T1, const unsigned char *T2);
  // Seca2 functions
  bool DecryptRSA(unsigned char *data, int n, BIGNUM *exp, BIGNUM *mod);
  bool DecryptSE(unsigned char *data, const unsigned char *key, int n, int start, const unsigned char *T1, const unsigned char *T2, const unsigned char *MT, unsigned char add);
  void CalcSHASignature(const unsigned char *data, int n, unsigned char *sig, unsigned char End, unsigned char Pad, bool PadMode=true);
  void CwProcess(unsigned char *data, const unsigned char *XT, const unsigned char *PT);
  void AdditionalAlgo(unsigned char *data, const unsigned char *key, const int mode);
  };

const unsigned char cSeca::TD[4] = { 1,3,0,2 };

const unsigned char cSeca::T1_S1[256] = { // Original Tables
  0x2a,0xe1,0x0b,0x13,0x3e,0x6e,0x32,0x48, 0xd3,0x31,0x08,0x8c,0x8f,0x95,0xbd,0xd0,
  0xe4,0x6d,0x50,0x81,0x20,0x30,0xbb,0x75, 0xf5,0xd4,0x7c,0x87,0x2c,0x4e,0xe8,0xf4,
  0xbe,0x24,0x9e,0x4d,0x80,0x37,0xd2,0x5f, 0xdb,0x04,0x7a,0x3f,0x14,0x72,0x67,0x2d,
  0xcd,0x15,0xa6,0x4c,0x2e,0x3b,0x0c,0x41, 0x62,0xfa,0xee,0x83,0x1e,0xa2,0x01,0x0e,//8
  0x7f,0x59,0xc9,0xb9,0xc4,0x9d,0x9b,0x1b, 0x9c,0xca,0xaf,0x3c,0x73,0x1a,0x65,0xb1,
  0x76,0x84,0x39,0x98,0xe9,0x53,0x94,0xba, 0x1d,0x29,0xcf,0xb4,0x0d,0x05,0x7d,0xd1,
  0xd7,0x0a,0xa0,0x5c,0x91,0x71,0x92,0x88, 0xab,0x93,0x11,0x8a,0xd6,0x5a,0x77,0xb5,
  0xc3,0x19,0xc1,0xc7,0x8e,0xf9,0xec,0x35, 0x4b,0xcc,0xd9,0x4a,0x18,0x23,0x9f,0x52,//16
  0xdd,0xe3,0xad,0x7b,0x47,0x97,0x60,0x10, 0x43,0xef,0x07,0xa5,0x49,0xc6,0xb3,0x55,
  0x28,0x51,0x5d,0x64,0x66,0xfc,0x44,0x42, 0xbc,0x26,0x09,0x74,0x6f,0xf7,0x6b,0x4f,
  0x2f,0xf0,0xea,0xb8,0xae,0xf3,0x63,0x6a, 0x56,0xb2,0x02,0xd8,0x34,0xa4,0x00,0xe6,
  0x58,0xeb,0xa3,0x82,0x85,0x45,0xe0,0x89, 0x7e,0xfd,0xf2,0x3a,0x36,0x57,0xff,0x06,//24
  0x69,0x54,0x79,0x9a,0xb6,0x6c,0xdc,0x8b, 0xa7,0x1f,0x90,0x03,0x17,0x1c,0xed,0xd5,
  0xaa,0x5e,0xfe,0xda,0x78,0xb0,0xbf,0x12, 0xa8,0x22,0x21,0x3d,0xc2,0xc0,0xb7,0xa9,
  0xe7,0x33,0xfb,0xf1,0x70,0xe5,0x17,0x96, 0xf8,0x8d,0x46,0xa1,0x86,0xe2,0x40,0x38,
  0xf6,0x68,0x25,0x16,0xac,0x61,0x27,0xcb, 0x5b,0xc8,0x2b,0x0f,0x99,0xde,0xce,0xc5
  };

const unsigned char cSeca::T2_S1[256] = {
  0xbf,0x11,0x6d,0xfa,0x26,0x7f,0xf3,0xc8, 0x9e,0xdd,0x3f,0x16,0x97,0xbd,0x08,0x80,
  0x51,0x42,0x93,0x49,0x5b,0x64,0x9b,0x25, 0xf5,0x0f,0x24,0x34,0x44,0xb8,0xee,0x2e,
  0xda,0x8f,0x31,0xcc,0xc0,0x5e,0x8a,0x61, 0xa1,0x63,0xc7,0xb2,0x58,0x09,0x4d,0x46,
  0x81,0x82,0x68,0x4b,0xf6,0xbc,0x9d,0x03, 0xac,0x91,0xe8,0x3d,0x94,0x37,0xa0,0xbb, //8
  0xce,0xeb,0x98,0xd8,0x38,0x56,0xe9,0x6b, 0x28,0xfd,0x84,0xc6,0xcd,0x5f,0x6e,0xb6,
  0x32,0xf7,0x0e,0xf1,0xf8,0x54,0xc1,0x53, 0xf0,0xa7,0x95,0x7b,0x19,0x21,0x23,0x7d,
  0xe1,0xa9,0x75,0x3e,0xd6,0xed,0x8e,0x6f, 0xdb,0xb7,0x07,0x41,0x05,0x77,0xb4,0x2d,
  0x45,0xdf,0x29,0x22,0x43,0x89,0x83,0xfc, 0xd5,0xa4,0x88,0xd1,0xf4,0x55,0x4f,0x78,//16
  0x62,0x1e,0x1d,0xb9,0xe0,0x2f,0x01,0x13, 0x15,0xe6,0x17,0x6a,0x8d,0x0c,0x96,0x7e,
  0x86,0x27,0xa6,0x0d,0xb5,0x73,0x71,0xaa, 0x36,0xd0,0x06,0x66,0xdc,0xb1,0x2a,0x5a,
  0x72,0xbe,0x3a,0xc5,0x40,0x65,0x1b,0x02, 0x10,0x9f,0x3b,0xf9,0x2b,0x18,0x5c,0xd7,
  0x12,0x47,0xef,0x1a,0x87,0xd2,0xc2,0x8b, 0x99,0x9c,0xd3,0x57,0xe4,0x76,0x67,0xca,//24
  0x3c,0xfb,0x90,0x20,0x14,0x48,0xc9,0x60, 0xb0,0x70,0x4e,0xa2,0xad,0x35,0xea,0xc4,
  0x74,0xcb,0x39,0xde,0xe7,0xd4,0xa3,0xa5, 0x04,0x92,0x8c,0xd9,0x7c,0x1c,0x7a,0xa8,
  0x52,0x79,0xf2,0x33,0xba,0x1f,0x30,0x9a, 0x00,0x50,0x4c,0xff,0xe5,0xcf,0x59,0xc3,
  0xe3,0x0a,0x85,0xb3,0xae,0xec,0x0b,0xfe, 0xe2,0xab,0x4a,0xaf,0x69,0x6c,0x2c,0x5d
  };

void cSeca::Fase(unsigned char *D, const unsigned char *key, const unsigned char *T1, const unsigned char *T2)
{
  for(int l=0; l<4; ++l) { D[l]^=key[l]; D[l]=T1[D[l]&0xff]; }
  for(int l=6; l>3; --l) {
    D[(l+2)&3]^=D[(l+1)&3];
    D[l&3]=T2[(sn8(D[(l+1)&3])+D[l&3])&0xff];
    } 
  for(int l=3; l>0; --l) {
    D[(l+2)&3]^=D[(l+1)&3];
    D[l&3]=T1[(sn8(D[(l+1)&3])+D[l&3])&0xff];
    }
  D[2]^=D[1];
  D[1]^=D[0];
}

void cSeca::Decrypt(unsigned char *d, const unsigned char *key, const unsigned char *T1, const unsigned char *T2)
{
  unsigned char k[16],D[4];
  memcpy(k,key,sizeof(k));
  unsigned char C=0xff;
  for(int j=0; j<4; ++j) {
    for(int i=0; i<16; ++i) {
      if((i&3)==0) ++C;
      k[i]^=T1[(k[(15+i)&0xf]^k[(i+1)&0xf]^C)&0xff]; 
      } 
    }
  int j=0;
  for(int i=0; i<16; ++i) {
    for(int l=0; l<4; ++l) D[l]=d[l+4];
    j=(j+12)&0xf;
    Fase(D,&k[j&0xc],T1,T2);
    for(int l=0; l<4; ++l) d[l]^=T2[D[TD[l]]&0xff];
    for(int l=3; l>=0; --l) k[(j+l)&0xf]^=T1[(k[(j+l+1)&0xf]^k[(j+l+15)&0xf]^(15-i))&0xff];
    if(i<15) swap8_4(d);
    }
}

void cSeca::Encrypt(unsigned char *d, const unsigned char *key, const unsigned char *T1, const unsigned char *T2)
{
  unsigned char D[4],kk[16];
  memcpy(kk, key, sizeof(kk));
  for(int j=0, i=0; i<16; ++i, j=(j+4)&0xf) {
    for(int l=0; l<4; ++l) kk[(j+l)&0xf] ^= T1[(kk[(j+l+1)&0xf]^kk[(j+l+15)&0xf]^i)&0xff]; 
    if(i>0) swap8_4(d);
    for(int l=0; l<4; ++l) D[l]=d[l+4];
    Fase(D,&kk[j&0xc],T1,T2);
    for(int l=0; l<4; ++l) d[l]^=T2[D[TD[l]]&0xff];
    } 
}

void cSeca::CalcSignature(const unsigned char *buff, int len, unsigned char *signature, const unsigned char *k, const unsigned char *T1, const unsigned char *T2)
{
  memset(signature,0,8);
  for(int i=0 ; i<len ; i+=8) {
    for(int j=0 ; j<8 && i+j<len; j++) signature[j]^=buff[i+j];
    Encrypt(signature,k,T1,T2);
    }
}

bool cSeca::DecryptRSA(unsigned char *data, int n, BIGNUM *exp, BIGNUM *mod)
{
  cBNctx temp;
  cBN r, rsaData;

  RotateBytes(data,n);
  BN_bin2bn(data,n,rsaData);
  BN_mod_exp(r,rsaData,exp,mod,temp);

  int s=BN_num_bytes(r);
  if(s>n) {
    m_DLLInstance.Log("seca: RSA len %d > %d, truncating\n",s,n);
    
    dse(printf("seca: RSA len %d > %d, truncating\n",s,n))
    unsigned char buff[s];
    BN_bn2bin(r,buff);
    memcpy(data,buff+s-n,n);
    }
  else if(s==n) {
    BN_bn2bin(r,data);
    }
  else {
    m_DLLInstance.Log("seca: RSA len %d < %d, failed\n",s,n);
    
    dse(printf("seca: RSA len %d < %d, failed\n",s,n))
    return false;
    }
  RotateBytes(data,n);
  return true;
}

bool cSeca::DecryptSE(unsigned char *data, const unsigned char *key, int n, int start, const unsigned char *T1, const unsigned char *T2, const unsigned char *MT, unsigned char add)
{
  const int sigStart=n-data[n-1]-9; // 82 <8 bytes> P5(sigStart)
  const int encrLen=sigStart-16;

#ifdef DEBUG_SECA_EXTRA
  if(encrLen>=n || encrLen<0) printf("encrLen error in SE\n");
  if(sigStart<16) printf("sigStart error in SE\n");
#endif
  if(encrLen>=n || encrLen<0 || sigStart<16){ 
    m_DLLInstance.Log("encrLen error in SE");
                  
  return false;}

  Decrypt(data+sigStart-8,key,T1,T2);
  ChainTableXor(data+sigStart-8,MT,add,Sum(data+start,sigStart-8-start));
  int i;
  for(i=start; i<encrLen; i+=8) {
    Decrypt(data+i,key,T1,T2);
    ChainTableXor(data+i,MT,add,Sum(&data[i+8],8));
    }

  int restBytes=sigStart&0x07;
  if(!restBytes) restBytes=8;

  // Last Block
  unsigned char lastBlock[8];
  memset(lastBlock,0,sizeof(lastBlock));
  memcpy(lastBlock,data+sigStart-restBytes,restBytes);
  Decrypt(lastBlock,key,T1,T2);

  Decrypt(data+i,key,T1,T2);
  ChainTableXor(data+i,MT,add,Sum(lastBlock,sizeof(lastBlock)));
#ifdef DEBUG_SECA_EXTRA
  printf("Result after SE decrypt:\n");
  HexDump(data,n);
#endif
  return true;
}

void cSeca::CalcSHASignature(const unsigned char *data, int n, unsigned char *signature, unsigned char End, unsigned char Pad, bool PadMode)
{
  SHA_CTX ctx;
  SHA1_Init(&ctx);
  SHA1_Update(&ctx,data,n);
  if(PadMode) {
    SHA1_Update(&ctx,&End,1);
    int l=(n&63)+1;
    if(l>62) {
      for(; l<64; l++) SHA1_Update(&ctx,&Pad,1);
      l=0;
      }
    for(; l<62; l++) SHA1_Update(&ctx,&Pad,1);
    unsigned short s=bswap_16(n);
    SHA1_Update(&ctx,&s,2);
    *((unsigned int *)(signature  ))=bswap_32(ctx.h0);
    *((unsigned int *)(signature+4))=bswap_32(ctx.h1);
    }
  else {
    for(; n&63; n++) SHA1_Update(&ctx,&Pad,1);
    *((unsigned int *)(signature   ))=bswap_32(ctx.h0);
    *((unsigned int *)(signature+ 4))=bswap_32(ctx.h1);
    *((unsigned int *)(signature+ 8))=bswap_32(ctx.h2);
    *((unsigned int *)(signature+12))=bswap_32(ctx.h3);
    *((unsigned int *)(signature+16))=bswap_32(ctx.h4);
    }
}

unsigned short cSeca::Sum(const unsigned char *data, int n)
{
  unsigned int sum=0;
  for(int i=1; i<n; i+=2) sum+=((data[i-1]<<8)+data[i]);
  if(n&1) sum+=(data[n-1]<<4);
  return sum&0x3FFF;
}

void cSeca::ChainTableXor(unsigned char *data, const unsigned char *MT, unsigned char add, unsigned short index)
{
  for(int i=0; i<8; ++i) {
    switch(add) {
      case 1:
        data[i]^=MT[index];
        break;
      case 2:
        data[i]^=MT[index]^MT[index+1];
        break;
      }
    index+=add;
    }
}

void cSeca::CwProcess(unsigned char *data, const unsigned char *XT, const unsigned char *PT)
{
  xxor(data,16,data,XT);
  unsigned char temp[16];
  for(unsigned int i=0; i<sizeof(temp); i++) temp[i]=data[PT[i]];
  memcpy(data,temp,sizeof(temp));
}

void cSeca::AdditionalAlgo(unsigned char *data, const unsigned char *key, const int mode)
{
/*
  unsigned short counter = 0;
  for(int i=0; i<0x10; i++) {
    counter += i;
    const unsigned int adder = 0xE555 * counter;
    printf("0x%04x,",adder&0xffff);
    }
  printf("\n");
*/
  static const unsigned int adders[] = {
    0x0000,0xe555,0xafff,0x5ffe,0xf552,0x6ffb,0xcff9,0x154c,
    0x3ff4,0x4ff1,0x4543,0x1fea,0xdfe6,0x8537,0x0fdd,0x7fd8
    };
  const unsigned short * const k = (const unsigned short *)key;
  unsigned short * const dd = (unsigned short *)data;
  unsigned int d1=dd[0], d2=dd[1], d3=dd[2], d4=dd[3];
  dse(printf("additional algo: %s\n",!mode?"encrypt":"decrypt"))
  if(!mode) {
    for(int i=0; i<0x10; i++) {
      const unsigned int adder = adders[i];
      d1 += (k[0] + d3 + adder) ^ (k[1] + d4 + adder);
      d2 += (k[2] + d3 + adder) ^ (k[3] + d4 + adder);
      d1 = ror16(d1,5);
      d2 = rol16(d2,3);
      d3 += (k[0] + d1 + adder) ^ (k[1] + d2 + adder);
      d4 += (k[2] + d1 + adder) ^ (k[3] + d2 + adder);
      d3 = rol16(d3,3);
      d4 = ror16(d4,5);
      }
    }
  else {
    for(int i=0xf; i>=0; i--) {
      const unsigned int adder = adders[i];
      d4 = rol16(d4,5);
      d3 = ror16(d3,3);
      d4 -= (k[2] + d1 + adder) ^ (k[3] + d2 + adder);
      d3 -= (k[0] + d1 + adder) ^ (k[1] + d2 + adder);
      d2 = ror16(d2,3);
      d1 = rol16(d1,5);
      d2 -= (k[2] + d3 + adder) ^ (k[3] + d4 + adder);
      d1 -= (k[0] + d3 + adder) ^ (k[1] + d4 + adder);
      }
    }
  dd[0]=d1; dd[1]=d2; dd[2]=d3; dd[3]=d4;
}

// -- cSystemSeca ---------------------------------------------------------------

struct SecaEcm {
  unsigned char tid;
  unsigned char cmdLen1;
  unsigned char cmdLen2;
  unsigned char id[2];
  unsigned char unknown;
  unsigned char seca;
  unsigned char keyNr;
  };

struct SecaEmm {
  unsigned char sep0, sep1;
  unsigned char cmdLen;
  unsigned char id[2];
  unsigned char sa[3];
  unsigned char seca;
  unsigned char keyNr;
  };

static const unsigned char secaPC1[] = {
  42,57,29,34,  41,53,30,15,
  19,36,23,14,  43,61,12, 3,
  51,49,5,  6,  45,54,52,47,
  63,38,58,22,  60,33,10,26,
  37,35,44, 1,  20,62,28,18,
  46, 9,39, 4,  27,11,21,50,
  31,25, 2, 7,  13,55,59,17
  };

static const unsigned char secaPC2[] = {
  18, 3,21,15,  42,35,37, 8,
  49,41,30,55,  56,29,12,23,
  43,14,7 ,27,  13, 2,11,45,
   4,34,54,51,  22,40,16,25,
  26,48,53,28,   1,17, 5,31,
  50, 6,39,24,  33,47,38,32
  };

class cSystemSeca : public cSystem, private cSeca, private cDes {
private:
  cSecaProv *sp, *spL;
  //
  bool Process0FNano(int count, unsigned char *hashDW, unsigned char *PK, const unsigned char *T1, const unsigned char *T2);
  bool Process51Nano(unsigned char *CW, const unsigned char *PermData, const unsigned char *hashDW, const unsigned char *T1, const unsigned char *T2, int provId);
  void Crypto51Nano(unsigned char *data, const unsigned char *key, const unsigned char crypto, const unsigned char mode);
  void Permute(unsigned char *data, const unsigned char *pdata, const unsigned char *P);
protected:
  virtual void ParseCAT(const cEcmInfo *ecm, const unsigned char *buffer, int n);
public:
  cSystemSeca(void);
  virtual ~cSystemSeca();
  virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *source);
  virtual void ProcessEMM(int pid, const cEcmInfo *ecm, unsigned char *buffer, int n);
  };

cSystemSeca::cSystemSeca(void)
:cSystem(SYSTEM_NAME,SYSTEM_PRI)
,cDes(secaPC1,secaPC2)
{
  sp=spL=0;
  hasLogger=true;
}

cSystemSeca::~cSystemSeca()
{
  delete sp;
  delete spL;
}

bool cSystemSeca::ProcessECM(const cEcmInfo *ecmD, unsigned char *source)
{
  const struct SecaEcm * const s=(struct SecaEcm *)source;
  const int msgLen=s->cmdLen2-(sizeof(struct SecaEcm)-3);
  unsigned char * const ecm=&source[sizeof(struct SecaEcm)];
 // dse(printf("[Seca] ECM:"); HexDump(source,SCT_LEN(source)))
  if(ecm[0]==0x10) {
    if(msgLen<0x5c) {
    //  if(doLog) ds(printf("[Seca] ECM length too small (%d)\n",msgLen))
      m_DLLInstance.Log("[Seca] ECM length too small (%d)\n",msgLen);
      
      return false;
      }
    const int rsaKeynr=(ecm[1]&0x0F)+'0';
    cBN exp, mod;
    cPlainKey *rsaKey;
    if(!(rsaKey=keys.FindKey('S',ecmD->provId,MBC('E',rsaKeynr)))) {
      m_DLLInstance.Log("[Seca] missing %04x E%c key\n",ecmD->provId,rsaKeynr);
                                                                   
     // if(doLog) d(printf("[Seca] missing %04x E%c key\n",ecmD->provId,rsaKeynr))
      return false;
      }
    rsaKey->Get(exp);
    if(!(rsaKey=keys.FindKey('S',ecmD->provId,MBC('M',rsaKeynr)))) {
      m_DLLInstance.Log("[Seca] missing %04x M%c key\n",ecmD->provId,rsaKeynr);
      
    //  if(doLog) d(printf("[Seca] missing %04x M%c key\n",ecmD->provId,rsaKeynr))
      return false;
      }
    rsaKey->Get(mod);
  //  dse(printf("[Seca] ECM before RSA decrypt :"); HexDump(ecm+(msgLen-0x5a),0x5a))
    if(!DecryptRSA(ecm+(msgLen-0x5a),0x5a,exp,mod)) {
       m_DLLInstance.Log("[Seca] RSA decrypt failed\n");
        
    //  ds(printf("[Seca] RSA decrypt failed\n"))
      return false;
      }
  //  dse(printf("[Seca] ECM after RSA decrypt :"); HexDump(ecm+(msgLen-0x5a),0x5a))
    }

  bool SE=false;
  if(s->keyNr&0x80) {
  /*  m_DLLInstance.Log("SuperEncryption enabled\n");
    dse(printf("SuperEncryption enabled\n"))*/
    SE=true;
    }

  const unsigned char *T1=T1_S1, *T2=T2_S1;
  const unsigned char *XT=0, *PT=0;
  int decrLen=msgLen;
  if(SE) {
    const int id=(s->id[0]<<8)+s->id[1];
    if(!sp || !sp->Matches(id)) {
      delete sp;
      sp=cSecaProv::GetProv(id);
      }
    if(!sp) {
       m_DLLInstance.Log("[Seca] Seca2 prv %04x not supported.\n",id);
       
    //  if(doLog) d(printf("[Seca] Seca2 provider %04x not supported\n",id))
      return false;
      }
    if(!(T1=sp->T1(KEY2INDEX(s->keyNr))) ||
       !(T2=sp->T2(KEY2INDEX(s->keyNr)))) return false;
    // Set the Xor and the Permutation table for later use.
    XT=sp->XT(); PT=sp->PT();
    decrLen-=ecm[msgLen-1]; if(decrLen<8) return false;
    }

  cPlainKey *pk;
  cKeySnoop ks(this,'S',ecmD->provId,s->keyNr&0x0F);

  while((pk=FastKey('S',ecmD->provId,s->keyNr&0x0F))) {
    unsigned char buff[msgLen];
    memcpy(buff,ecm,sizeof(buff)); // if decoding fails we need the original data
    
    unsigned char PK[16], signature[8];
    pk->Get(PK);
    if(!(s->seca&0x10))
      memcpy(&PK[8],&PK[0],8); // duplicate key
    else if(pk->Size()!=16) {
       m_DLLInstance.Log("[Seca]16 byte key requested.%d byte avaible.\n",pk->Size());
       
      //ds(printf("[Seca] 16 byte key requested, %d byte key available\n",pk->Size()))
      continue;
      }
    if(SE) {
      CalcSHASignature(buff,decrLen-8,signature,sp->Sha1End(),sp->Sha1Pad());
      Encrypt(signature,PK,T1,T2);
      if(memcmp(signature,&buff[decrLen-8],8)) {
      m_DLLInstance.Log("[Seca] sigcheck failed before SE decrypt\n");
        
       // dse(printf("[Seca] sigcheck failed before SE decrypt\n"))
        continue;
        }
      if(!DecryptSE(buff+2,PK,msgLen-2,8,T1,T2,sp->MT(),sp->Add())) {
      m_DLLInstance.Log("[Seca] SE decrypt failed\n");
       
    //    dse(printf("[Seca] SE decrypt failed\n"))
        continue;
        }
      }
    else {
      CalcSignature(buff,decrLen-8,signature,PK,T1,T2);
      }
    unsigned char hashDW[28]; // actually 16 bytes used for processing, but SHA needs more
    unsigned char *cCW=0;
    const unsigned char *nano51Data=0;
    int nr0F=0;

    for(int i=0 ; i<decrLen; ) {
      int param=buff[i++];
      int extra=(param >> 4) & 0x0f;
      switch(extra) {
        case 0x0d: extra=0x10; break;
        case 0x0e: extra=0x18; break;
        case 0x0f: extra=0x20; break;
        }
      switch(param) {
        case 0x0f:
          if(SE) nr0F++;
          break;
        case 0x51:
          if(SE) nano51Data=&buff[i];
          break;
        case 0xd1:
          cCW=&buff[i];
          if(SE) {
            CalcSHASignature(buff,i,hashDW,0,0,false);
            memcpy(&hashDW[20],hashDW,8);
            Decrypt(&hashDW[20],PK,sp->T1(0),sp->T2(0));
            CalcSHASignature(hashDW,28,hashDW,0,0,false);
            }
          break;
        case 0x82:
          if(memcmp(&buff[i],signature,8)) { cCW=0; i=decrLen; }
          break;
        }
      i+=extra;
      }
    if(cCW) {
      if(!nr0F || Process0FNano(nr0F,hashDW,PK,T1,T2)) {
        if(XT) CwProcess(cCW,XT,PT);
        if(!nano51Data || Process51Nano(cCW,nano51Data,hashDW,T1,T2,ecmD->provId)) {
          Decrypt(&cCW[0],PK,T1,T2);
          Decrypt(&cCW[8],PK,T1,T2);
          memcpy(cw,cCW,16);
        //  dse(printf("Decrypted CW:"); HexDump(cCW,16))
          ks.OK(pk);
          return true;
          }
        }
      }
    }
  return false;
}

bool cSystemSeca::Process0FNano(int count, unsigned char *hashDW, unsigned char *PK, const unsigned char *T1, const unsigned char *T2)
{
  const unsigned char *MT=sp->MT();
  unsigned char buffB[8];
  
//  dse(printf("0f: initial hashDW:"); HexDump(hashDW,16))
//  dse(printf("0f: PK:"); HexDump(PK,16))
  while(count--) {
    unsigned char buffA[8];
    swap8_4(hashDW);
    memcpy(buffA,hashDW,8);
    Decrypt(hashDW,PK,sp->T1(0),sp->T2(0));
//    dse(printf("0f: hashDW:"); HexDump(hashDW,16))

    unsigned char buffE[8];
    int off=((hashDW[0]<<8)+hashDW[1])&0x3FFF;
    xxor(buffE,8,&MT[off],&MT[0x3FFF-off]);
    xxor(buffE,8,&MT[( ((buffE[0] ^ hashDW[6])<<8)
                     +  buffE[7] ^ hashDW[7]     )&0x3FFF],buffE);
//    dse(printf("0f: buffE:"); HexDump(buffE,8))

    unsigned char buffC[16];
    off = ((hashDW[2]<<8)+hashDW[3])&0x3FFF;
    xxor(buffC,16,&MT[off],&MT[0x3FFF-off]);
    xxor(buffC,16,&MT[( ((hashDW[6] ^ buffC[0])<<8)
                      + (hashDW[7] ^ buffC[15])   )&0x3FFF],buffC);
//    dse(printf("0f: buffC:"); HexDump(buffC,16))

    off=((hashDW[4]<<8)+hashDW[5])&0x3FFF;
    off=((MT[off]<<8)+MT[off+1])&0x3FFF;
    for(int i=0; i<15; ++i) off=((MT[off]<<8)+MT[off+1])&0x3FFF;
    if(off+512 >= sp->MTSize()) {
      m_DLLInstance.Log("error: masking table overflow\n");
       
    //  printf("BUG!!!: error: masking table overflow\n");
      return false;
      }
//    dse(printf("0f: off=%04x\n",off))

    memcpy(buffB,buffE,8);
    Decrypt(buffB,buffC,&MT[off],&MT[off+256]);
//    dse(printf("0f: buffB after 1st decr:"); HexDump(buffB,8))
    xxor(buffB,8,buffB,buffE);
//    dse(printf("0f: buffB after xor:"); HexDump(buffB,8))
    Decrypt(buffB,PK,T1,T2);
//    dse(printf("0f: buffB after 2nd decr:"); HexDump(buffB,8))
    xxor(hashDW,8,buffA,buffB);
    }

  for(int i=0; i<8; ++i) {
    PK[i] = buffB[i];
    PK[i+8] = ~sn8(buffB[i]);
    }
//  dse(printf("0f: hashDW:"); HexDump(hashDW,16))
//  dse(printf("0f: new PK:"); HexDump(PK,16))
  return true;
}

void cSystemSeca::Crypto51Nano(unsigned char *data, const unsigned char *key, const unsigned char crypto, const unsigned char mode)
{
  dse(printf("51: ")); 
  switch(crypto) {
    case 0: // Xor
//      dse(printf("XOR crypto selected\n"))
      xxor(data+0,8,key,data+0); xxor(data+8,8,key,data+8);
      break;
    case 1: // Seca crypto, with 9x table, always!
      {
      unsigned char PK[16];
      const unsigned char *T1 = sp->T1(0);
      const unsigned char *T2 = sp->T2(0);
      memcpy(PK+0,key,8); memcpy(PK+8,key,8);
//      dse(printf("SECA crypto selected: %s\n",mode?"decrypt":"encrypt"))
      if(mode) { Decrypt(data,PK,T1,T2); Decrypt(data+8,PK,T1,T2); }
      else     { Encrypt(data,PK,T1,T2); Encrypt(data+8,PK,T1,T2); }
      break;
      }
    case 2: // DES crypto (Modified PC1,PC2)
//      dse(printf("DES crypto selected: %s\n",mode?"decrypt":"encrypt"))
      if(mode) { Des(data,key,SECA_DES_DECR); Des(data+8,key,SECA_DES_DECR); }
      else     { Des(data,key,SECA_DES_ENCR); Des(data+8,key,SECA_DES_ENCR); }
      break;
    case 3: // Additional Algo
//      dse(printf("Additional crypto selected: %s\n",mode?"decrypt":"encrypt"))
      AdditionalAlgo(data,key,mode); AdditionalAlgo(data+8,key,mode);
      break;
    }
}

bool cSystemSeca::Process51Nano(unsigned char *CW, const unsigned char *PermData, const unsigned char *hashDW, const unsigned char *T1, const unsigned char *T2, int provId)
{
  const int pMode=(PermData[0]&0x3F)-1;
  const struct Perm *PT=sp->P(pMode);
  if(!PT) {
    m_DLLInstance.Log("51: failed to get permutation table\n");
    
//   // dse(printf("51: failed to get permutation table\n"))
    return false;
    }
  unsigned char buffF[8], buffG[8];
  switch(pMode) {
    case 0x00:
    case 0x01:
      // Permutation 1
      Permute(buffF,PermData,PT->P1);
      dse(printf("51: buffF:"); HexDump(buffF,8))
      xxor(buffG,8,buffF,hashDW);
      for(int i=(PermData[1]&0x0f); i>0; i--) AdditionalAlgo(buffG,hashDW+8,pMode);
      // Permutation 2
      Permute(buffF,PermData,PT->P2);
      xxor(buffG,8,buffF,buffG);
//      dse(printf("51: buffG:"); HexDump(buffG,8))
      // Permutation 3
      Permute(buffF,PermData,PT->P3);
      xxor(buffG,8,buffF,buffG);
      // Permutation 4
      Permute(buffF,PermData,PT->P4);
      xxor(buffG,8,buffF,buffG);
      break;

    case 0x08:
      {
      unsigned char buff1[128], buff2[64], buff3[9];
      // prepare buffers for rsa data
      for(int i=0; i<20; i++)
        buff1[i]=buff2[i]=buff2[i+20]=buff2[i+40]=hashDW[i];
      memcpy(buff3,PermData+2,3);
      for(int i=0; i<8; i++) buff1[i] = buff3[PT->P1[i]-2] ^= buff1[i];
//      dse(printf("51: permuted hashDW:"); HexDump(buff1,20))
      for(int i=0; i<20; i++)
        buff1[i+24]=buff1[i+44]=buff1[i+68]=buff1[i+88]=buff1[i+108]=buff1[i];
//      dse(printf("51: nano51Data:"))
      for(int i=0; i<4; i++) {
        unsigned char t=PermData[PT->P3[i]];
        if(PT->P3[i]==1) t&=0x0F;
        buff1[i+20]=buff1[i+64]=buff2[i+60]=t;
//        dse(printf(" %02x",t))
        }
//      dse(printf("\n"))
//      dse(printf("51: buff1:"); HexDump(buff1,128))
//      dse(printf("51: buff2:"); HexDump(buff2,64))

      memcpy(buff3,buff1,9);
      for(int j=0; j<64; j++) { // XOR even with buff2, odd with 0xFF
        buff1[ j*2   ] ^= buff2[j];
        buff1[(j*2)+1] ^= 0xFF;
        }
///      dse(printf("51: buff1 rsa prepped:"); HexDump(buff1,128))

      // RSA decrypt
      {
      cBN exp, mod;
      cPlainKey *pk;
      if(!(pk=keys.FindKey('S',provId,MBC3('E','9',N51_MAGIC)))) {
        m_DLLInstance.Log("[Seca] missing %04x N51 E9 key\n",provId);
        
       // if(doLog) d(printf("[Seca] missing %04x N51 E9 key\n",provId))
        return false;
        }
      pk->Get(exp);
      if(!(pk=keys.FindKey('S',provId,MBC3('M','9',N51_MAGIC)))) {
        m_DLLInstance.Log("[Seca] missing %04x N51 M9 key\n",provId);
        
       // if(doLog) d(printf("[Seca] missing %04x N51 M9 key\n",provId))
        return false;
        }
      pk->Get(mod);
      DecryptRSA(buff1,128,exp,mod);
    // dse(printf("51: buff1 rsa decrypted:"); HexDump(buff1,128))
      }

      unsigned int sum=0;
      for(int j=0; j<9; j++) {
        unsigned int nextSum=(buff1[j]&0x80) ? 1:0;
        buff1[j]=(sum+2*buff1[j])^buff3[j];
        sum=nextSum;        
        }
     // dse(printf("51: buff1 after sumalgo:"); HexDump(buff1,9))

      memcpy(buffG,buff1,8);
      memcpy(buff3,PermData+2,3);
      for(int i=0; i<8; i++) buffG[i] = buff3[PT->P2[i]-2] ^= buffG[i];
      break;
      }

    default:
        m_DLLInstance.Log("51: data incorrect or proccessing not needed\n");
        
   //   dse(printf("51: data incorrect or proccessing not needed\n"))
      break;
      return false;
    }
  //dse(printf("51: cryptokey:"); HexDump(buffG,8))
  Crypto51Nano(CW,buffG,(PermData[1]>>6)&3,PermData[0]&0x80);
  // Final Permutation
  Permute(buffF,PermData,sp->FP());
  xxor(CW+0,8,CW+0,buffF); xxor(CW+8,8,CW+8,buffF);
//  dse(printf("51: cryptokey:"); HexDump(hashDW,8))
  Crypto51Nano(CW,hashDW,(PermData[1]>>4)&0x3,PermData[0]&0x40);
 // dse(printf("51: new encrypted CW:"); HexDump(CW,16))
  return true;
}

void cSystemSeca::Permute(unsigned char *data, const unsigned char *pdata, const unsigned char *P)
{
  for(int i=7; i>=0; i--) data[i] = P[i] ? pdata[P[i]] : 0;
}

void cSystemSeca::ParseCAT(const cEcmInfo *ecm, const unsigned char *buffer, int n)
{
  for(int index=8 ; index<n ;) {
    if(buffer[index]==0x09) {
      if(WORD(buffer,index+2,0xFFFF)==ecm->caId && buffer[index+1]>4) {
        int i=index+7;
        for(int nn=buffer[index+6] ; nn ; nn--) {
          if(WORD(buffer,i+2,0xFFFF)==ecm->provId) {
            int pid=WORD(buffer,i,0x1FFF);
            if(pid>=0) AddPid(pid,0x84,0xFF);
            }
          i+=4;
          }
        }
      }
    index+=buffer[index+1]+2;
    }
}

void cSystemSeca::ProcessEMM(int pid, const cEcmInfo *ecm, unsigned char *buffer, int n)
{
  if(buffer[0]!=0x84) return; // we only support shared updates

  const struct SecaEmm * const s=(struct SecaEmm *)buffer;
  const int msgLen=s->cmdLen-(sizeof(struct SecaEmm)-3);

  if(s->cmdLen!=0x55 && s->cmdLen!=0x63) {
   // ds(printf("[Seca] length of EMM does not match (%02x <> 55/63)\n",s->cmdLen))
    return;
    }

  unsigned char *emm=&buffer[sizeof(struct SecaEmm)];
  bool SE=false;
  if(emm[0]==0x10) { // de-SSE
    const int rsaKeynr = emm[1]+'0';
    cPlainKey *pk;
    cBN exp, mod;
    
    if(!(pk=keys.FindKey('S',ecm->provId,MBC3('E',rsaKeynr,EMM_MAGIC)))) return;
    pk->Get(exp);
    if(!(pk=keys.FindKey('S',ecm->provId,MBC3('M',rsaKeynr,EMM_MAGIC)))) return;
    pk->Get(mod);
 
    if(!DecryptRSA(emm+2,msgLen-2,exp,mod)) return;
    if(s->keyNr&0x80) SE=true;
    }

  const unsigned char *T1=T1_S1, *T2=T2_S1;
  int decrLen=msgLen;
  if(SE) {
    const int id=(s->id[0]<<8)+s->id[1];
    if(!spL || !spL->Matches(id)) {
      delete spL;
      spL=cSecaProv::GetProv(id);
      }
    decrLen-=emm[msgLen-1]; 
    if(!spL ||
       !(T1=spL->T1(KEY2INDEX(s->keyNr))) ||
       !(T2=spL->T2(KEY2INDEX(s->keyNr))) ||
       decrLen<8) return;
    }

#ifdef DEBUG_LOG
  bool first=true;
#endif
  for(cSecaCardInfo *ci=Scards.First(); ci; ci=Scards.cList<cSecaCardInfo>::Next(ci)) {
    if(((s->id[0]==ci->ID[0] && s->id[1]==ci->ID[1]) || (!ci->ID[0] && !ci->ID[1])) &&
       ((s->sa[0]==ci->SA[0] && s->sa[1]==ci->SA[1] && s->sa[2]==ci->SA[2]) || (!ci->SA[0] && !ci->SA[1] && !ci->SA[2] && !ci->SA[3])) ) {
#ifdef DEBUG_LOG
      char str[20],str2[20],str3[20];
      if(first) {
        printf("\rID %s SA %s",HexStr(str,s->id,2),HexStr(str2,s->sa,3));
        first=false;
        }
#endif
      unsigned char MK[16];
      if(s->seca&0x10) {
        if(ci->KeySize()!=16) continue; // don't bother
        memcpy(&MK[0],ci->KEY,16);
        }
      else {
        memcpy(&MK[0],ci->KEY,8);
        memcpy(&MK[8],ci->KEY,8);
        }

      unsigned char buff[msgLen], signature[8];
      memcpy(buff,emm,sizeof(buff)); // if decoding fails we need the original de-sse'd data

      if(!SE) {
        for(int i=0 ; i<=64 && i<msgLen-8; i+=8) Decrypt(&buff[i],MK,T1,T2);
        CalcSignature(buff,decrLen-8,signature,MK,T1,T2);
        }
      else {
        CalcSHASignature(buff,decrLen-8,signature,spL->Sha1End(),spL->Sha1Pad());
        Encrypt(signature,MK,T1,T2);
        if(memcmp(signature,&buff[decrLen-8],8) ||
           !DecryptSE(buff+2,MK,msgLen-2,0,T1,T2,spL->MT(),spL->Add())) continue;
        }

      unsigned char keyN[4], *key[4];
      int pos, len;
      unsigned int numKeys=0;
      bool sigOk=false;
      for(pos=0 ; pos<decrLen ; pos+=len) {
        int cmd=buff[pos++];
        len=(cmd>>4)&0x0f;
        switch(len) {
          case 0x0b: len=0x0e; break;
          case 0x0d: len=0x10; break;
          case 0x0e: len=0x18; break;
          case 0x0f: len=0x20; break;
          }
        switch(cmd) {
          case 0x82: // signature
            if(!memcmp(signature,&buff[pos],sizeof(signature))) sigOk=true;
            pos=decrLen;
            break;
          case 0x90: // new primary key
            if(numKeys<sizeof(keyN)) {
              keyN[numKeys]=buff[pos] & 0x0f;
              key[numKeys]=&buff[pos+1];
              numKeys++;
              }
            break;
          }
        }
      if(sigOk) {
        dl(printf(" - OK (CI ID %s SA %s KEY01 %s)",HexStr(str,ci->ID,2),HexStr(str2,ci->SA,3),HexStr(str3,ci->KEY,8)))
#ifdef DEBUG_LOG_FILE
        FILE *f=LogStart();
        if(f) {
          char str[64],str2[20],str3[20];
          fprintf(f,"ID %s SA %s -  OK ",HexStr(str,s->id,2),HexStr(str2,s->sa,3));
          fprintf(f," with CI ID %s SA %s KEY01 %s\n",HexStr(str,ci->ID,2),HexStr(str2,ci->SA,3),HexStr(str3,ci->KEY,8));
          fprintf(f,"     cmds:");
          for(pos=0 ; pos<decrLen ; pos+=len) {
            int cmd=buff[pos++];
            len=(cmd>>4)&0x0f;
            switch(len) {
              case 0x0b: len=0x0e; break;
              case 0x0d: len=0x10; break;
              case 0x0e: len=0x18; break;
              case 0x0f: len=0x20; break;
              }
            fprintf(f," 0x%02x/%d",cmd,len);
            }
          fprintf(f,"\n");
          for(int i=0 ; i<(decrLen+2) ; i+=16)
            fprintf(f,"     %02x %s\n",i,HexStr(str,&buff[i],i+16>decrLen?decrLen-i:16));
          LogEnd(f);
          }
#endif
        char str[20];
        for(unsigned int i=0 ; i<numKeys ; i++) {
          Decrypt(key[i],MK,T1,T2);
          dl(printf(" KEY %02x %s",keyN[i],HexStr(str,key[i],8)))
          m_DLLInstance.Log("key %02x %s\n",keyN[i],HexStr(str,key[i],8));
//          cSoftCAM::FoundKey();
          if(keys.NewKey('S',ecm->provId,keyN[i],key[i],8)) {
//            cSoftCAM::NewKey();
          
          m_DLLInstance.Log("New key %02x %s\n",keyN[i],HexStr(str,key[i],8));
          
#ifdef DEBUG_LOG_FILE
            FILE *f=LogStart();
            if(f) {
              char str[20];
              fprintf(f,"New key %02x %s\n",keyN[i],HexStr(str,key[i],8));
              LogEnd(f);
              }
#endif
            }
         }
//        cSoftCAM::Save();

        dl(printf("\n"))
        break;
        }
      else if((ci->ID[0]+ci->ID[1])!=0 && (ci->SA[0]+ci->SA[1]+ci->SA[2]+ci->SA[3])!=0) {
#ifdef DEBUG_LOG
        printf(" - FAIL\n");
        first=true;
#endif
#ifdef DEBUG_LOG_FILE
        FILE *f=LogStart();
        if(f) {
          char str[20],str2[20],str3[20];
          fprintf(f,"ID %s SA %s - FAIL",HexStr(str,s->id,2),HexStr(str2,s->sa,sizeof(s->sa)));
          fprintf(f," with CI ID %s SA %s KEY01 %s\n",HexStr(str,ci->ID,2),HexStr(str2,ci->SA,4),HexStr(str3,ci->KEY,8));
          LogEnd(f);
          }
#endif
        }
      dl(fflush(stdout))
      }
    }
  return;
}

// -- cSystemLinkSeca ----------------------------------------------------------

class cSystemLinkSeca : public cSystemLink {
public:
  cSystemLinkSeca(void):cSystemLink(SYSTEM_NAME,SYSTEM_PRI) {}
  virtual bool CanHandle(unsigned short SysId);
  virtual cSystem *Create(void) { return new cSystemSeca; }
  virtual bool Init(const char *cfgdir);
  };

static cSystemLinkSeca staticInit;

bool cSystemLinkSeca::CanHandle(unsigned short SysId)
{
  SysId&=SYSTEM_MASK;
  return SYSTEM_CAN_HANDLE(SysId);
}

bool cSystemLinkSeca::Init(const char *cfgdir)
{
  Scards.Load(cfgdir,SYSTEM_NAME,"Seca.KID");
  return true;
}

#else //#ifdef SECA

// add dummy loader for Seca keys
#include "system-common.h"
static cPlainKeyTypeReg<cPlainKeyDummy,'S'> KeyReg;

#endif //#ifdef SECA
