/*
 * 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
 */

#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <ctype.h>
#define OPENSSL
#ifdef OPENSSL
#include <openssl/bn.h>
#endif

#include "common.h"
#include "system-common.h"
#include "data.h"
#include "misc.h"

// -- cHexKey ------------------------------------------------------------------

cHexKey::cHexKey(bool Super)
:cPlainKey(Super)
{
  key=0;
}

cHexKey::~cHexKey()
{
  free(key);
}

bool cHexKey::SetKey(void *Key, int Keylen)
{
  keylen=Keylen;
  free(key); key=malloc(keylen);
  if(key) memcpy(key,Key,keylen);
  return key!=0;
}

bool cHexKey::Cmp(void *Key, int Keylen)
{
  return keylen==Keylen && memcmp(key,Key,keylen)==0;
}

void cHexKey::Get(void *mem)
{
  memcpy(mem,key,keylen);
}

char *cHexKey::Print(void)
{
  char *str=(char *)malloc(keylen*2+2);
  if(str) HexStr(str,(unsigned char *)key,keylen);
  return str;
}

#ifdef OPENSSL

// -- cBNKey -------------------------------------------------------------------

cBNKey::cBNKey(bool Super)
:cPlainKey(Super)
{
  key=0;
}

cBNKey::~cBNKey()
{
  if(key) BN_free((BIGNUM *)key);
}

bool cBNKey::SetKey(void *Key ,int Keylen)
{
  keylen=Keylen;
  if(key) BN_free((BIGNUM *)key);
  Rotate(Key,keylen);
  key=BN_bin2bn((unsigned char *)Key,keylen,0);
  return key!=0;
}

bool cBNKey::Cmp(void *Key, int Keylen)
{
  return (BN_cmp((BIGNUM *)key,(BIGNUM *)Key)==0);
}

void cBNKey::Get(void *mem)
{
  BN_copy((BIGNUM *)mem,(BIGNUM *)key);
}

char *cBNKey::Print(void)
{
  unsigned char mem[keylen*2];
  int len=BN_bn2bin((BIGNUM *)key,mem);
  if(len!=keylen) { // handle short BN
    const int r=keylen-len;
    memmove(mem+r,mem,len);
    memset(mem,0,r);
    }
  Rotate(mem,keylen);
  char *str=(char *)malloc(keylen*2+2);
  if(str) HexStr(str,mem,keylen);
  return str;
}

// -- cBNRKey ------------------------------------------------------------------

void RotateBytes(unsigned char *in, int n)
{
  // loop is executed atleast once, so it's not a good idea to
  // call with n=0 !!
  unsigned char *e=in+n-1;
  do {
    unsigned char temp=*in;
    *in++=*e;
    *e-- =temp;
    } while(in<e);
}

cBNRKey::cBNRKey(bool Super)
:cBNKey(Super)
{}

void cBNRKey::Rotate(void *mem, int len)
{
  RotateBytes((unsigned char *)mem,len);
}

// -- cDualKey -----------------------------------------------------------------

cDualKey::cDualKey(bool Super, bool Rotate)
:cPlainKey(Super)
{
  real=0; rotate=Rotate;
}

cDualKey::~cDualKey()
{
  delete real;
}

bool cDualKey::SetKey(void *Key, int Keylen)
{
  SetIsBNKey();
  delete real;
  if(keyIsBN) {
    if(rotate) real=new cBNRKey(CanSupersede());
    else       real=new cBNKey(CanSupersede());
    }
  else         real=new cHexKey(CanSupersede());
  if(!real) {
    d(printf("dualkey: allocation error\n"))
    return false;
    }
  return real->Set(0,0,0,Key,Keylen);
}

int cDualKey::Size(void)
{
  return real->Size();
}

bool cDualKey::Cmp(void *Key, int Keylen)
{
  return real->Cmp(Key,Keylen);
}

void cDualKey::Get(void *mem)
{
  real->Get(mem);
}

char *cDualKey::Print(void)
{
  return real->Print();
}

#endif //OPENSSL

// -- cPlainKeyStd ---------------------------------------------------------------

#define PLAINLEN_STD 8

cPlainKeyStd::cPlainKeyStd(bool Super)
:cHexKey(Super)
{}

bool cPlainKeyStd::Parse(char *line)
{
  unsigned char sid[3], skeynr, skey[PLAINLEN_STD];
#ifdef DEBUG
  char *sline=line;
#endif
  int len;
  if(GetChar(line,&type,1) && (len=GetHex(line,sid,3,false)) &&
     GetHex(line,&skeynr,1) && GetHex(line,skey,PLAINLEN_STD)) {
    type=toupper(type); id=Bin2Int(sid,len); keynr=skeynr;
    SetKey(skey,PLAINLEN_STD);
    return true;
    }
  d(printf("plainkeystd: bad key format '%s'\n",sline))
  return false;
}

bool cPlainKeyStd::Save(FILE *f)
{
  char *str=Print();
  int size=id>0xFF ? (id>0xFFFF ? 6 : 4) : 2;
  fprintf(f,"%c %.*X %.2X %s\n",type,size,id,keynr,str);
  free(str);
  return ferror(f)==0;
}
