#include "misc.h"
#include "parse.h"
#include "log-core.h"
+#include "helper.h"
#include "viaccess.h"
#include "tps.h"
keynr=MBC3('T','P','S');
ok=(len=GetHex(line,skey,VIATPS_KEYLEN));
}
+ else if(*line == 'D' || *line == 'P' || *line == 'X' || *line == 'C' || *line == 'E') {
+ if(isxdigit(line[1])) {
+ bool e_line=(*line=='E');
+ keynr=MBC3(*line,line[1]>='A' ? toupper(line[1])-'A' : line[1]-'0', 0);
+ line+=2;
+ ok=((len=GetHex(line,skey,VIA2_KEYLEN,false)) && (len==e_line ? VIA2_KEYLEN : VIA1_KEYLEN));
+ }
+ }
+ else if(*line == 'T') {
+ unsigned char tkey[256];
+ if(isxdigit(line[1])) {
+ keynr=MBC3(*line,line[1]>='A' ? toupper(line[1])-'A' : line[1]-'0', 0);
+ line+=2;
+ if((len=GetHex(line,tkey,256,false)) && (len==256)) {
+ SetBinKey(tkey,len);
+ return true;
+ }
+ }
+ }
else if(GetHex(line,&skeynr,1)) {
keynr=skeynr;
ok=((len=GetHex(line,skey,VIA2_KEYLEN,false)) && (len==VIA1_KEYLEN || len==VIA2_KEYLEN));
case MBC3('M','K',9):
snprintf(tmp,sizeof(tmp),"TPSMK%d",C3(keynr)); break;
default:
- snprintf(tmp,sizeof(tmp),"%02X",keynr); break;
+ {
+ char c2=C2(keynr);
+ if(c2=='D' || c2=='P' || c2=='X' || c2=='C' || c2=='E' || c2=='T')
+ snprintf(tmp,sizeof(tmp),"%c%01X",c2,keynr & 0x0f);
+ else
+ snprintf(tmp,sizeof(tmp),"%02X",keynr);
+ break;
+ }
}
return kn;
}
static cViaccessCardInfos Vcards;
+// -- cViacessExtendedKeys -----------------------------------------------------
+
+class cViacessExtendedKeys {
+public:
+ static bool GetKey(int provId, char keyId, int keyNr, unsigned char *key, int len, int log=1, cPlainKey **ppk=0);
+ bool GetVia26BasicKeys(int provId, int log=1);
+ unsigned char key[VIA2_KEYLEN];
+ unsigned char dkey[VIA1_KEYLEN];
+ unsigned char pkey[VIA1_KEYLEN];
+ unsigned char xkey[VIA1_KEYLEN];
+ unsigned char ckey[VIA1_KEYLEN];
+ unsigned char tkey[256];
+ unsigned char ekey[VIA2_KEYLEN];
+ };
+
+bool cViacessExtendedKeys::GetKey(int provId, char keyId, int keyNr, unsigned char *key, int len, int log, cPlainKey **ppk)
+{
+ cPlainKey *pk = keys.FindKey('V', provId, MBC3(keyId, keyNr, 0),-1,0);
+ if(pk && pk->Size()==len) {
+ pk->Get(key);
+ if(log>1) PRINTF(L_SYS_KEY,"found key \"%s\"",*pk->ToString(true));
+ if(ppk) *ppk = pk;
+ return true;
+ }
+ if(log) PRINTF(L_SYS_KEY,"missing key \"V %06X %c1 ...\"",provId, keyId);
+ return false;
+}
+
+bool cViacessExtendedKeys::GetVia26BasicKeys(int provId, int log)
+{
+ bool result=true;
+ if(!GetKey(provId, 'P', 1, pkey, sizeof(pkey), log)) result=false;
+ if(!GetKey(provId, 'X', 1, xkey, sizeof(xkey), log)) result=false;
+ if(!GetKey(provId, 'C', 1, ckey, sizeof(ckey), log)) result=false;
+ if(!GetKey(provId, 'T', 1, tkey, sizeof(tkey), log)) result=false;
+ return result;
+}
+
// -- cViaccess ----------------------------------------------------------------
class cViaccess : protected cDes {
//
int HashNanos(const unsigned char *data, int len);
void Via2Mod(const unsigned char *key2, unsigned char *data);
+ //
+ void Via3Core(int provID, const cViacessExtendedKeys *keys, unsigned char *data, int Off);
+ void Via3Fct1(int provID, const cViacessExtendedKeys *keys, unsigned char *data);
+ void Via3Fct2(int provID, const cViacessExtendedKeys *keys, unsigned char *data);
protected:
unsigned char hbuff[8], hkey[8];
int pH;
//
void Decode(unsigned char *data, const unsigned char *key);
bool Decrypt(const unsigned char *work_key, const unsigned char *data, int len, unsigned char *des_data1, unsigned char *des_data2);
+ bool Decrypt26(const cViacessExtendedKeys *work_keys, const unsigned char *ecw, unsigned char *des_data1, unsigned char *des_data2);
+ bool Decrypt3(int provID, const cViacessExtendedKeys *work_keys, const unsigned char *ecw, int modeAES, unsigned char *des_data1, unsigned char *des_data2);
//
virtual unsigned int Mod(unsigned int R, unsigned int key7) const;
public:
return (memcmp(signatur,hbuff,8)==0);
}
+void cViaccess::Via3Core(int provID, const cViacessExtendedKeys *keys, unsigned char *data, int Off)
+{
+ int i;
+ unsigned long R2, R3, R4, R6, R7;
+
+ for(i=0; i<4; i++) data[i]^= keys->xkey[(Off+i) & 0x07];
+ switch(provID) {
+ case 0x032820:
+ R2 = (data[0]^0xBD)+data[0];
+ R3 = (data[3]^0xEB)+data[3];
+ R2 = (R2-R3)^data[2];
+ R3 = ((0x39*data[1])<<2);
+ data[4] = (R2|R3)+data[2];
+
+ R3 = ((((data[0]+6)^data[0]) | (data[2]<<1))^0x65)+data[0];
+ R2 = (data[1]^0xED)+data[1];
+ R7 = ((data[3]+0x29)^data[3])*R2;
+ data[5] = R7+R3;
+
+ R2 = ((data[2]^0x33)+data[2]) & 0x0A;
+ R3 = (data[0]+0xAD)^data[0];
+ R3 = R3+R2;
+ R2 = data[3]*data[3];
+ R7 = (R2 | 1) + data[1];
+ data[6] = (R3|R7)+data[1];
+
+ R3 = data[1] & 0x07;
+ R2 = (R3-data[2]) & (data[0] | R2 |0x01);
+ data[7] = R2+data[3];
+ break;
+ case 0x030B00:
+ R6 = (data[3] + 0x6E) ^ data[3];
+ R6 = (R6*(data[2] << 1)) + 0x17;
+ R3 = (data[1] + 0x77) ^ data[1];
+ R4 = (data[0] + 0xD7) ^ data[0];
+ data[4] = ((R4 & R3) | R6) + data[0];
+
+ R4 = ((data[3] + 0x71) ^ data[3]) ^ 0x90;
+ R6 = (data[1] + 0x1B) ^ data[1];
+ R4 = (R4*R6) ^ data[0];
+ data[5] = (R4 ^ (data[2] << 1)) + data[1];
+
+ R3 = (data[3] * data[3])| 0x01;
+ R4 = (((data[2] ^ 0x35) + data[2]) | R3) + data[2];
+ R6 = data[1] ^ (data[0] + 0x4A);
+ data[6] = R6 + R4;
+
+ R3 = (data[0] * (data[2] << 1)) | data[1];
+ R4 = 0xFE - data[3];
+ R3 = R4 ^ R3;
+ data[7] = R3 + data[3];
+ break;
+ default:
+ break;
+ }
+ for(i=4;i<8;i++) data[i] = keys->tkey[data[i]];
+}
+
+void cViaccess::Via3Fct1(int provID, const cViacessExtendedKeys *keys, unsigned char *data)
+{
+ Via3Core(provID, keys, data, 0);
+ switch(provID) {
+ case 0x032820:
+ // swap data[4] and data[7]
+ data[4] ^= data[7];
+ data[7] ^= data[4];
+ data[4] ^= data[7];
+ break;
+ case 0x030B00:
+ // swap data[5] and data[7]
+ data[5] ^= data[7];
+ data[7] ^= data[5];
+ data[5] ^= data[7];
+ break;
+ default:
+ break;
+ }
+}
+
+void cViaccess::Via3Fct2(int provID, const cViacessExtendedKeys *keys, unsigned char *data)
+{
+ unsigned char t;
+ Via3Core(provID, keys, data, 4);
+ switch(provID) {
+ case 0x032820:
+ t = data[4];
+ data[4] = data[7];
+ data[7] = data[5];
+ data[5] = data[6];
+ data[6] = t;
+ break;
+ case 0x030B00:
+ t = data[6];
+ data[6] = data[7];
+ data[7] = t;
+ break;
+ default:
+ break;
+ }
+}
+
+#define DES_ECS2_DECRYPT (DES_IP | DES_FP | DES_RIGHT)
+#define DES_ECS2_CRYPT (DES_IP | DES_FP)
+
+bool cViaccess::Decrypt3(int provID, const cViacessExtendedKeys *work_keys, const unsigned char *ecw, int modeAES, unsigned char *des_data1, unsigned char *des_data2) {
+ unsigned char dcw[16];
+ char hex1[34], hex2[34];
+ cLogLineBuff LineBuff(L_SYS_KEY);
+ int i, pass;
+ memcpy(dcw,ecw,16);
+
+ LineBuff.Printf("ecw %s %s", HexStr(hex1, dcw, 8), HexStr(hex2, dcw+8, 8));
+ LineBuff.Flush();
+
+ if(modeAES==1) {
+ cAES Aes;
+ Aes.SetKey(work_keys->ekey);
+ Aes.Decrypt(dcw, 16);
+ }
+ memcpy(des_data1,dcw,8); // needed for final xor
+
+ for(pass = 0; pass < 2; pass++) {
+ unsigned char *tmp_dcw=dcw + pass * 8, tmp[8];
+ for (i=0; i<4; i++) tmp[i] = tmp_dcw[i+4];
+ Via3Fct1(provID, work_keys, tmp);
+ for (i=0; i<4; i++) tmp[i] = tmp_dcw[i]^tmp[i+4];
+ Via3Fct2(provID, work_keys, tmp);
+ for (i=0; i<4; i++) tmp[i]^= work_keys->xkey[i+4];
+ for (i=0; i<4; i++) {
+ tmp_dcw[i] = tmp_dcw[i+4]^tmp[i+4];
+ tmp_dcw[i+4] = tmp[i];
+ }
+ Des(tmp_dcw, work_keys->key, DES_PC1 | DES_ECS2_DECRYPT);
+ Des(tmp_dcw, work_keys->key + 8, DES_PC1 | DES_ECS2_CRYPT);
+ Des(tmp_dcw, work_keys->key, DES_PC1 | DES_ECS2_DECRYPT);
+ for (i=0; i<4; i++) tmp[i] = tmp_dcw[i+4];
+ Via3Fct2(provID, work_keys, tmp);
+ for (i=0; i<4; i++) tmp[i] = tmp_dcw[i]^tmp[i+4];
+ Via3Fct1(provID, work_keys, tmp);
+ for (i=0; i<4; i++) tmp[i]^= work_keys->xkey[i];
+ for (i=0; i<4; i++) {
+ tmp_dcw[i] = tmp_dcw[i+4]^tmp[i+4];
+ tmp_dcw[i+4] = tmp[i];
+ }
+ }
+ xxor(dcw, 8, dcw, work_keys->ckey);
+ xxor(dcw + 8, 8, dcw + 8, des_data1);
+
+ if(modeAES==2) {
+ cAES Aes;
+ Aes.SetKey(work_keys->ekey);
+ Aes.Decrypt(dcw, 16);
+ }
+
+ LineBuff.Printf("cw %s %s", HexStr(hex1, dcw, 8), HexStr(hex2, dcw+8, 8));
+ LineBuff.Flush();
+
+ for(i=0;i<16;i+=4) if(dcw[i+3] != ((dcw[i]+dcw[i+1]+dcw[i+2]) & 0xFF)) return false;
+ memcpy(des_data1, dcw, 8);
+ memcpy(des_data2, dcw+8, 8);
+ return true;
+}
+
+bool cViaccess::Decrypt26(const cViacessExtendedKeys *work_keys, const unsigned char *ecw, unsigned char *des_data1, unsigned char *des_data2)
+{
+ unsigned char dcw[16];
+ char hex1[34], hex2[34];
+ cLogLineBuff LineBuff(L_SYS_KEY);
+ int i, pass;
+ memcpy(dcw,ecw,16);
+
+ LineBuff.Printf("ecw %s %s", HexStr(hex1, dcw, 8), HexStr(hex2, dcw+8, 8));
+ LineBuff.Flush();
+
+ for(pass = 0; pass < 2; pass++) {
+ unsigned char *tmp_dcw=dcw + pass * 8, tmp[8];
+ for(i = 0; i < 8; i++) tmp[i] = work_keys->tkey[tmp_dcw[i]];
+ for(i = 0; i < 8; i++) tmp_dcw[i] = tmp[work_keys->pkey[i]];
+ Des(tmp_dcw, work_keys->dkey, DES_PC1 | DES_ECS2_CRYPT);
+ xxor(tmp_dcw, 8, tmp_dcw, work_keys->xkey);
+ Des(tmp_dcw, work_keys->key, DES_PC1 | DES_ECS2_DECRYPT);
+ Des(tmp_dcw, work_keys->key + 8, DES_PC1 | DES_ECS2_CRYPT);
+ Des(tmp_dcw, work_keys->key, DES_PC1 | DES_ECS2_DECRYPT);
+ xxor(tmp_dcw, 8, tmp_dcw, work_keys->xkey);
+ Des(tmp_dcw, work_keys->dkey, DES_PC1 | DES_ECS2_DECRYPT);
+ for(i = 0; i < 8; i++) tmp[work_keys->pkey[i]] = tmp_dcw[i];
+ for(i = 0; i < 8; i++) tmp_dcw[i] = work_keys->tkey[tmp[i]];
+ }
+ xxor(dcw, 8, dcw, work_keys->ckey);
+ xxor(dcw + 8, 8, dcw + 8, ecw);
+
+ LineBuff.Printf("cw %s %s", HexStr(hex1, dcw, 8), HexStr(hex2, dcw+8, 8));
+ LineBuff.Flush();
+
+ for(i=0;i<16;i+=4) if(dcw[i+3] != ((dcw[i]+dcw[i+1]+dcw[i+2]) & 0xFF)) return false;
+ memcpy(des_data1, dcw, 8);
+ memcpy(des_data2, dcw+8, 8);
+ return true;
+}
+
// -- cSystemViaccess ----------------------------------------------------------
#define MAX_NEW_KEYS 5
if(num<0) return false;
nanos+=num; len-=num;
}
-
- if(cParseViaccess::CheckNano90FromNano(nanos)) {
- int keynr=cParseViaccess::KeyNrFromNano(nanos);
- cKeySnoop ks(this,'V',ecm->provId,keynr);
- cPlainKey *pk=0;
- while((pk=keys.FindKey('V',ecm->provId,keynr,-1,pk))) {
- unsigned char key[16];
- if(pk->Size()<=(int)sizeof(key)) {
- pk->Get(key);
- SetV2Mode(pk->Size()==VIA2_KEYLEN ? &key[VIA1_KEYLEN] : 0);
- if(cViaccess::Decrypt(key,&nanos[5],len-5,&cw[0],&cw[8])) {
- if(mayHaveTps) tps.PostProc(cw);
- ks.OK(pk);
- return true;
+ cViacessExtendedKeys work_keys;
+ int nanoCmd, nanoLen=0, version=0, pos=0, desKeyIdx=-1, keySelectPos=0, providerKeyLen=0, aesMode=0, encStart=0;
+ cPlainKey *pk=0;
+ unsigned char signatur[8];
+ for(;pos<len; pos+=nanoLen) {
+ nanoCmd = nanos[pos++];
+ nanoLen = nanos[pos++];
+ switch(nanoCmd) {
+ case 0x40:
+ if(nanoLen < 0x03) break;
+ version = nanos[pos];
+ if(nanoLen == 3) { //CSat
+// currentIdent=((nanos[pos+i]<<16)|(nanos[pos+1]<<8))|(nanos[pos+2]&0xF0);
+ desKeyIdx = nanos[pos+2]&0x0F;
+ keySelectPos = pos+3;
}
- }
+ else { //TNT
+// currentIdent=(nanos[pos+0]<<16)|(nanos[pos+1]<<8)|((nanos[pos+2]>>4)&0x0F);
+ desKeyIdx = nanos[pos+3];
+ keySelectPos = pos+4;
+ }
+ pk=0;
+ if(keys.FindKey('V',ecm->provId,desKeyIdx,-1,pk)) {
+ if(version >= 2 && !work_keys.GetVia26BasicKeys(ecm->provId, 1))
+ desKeyIdx = -1;
+ if(version == 2 && !work_keys.GetKey(ecm->provId, 'D', 1, work_keys.dkey, sizeof(work_keys.dkey), 1))
+ desKeyIdx = -1;
+ }
+ else
+ desKeyIdx = -1;
+// if (showLog) SendMSG(0,"nano40->nanoLen:%d, version:%d, provider:0x%x, keyIdx:0x%x", nanoLen, version, currentIdent, desKeyIdx);
+ providerKeyLen = nanoLen;
+ break;
+ case 0x90:
+ if (nanoLen < 0x03)
+ break;
+ version = nanos[pos];
+// currentIdent= ((nanos[pos]<<16)|(nanos[pos+1]<<8))|(nanos[pos+2]&0xF0);
+ desKeyIdx = nanos[pos+2]&0x0F;
+ keySelectPos = pos+4;
+ if((version == 3) && (nanoLen > 3))
+ desKeyIdx = nanos[pos+(nanoLen-4)]&0x0F;
+ pk=0;
+ if(keys.FindKey('V',ecm->provId,desKeyIdx,-1,pk)) {
+ if(version >= 2 && !work_keys.GetVia26BasicKeys(ecm->provId, 1))
+ desKeyIdx = -1;
+ if(version == 2 && !work_keys.GetKey(ecm->provId, 'D', 1, work_keys.dkey, sizeof(work_keys.dkey), 1))
+ desKeyIdx = -1;
+ }
+ else
+ desKeyIdx = -1;
+// if (showLog) SendMSG(0,"nano90->nanoLen:%d, version:%d, provider:0x%x, keyIdx:0x%x",nanoLen, version,currentIdent,desKeyIdx);
+ providerKeyLen = nanoLen;
+ break;
+ case 0x80: // sub ecm
+ nanoLen = 0;
+ break;
+ case 0xD2:
+ if(nanoLen < 0x02) break;
+ if(!work_keys.GetKey(ecm->provId, 'E', nanos[pos+1]==1 ? 1 : 2, work_keys.ekey, sizeof(work_keys.ekey), 1))
+ break;
+ if(nanos[pos]==0x0B) aesMode = 1;
+ if(nanos[pos]==0x0D) aesMode = 2;
+ break;
+ case 0xDD:
+ nanoLen = 0;
+ break;
+ case 0xEA:
+ if(nanoLen < 0x10) break;
+// memcpy(des_data1,&nanos[pos],8);
+// memcpy(des_data2,&nanos[pos+8],8);
+ if (version == 3) {
+ if (ecm->provId == 0x030B00 && providerKeyLen>3) {
+ //surencrypted DCWs with set 1 of keys (i.e. publicly known AES keys)
+ bool isGoodTntPacket = (nanos[keySelectPos]==0x05 && nanos[keySelectPos+1]==0x67 && nanos[keySelectPos+2]==0x00);
+ if(!isGoodTntPacket) break; //try next TNT packet
+ }
+ }
+ encStart = pos;
+ break;
+ case 0xf0: // signature
+ memcpy(signatur,&nanos[pos],8);
+ if(desKeyIdx!=-1 && encStart) { // has nano90, encryptet Key (nanoAE) and Signature (nano0F)
+ cKeySnoop ks(this,'V',ecm->provId,desKeyIdx);
+ cPlainKey *pk=0;
+ while((pk=keys.FindKey('V',ecm->provId,desKeyIdx,-1,pk))) {
+ if(pk->Size()<=(int)sizeof(work_keys.key)) {
+ pk->Get(work_keys.key);
+ if(version==1) {
+ SetV2Mode(pk->Size()==VIA2_KEYLEN ? &work_keys.key[VIA1_KEYLEN] : 0);
+ if(cViaccess::Decrypt(work_keys.key,&nanos[5],len-5,&cw[0],&cw[8])) {
+ if(mayHaveTps) tps.PostProc(cw);
+ ks.OK(pk);
+ return true;
+ }
+ }
+ else if(version==2) {
+ if(cViaccess::Decrypt26(&work_keys,&nanos[encStart],&cw[0],&cw[8])) {
+ ks.OK(pk);
+ return true;
+ }
+ }
+ else if(version==3) {
+ if(cViaccess::Decrypt3(ecm->provId, &work_keys,&nanos[encStart], aesMode,&cw[0],&cw[8])) {
+ ks.OK(pk);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ version=0; desKeyIdx=-1; encStart=0;
+ break;
}
}
return false;