1184 lines
20 KiB
C
1184 lines
20 KiB
C
|
|
#define MODULE_LOG_PREFIX "emu"
|
||
|
|
|
||
|
|
#include "globals.h"
|
||
|
|
|
||
|
|
#ifdef WITH_EMU
|
||
|
|
|
||
|
|
#include "cscrypt/des.h"
|
||
|
|
#include "module-emulator-osemu.h"
|
||
|
|
#include "module-newcamd-des.h"
|
||
|
|
#include "oscam-aes.h"
|
||
|
|
#include "oscam-string.h"
|
||
|
|
|
||
|
|
// from reader-viaccess.c:
|
||
|
|
void hdSurEncPhase1_D2_0F_11(uint8_t *CWs);
|
||
|
|
void hdSurEncPhase2_D2_0F_11(uint8_t *CWs);
|
||
|
|
void hdSurEncPhase1_D2_13_15(uint8_t *cws);
|
||
|
|
void hdSurEncPhase2_D2_13_15(uint8_t *cws);
|
||
|
|
|
||
|
|
// Viaccess EMU
|
||
|
|
|
||
|
|
static int8_t get_key(uint8_t *buf, uint32_t ident, char keyName, uint32_t keyIndex,
|
||
|
|
uint32_t keyLength, uint8_t isCriticalKey)
|
||
|
|
{
|
||
|
|
char keyStr[EMU_MAX_CHAR_KEYNAME];
|
||
|
|
snprintf(keyStr, EMU_MAX_CHAR_KEYNAME, "%c%X", keyName, keyIndex);
|
||
|
|
|
||
|
|
if (emu_find_key('V', ident, 0, keyStr, buf, keyLength, isCriticalKey, 0, 0, NULL))
|
||
|
|
{
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ident == 0xD00040 && emu_find_key('V', 0x030B00, 0, keyStr, buf, keyLength, isCriticalKey, 0, 0, NULL))
|
||
|
|
{
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void via1_mod(const uint8_t *key2, uint8_t *data)
|
||
|
|
{
|
||
|
|
int32_t kb, db;
|
||
|
|
|
||
|
|
for (db = 7; db >= 0; db--)
|
||
|
|
{
|
||
|
|
for (kb = 7; kb > 3; kb--)
|
||
|
|
{
|
||
|
|
int32_t a0 = kb ^ db;
|
||
|
|
int32_t pos = 7;
|
||
|
|
|
||
|
|
if (a0 & 4)
|
||
|
|
{
|
||
|
|
a0 ^= 7;
|
||
|
|
pos ^= 7;
|
||
|
|
}
|
||
|
|
|
||
|
|
a0 = (a0 ^ (kb & 3)) + (kb & 3);
|
||
|
|
|
||
|
|
if (!(a0 & 4))
|
||
|
|
{
|
||
|
|
data[db] ^= (key2[kb] ^ ((data[kb ^ pos] * key2[kb ^ 4]) & 0xFF));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for (db = 0; db < 8; db++)
|
||
|
|
{
|
||
|
|
for (kb = 0; kb < 4; kb++)
|
||
|
|
{
|
||
|
|
int32_t a0 = kb ^ db;
|
||
|
|
int32_t pos = 7;
|
||
|
|
|
||
|
|
if (a0 & 4)
|
||
|
|
{
|
||
|
|
a0 ^= 7;
|
||
|
|
pos ^= 7;
|
||
|
|
}
|
||
|
|
|
||
|
|
a0 = (a0 ^ (kb & 3)) + (kb & 3);
|
||
|
|
|
||
|
|
if (!(a0 & 4))
|
||
|
|
{
|
||
|
|
data[db] ^= (key2[kb] ^ ((data[kb ^ pos] * key2[kb ^ 4]) & 0xFF));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void via1_decode(uint8_t *data, uint8_t *key)
|
||
|
|
{
|
||
|
|
via1_mod(key + 8, data);
|
||
|
|
nc_des(key, DES_ECM_CRYPT, data);
|
||
|
|
via1_mod(key + 8, data);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void via1_hash(uint8_t *data, uint8_t *key)
|
||
|
|
{
|
||
|
|
via1_mod(key + 8, data);
|
||
|
|
nc_des(key, DES_ECM_HASH, data);
|
||
|
|
via1_mod(key + 8, data);
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline void via1_do_hash(uint8_t *hashbuffer, uint8_t *pH, uint8_t data, uint8_t *hashkey)
|
||
|
|
{
|
||
|
|
hashbuffer[*pH] ^= data;
|
||
|
|
(*pH)++;
|
||
|
|
|
||
|
|
if (*pH == 8)
|
||
|
|
{
|
||
|
|
via1_hash(hashbuffer, hashkey);
|
||
|
|
*pH = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static int8_t via1_decrypt(uint8_t *ecm, uint8_t *dw, uint32_t ident, uint8_t desKeyIndex)
|
||
|
|
{
|
||
|
|
int32_t msg_pos, encStart = 0, hash_start, i;
|
||
|
|
|
||
|
|
uint8_t tmp, k, pH, foundData = 0;
|
||
|
|
uint8_t work_key[16], signature[8], hashbuffer[8], prepared_key[16], hashkey[16];
|
||
|
|
uint8_t *data, *des_data1, *des_data2;
|
||
|
|
|
||
|
|
uint16_t ecmLen = SCT_LEN(ecm);
|
||
|
|
|
||
|
|
if (ident == 0)
|
||
|
|
{
|
||
|
|
return EMU_CORRUPT_DATA;
|
||
|
|
}
|
||
|
|
|
||
|
|
memset(work_key, 0, 16);
|
||
|
|
|
||
|
|
if (!get_key(work_key, ident, '0', desKeyIndex, 8, 1))
|
||
|
|
{
|
||
|
|
return EMU_KEY_NOT_FOUND;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ecmLen < 11)
|
||
|
|
{
|
||
|
|
return EMU_NOT_SUPPORTED;
|
||
|
|
}
|
||
|
|
|
||
|
|
data = ecm + 9;
|
||
|
|
des_data1 = dw;
|
||
|
|
des_data2 = dw + 8;
|
||
|
|
|
||
|
|
msg_pos = 0;
|
||
|
|
pH = 0;
|
||
|
|
memset(hashbuffer, 0, sizeof(hashbuffer));
|
||
|
|
memcpy(hashkey, work_key, sizeof(hashkey));
|
||
|
|
memset(signature, 0, 8);
|
||
|
|
|
||
|
|
while (9 + msg_pos + 2 < ecmLen)
|
||
|
|
{
|
||
|
|
switch (data[msg_pos])
|
||
|
|
{
|
||
|
|
case 0xEA:
|
||
|
|
if (9 + msg_pos + 2 + 15 < ecmLen)
|
||
|
|
{
|
||
|
|
encStart = msg_pos + 2;
|
||
|
|
memcpy(des_data1, &data[msg_pos + 2], 8);
|
||
|
|
memcpy(des_data2, &data[msg_pos + 2 + 8], 8);
|
||
|
|
foundData |= 1;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 0xF0:
|
||
|
|
if (9 + msg_pos + 2 + 7 < ecmLen)
|
||
|
|
{
|
||
|
|
memcpy(signature, &data[msg_pos + 2], 8);
|
||
|
|
foundData |= 2;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
msg_pos += data[msg_pos + 1] + 2;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (foundData != 3)
|
||
|
|
{
|
||
|
|
return EMU_NOT_SUPPORTED;
|
||
|
|
}
|
||
|
|
|
||
|
|
pH = i = 0;
|
||
|
|
|
||
|
|
if (data[0] == 0x9F && 10 + data[1] <= ecmLen)
|
||
|
|
{
|
||
|
|
via1_do_hash(hashbuffer, &pH, data[i++], hashkey);
|
||
|
|
via1_do_hash(hashbuffer, &pH, data[i++], hashkey);
|
||
|
|
|
||
|
|
for (hash_start = 0; hash_start < data[1]; hash_start++)
|
||
|
|
{
|
||
|
|
via1_do_hash(hashbuffer, &pH, data[i++], hashkey);
|
||
|
|
}
|
||
|
|
|
||
|
|
while (pH != 0)
|
||
|
|
{
|
||
|
|
via1_do_hash(hashbuffer, &pH, 0, hashkey);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (work_key[7] == 0)
|
||
|
|
{
|
||
|
|
for (; i < encStart + 16; i++)
|
||
|
|
{
|
||
|
|
via1_do_hash(hashbuffer, &pH, data[i], hashkey);
|
||
|
|
}
|
||
|
|
memcpy(prepared_key, work_key, 8);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
prepared_key[0] = work_key[2];
|
||
|
|
prepared_key[1] = work_key[3];
|
||
|
|
prepared_key[2] = work_key[4];
|
||
|
|
prepared_key[3] = work_key[5];
|
||
|
|
prepared_key[4] = work_key[6];
|
||
|
|
prepared_key[5] = work_key[0];
|
||
|
|
prepared_key[6] = work_key[1];
|
||
|
|
prepared_key[7] = work_key[7];
|
||
|
|
|
||
|
|
memcpy(prepared_key + 8, work_key + 8, 8);
|
||
|
|
|
||
|
|
if (work_key[7] & 1)
|
||
|
|
{
|
||
|
|
for (; i < encStart; i++)
|
||
|
|
{
|
||
|
|
via1_do_hash(hashbuffer, &pH, data[i], hashkey);
|
||
|
|
}
|
||
|
|
|
||
|
|
k = ((work_key[7] & 0xF0) == 0) ? 0x5A : 0xA5;
|
||
|
|
|
||
|
|
for (i = 0; i < 8; i++)
|
||
|
|
{
|
||
|
|
tmp = des_data1[i];
|
||
|
|
des_data1[i] = (k & hashbuffer[pH] ) ^ tmp;
|
||
|
|
via1_do_hash(hashbuffer, &pH, tmp, hashkey);
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; i < 8; i++)
|
||
|
|
{
|
||
|
|
tmp = des_data2[i];
|
||
|
|
des_data2[i] = (k & hashbuffer[pH] ) ^ tmp;
|
||
|
|
via1_do_hash(hashbuffer, &pH, tmp, hashkey);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
for ( ; i < encStart + 16; i++)
|
||
|
|
{
|
||
|
|
via1_do_hash(hashbuffer, &pH, data[i], hashkey);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
via1_decode(des_data1, prepared_key);
|
||
|
|
via1_decode(des_data2, prepared_key);
|
||
|
|
via1_hash(hashbuffer, hashkey);
|
||
|
|
|
||
|
|
if (memcmp(signature, hashbuffer, 8))
|
||
|
|
{
|
||
|
|
return EMU_CHECKSUM_ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
return EMU_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int8_t via26_process_dw(uint8_t *indata, uint32_t ident, uint8_t desKeyIndex)
|
||
|
|
{
|
||
|
|
uint8_t pv1,pv2, i;
|
||
|
|
uint8_t Tmp[8], T1Key[300], P1Key[8], KeyDes1[16], KeyDes2[16], XorKey[8];
|
||
|
|
uint32_t ks1[32], ks2[32];
|
||
|
|
|
||
|
|
if (!get_key(T1Key, ident, 'T', 1, 300, 1))
|
||
|
|
{
|
||
|
|
return 2;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!get_key(P1Key, ident, 'P', 1, 8, 1))
|
||
|
|
{
|
||
|
|
return 2;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!get_key(KeyDes1, ident, 'D', 1, 16, 1))
|
||
|
|
{
|
||
|
|
return 2;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!get_key(KeyDes2, ident, '0', desKeyIndex, 16, 1))
|
||
|
|
{
|
||
|
|
return 2;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!get_key(XorKey, ident, 'X', 1, 8, 1))
|
||
|
|
{
|
||
|
|
return 2;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; i < 8; i++)
|
||
|
|
{
|
||
|
|
pv1 = indata[i];
|
||
|
|
Tmp[i] = T1Key[pv1];
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; i < 8; i++)
|
||
|
|
{
|
||
|
|
pv1 = P1Key[i];
|
||
|
|
pv2 = Tmp[pv1];
|
||
|
|
indata[i] = pv2;
|
||
|
|
}
|
||
|
|
|
||
|
|
des_set_key(KeyDes1, ks1);
|
||
|
|
des(indata, ks1, 1);
|
||
|
|
|
||
|
|
for (i = 0; i < 8; i++)
|
||
|
|
{
|
||
|
|
indata[i] ^= XorKey[i];
|
||
|
|
}
|
||
|
|
|
||
|
|
des_set_key(KeyDes2, ks1);
|
||
|
|
des_set_key(KeyDes2 + 8, ks2);
|
||
|
|
des(indata, ks1, 0);
|
||
|
|
des(indata, ks2, 1);
|
||
|
|
des(indata, ks1, 0);
|
||
|
|
|
||
|
|
for (i = 0; i < 8; i++)
|
||
|
|
{
|
||
|
|
indata[i] ^= XorKey[i];
|
||
|
|
}
|
||
|
|
|
||
|
|
des_set_key(KeyDes1, ks1);
|
||
|
|
des(indata, ks1, 0);
|
||
|
|
|
||
|
|
for (i = 0; i < 8; i++)
|
||
|
|
{
|
||
|
|
pv1 = indata[i];
|
||
|
|
pv2 = P1Key[i];
|
||
|
|
Tmp[pv2] = pv1;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; i < 8; i++)
|
||
|
|
{
|
||
|
|
pv1 = Tmp[i];
|
||
|
|
pv2 = T1Key[pv1];
|
||
|
|
indata[i] = pv2;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int8_t via26_decrypt(uint8_t *source, uint8_t *dw, uint32_t ident, uint8_t desKeyIndex)
|
||
|
|
{
|
||
|
|
uint8_t tmpData[8], C1[8];
|
||
|
|
uint8_t *pXorVector;
|
||
|
|
int32_t i, j;
|
||
|
|
|
||
|
|
if (ident == 0)
|
||
|
|
{
|
||
|
|
return EMU_CORRUPT_DATA;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!get_key(C1, ident, 'C', 1, 8, 1))
|
||
|
|
{
|
||
|
|
return EMU_KEY_NOT_FOUND;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; i < 2; i++)
|
||
|
|
{
|
||
|
|
memcpy(tmpData, source + i * 8, 8);
|
||
|
|
via26_process_dw(tmpData, ident, desKeyIndex);
|
||
|
|
|
||
|
|
if (i != 0)
|
||
|
|
{
|
||
|
|
pXorVector = source;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
pXorVector = &C1[0];
|
||
|
|
}
|
||
|
|
|
||
|
|
for (j = 0; j < 8; j++)
|
||
|
|
{
|
||
|
|
dw[i * 8 + j] = tmpData[j] ^ pXorVector[j];
|
||
|
|
}
|
||
|
|
|
||
|
|
// Fix CW checksum bytes
|
||
|
|
for (j = 3; j < 8; j += 4)
|
||
|
|
{
|
||
|
|
dw[i * 8 + j] = (dw[i * 8 + j - 3] + dw[i * 8 + j - 2] + dw[i * 8 + j - 1]) & 0xFF;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return EMU_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void via3_core(uint8_t *data, uint8_t Off, uint32_t ident, uint8_t *XorKey, uint8_t *T1Key)
|
||
|
|
{
|
||
|
|
uint8_t i;
|
||
|
|
uint32_t lR2, lR3, lR4, lR6, lR7;
|
||
|
|
|
||
|
|
switch (ident)
|
||
|
|
{
|
||
|
|
case 0x032820:
|
||
|
|
{
|
||
|
|
for (i = 0; i < 4; i++)
|
||
|
|
{
|
||
|
|
data[i] ^= XorKey[(Off + i) & 0x07];
|
||
|
|
}
|
||
|
|
|
||
|
|
lR2 = (data[0] ^ 0xBD) + data[0];
|
||
|
|
lR3 = (data[3] ^ 0xEB) + data[3];
|
||
|
|
lR2 = (lR2 - lR3) ^ data[2];
|
||
|
|
lR3 = ((0x39 * data[1]) << 2);
|
||
|
|
data[4] = (lR2 | lR3) + data[2];
|
||
|
|
|
||
|
|
lR3 = ((((data[0] + 6) ^ data[0]) | (data[2] << 1)) ^ 0x65) + data[0];
|
||
|
|
lR2 = (data[1] ^ 0xED) + data[1];
|
||
|
|
lR7 = ((data[3] + 0x29) ^ data[3]) * lR2;
|
||
|
|
data[5] = lR7 + lR3;
|
||
|
|
|
||
|
|
lR2 = ((data[2] ^ 0x33) + data[2]) & 0x0A;
|
||
|
|
lR3 = (data[0] + 0xAD) ^ data[0];
|
||
|
|
lR3 = lR3 + lR2;
|
||
|
|
lR2 = data[3] * data[3];
|
||
|
|
lR7 = (lR2 | 1) + data[1];
|
||
|
|
data[6] = (lR3 | lR7) + data[1];
|
||
|
|
|
||
|
|
lR3 = data[1] & 0x07;
|
||
|
|
lR2 = (lR3 - data[2]) & (data[0] | lR2 | 0x01);
|
||
|
|
data[7] = lR2 + data[3];
|
||
|
|
|
||
|
|
for (i = 0; i < 4; i++)
|
||
|
|
{
|
||
|
|
data[i + 4] = T1Key[data[i + 4]];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 0x030B00:
|
||
|
|
{
|
||
|
|
for (i = 0; i < 4; i++)
|
||
|
|
{
|
||
|
|
data[i] ^= XorKey[(Off + i) & 0x07];
|
||
|
|
}
|
||
|
|
|
||
|
|
lR6 = (data[3] + 0x6E) ^ data[3];
|
||
|
|
lR6 = (lR6 * (data[2] << 1)) + 0x17;
|
||
|
|
lR3 = (data[1] + 0x77) ^ data[1];
|
||
|
|
lR4 = (data[0] + 0xD7) ^ data[0];
|
||
|
|
data[4] = ((lR4 & lR3) | lR6) + data[0];
|
||
|
|
|
||
|
|
lR4 = ((data[3] + 0x71) ^ data[3]) ^ 0x90;
|
||
|
|
lR6 = (data[1] + 0x1B) ^ data[1];
|
||
|
|
lR4 = (lR4 * lR6) ^ data[0];
|
||
|
|
data[5] = (lR4 ^ (data[2] << 1)) + data[1];
|
||
|
|
|
||
|
|
lR3 = (data[3] * data[3]) | 0x01;
|
||
|
|
lR4 = (((data[2] ^ 0x35) + data[2]) | lR3) + data[2];
|
||
|
|
lR6 = data[1] ^ (data[0] + 0x4A);
|
||
|
|
data[6] = lR6 + lR4;
|
||
|
|
|
||
|
|
lR3 = (data[0] * (data[2] << 1)) | data[1];
|
||
|
|
lR4 = 0xFE - data[3];
|
||
|
|
lR3 = lR4 ^ lR3;
|
||
|
|
data[7] = lR3 + data[3];
|
||
|
|
|
||
|
|
for (i = 0; i < 4; i++)
|
||
|
|
{
|
||
|
|
data[4 + i] = T1Key[data[4 + i]];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void via3_fct1(uint8_t *data, uint32_t ident, uint8_t *XorKey, uint8_t *T1Key)
|
||
|
|
{
|
||
|
|
uint8_t t;
|
||
|
|
|
||
|
|
via3_core(data, 0, ident, XorKey, T1Key);
|
||
|
|
|
||
|
|
switch (ident)
|
||
|
|
{
|
||
|
|
case 0x032820:
|
||
|
|
{
|
||
|
|
t = data[4];
|
||
|
|
data[4] = data[7];
|
||
|
|
data[7] = t;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 0x030B00:
|
||
|
|
{
|
||
|
|
t = data[5];
|
||
|
|
data[5] = data[7];
|
||
|
|
data[7] = t;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void via3_fct2(uint8_t *data, uint32_t ident, uint8_t *XorKey, uint8_t *T1Key)
|
||
|
|
{
|
||
|
|
uint8_t t;
|
||
|
|
|
||
|
|
via3_core(data, 4, ident, XorKey, T1Key);
|
||
|
|
|
||
|
|
switch (ident)
|
||
|
|
{
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static int8_t via3_process_dw(uint8_t *data, uint32_t ident, uint8_t desKeyIndex)
|
||
|
|
{
|
||
|
|
uint8_t i;
|
||
|
|
uint8_t tmp[8], T1Key[300], P1Key[8], KeyDes[16], XorKey[8];
|
||
|
|
uint32_t ks1[32], ks2[32];
|
||
|
|
|
||
|
|
if (!get_key(T1Key, ident, 'T', 1, 300, 1))
|
||
|
|
{
|
||
|
|
return 2;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!get_key(P1Key, ident, 'P', 1, 8, 1))
|
||
|
|
{
|
||
|
|
return 2;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!get_key(KeyDes, ident, '0', desKeyIndex, 16, 1))
|
||
|
|
{
|
||
|
|
return 2;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!get_key(XorKey, ident, 'X', 1, 8, 1))
|
||
|
|
{
|
||
|
|
return 2;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; i < 4; i++)
|
||
|
|
{
|
||
|
|
tmp[i] = data[i + 4];
|
||
|
|
}
|
||
|
|
|
||
|
|
via3_fct1(tmp, ident, XorKey, T1Key);
|
||
|
|
|
||
|
|
for (i = 0; i < 4; i++)
|
||
|
|
{
|
||
|
|
tmp[i] = data[i] ^ tmp[i + 4];
|
||
|
|
}
|
||
|
|
|
||
|
|
via3_fct2(tmp, ident, XorKey, T1Key);
|
||
|
|
|
||
|
|
for (i = 0; i < 4; i++)
|
||
|
|
{
|
||
|
|
tmp[i] ^= XorKey[i + 4];
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; i < 4; i++)
|
||
|
|
{
|
||
|
|
data[i] = data[i + 4] ^ tmp[i + 4];
|
||
|
|
data[i + 4] = tmp[i];
|
||
|
|
}
|
||
|
|
|
||
|
|
des_set_key(KeyDes, ks1);
|
||
|
|
des_set_key(KeyDes + 8, ks2);
|
||
|
|
|
||
|
|
des(data, ks1, 0);
|
||
|
|
des(data, ks2, 1);
|
||
|
|
des(data, ks1, 0);
|
||
|
|
|
||
|
|
for (i = 0; i < 4; i++)
|
||
|
|
{
|
||
|
|
tmp[i] = data[i + 4];
|
||
|
|
}
|
||
|
|
|
||
|
|
via3_fct2(tmp, ident, XorKey, T1Key);
|
||
|
|
|
||
|
|
for (i = 0; i < 4; i++)
|
||
|
|
{
|
||
|
|
tmp[i] = data[i] ^ tmp[i + 4];
|
||
|
|
}
|
||
|
|
|
||
|
|
via3_fct1(tmp, ident, XorKey, T1Key);
|
||
|
|
|
||
|
|
for (i = 0; i < 4; i++)
|
||
|
|
{
|
||
|
|
tmp[i] ^= XorKey[i];
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; i < 4; i++)
|
||
|
|
{
|
||
|
|
data[i] = data[i + 4] ^ tmp[i + 4];
|
||
|
|
data[i + 4] = tmp[i];
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void via3_final_mix(uint8_t *dw)
|
||
|
|
{
|
||
|
|
uint8_t tmp[4];
|
||
|
|
|
||
|
|
memcpy(tmp, dw, 4);
|
||
|
|
memcpy(dw, dw + 4, 4);
|
||
|
|
memcpy(dw + 4, tmp, 4);
|
||
|
|
|
||
|
|
memcpy(tmp, dw + 8, 4);
|
||
|
|
memcpy(dw + 8, dw + 12, 4);
|
||
|
|
memcpy(dw + 12, tmp, 4);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int8_t via3_decrypt(uint8_t *source, uint8_t *dw, uint32_t ident, uint8_t desKeyIndex,
|
||
|
|
uint8_t aesKeyIndex, uint8_t aesMode, int8_t doFinalMix)
|
||
|
|
{
|
||
|
|
int8_t aesAfterCore = 0, needsAES = (aesKeyIndex != 0xFF);
|
||
|
|
int32_t i, j;
|
||
|
|
|
||
|
|
uint8_t tmpData[8], C1[8];
|
||
|
|
uint8_t *pXorVector;
|
||
|
|
|
||
|
|
char aesKey[16];
|
||
|
|
|
||
|
|
if (ident == 0)
|
||
|
|
{
|
||
|
|
return EMU_CORRUPT_DATA;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!get_key(C1, ident, 'C', 1, 8, 1))
|
||
|
|
{
|
||
|
|
return EMU_KEY_NOT_FOUND;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (needsAES && !get_key((uint8_t *)aesKey, ident, 'E', aesKeyIndex, 16, 1))
|
||
|
|
{
|
||
|
|
return EMU_KEY_NOT_FOUND;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (aesMode == 0x0D || aesMode == 0x11 || aesMode == 0x15)
|
||
|
|
{
|
||
|
|
aesAfterCore = 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (needsAES && !aesAfterCore)
|
||
|
|
{
|
||
|
|
if (aesMode == 0x0F)
|
||
|
|
{
|
||
|
|
hdSurEncPhase1_D2_0F_11(source);
|
||
|
|
hdSurEncPhase2_D2_0F_11(source);
|
||
|
|
}
|
||
|
|
else if (aesMode == 0x13)
|
||
|
|
{
|
||
|
|
hdSurEncPhase1_D2_13_15(source);
|
||
|
|
}
|
||
|
|
|
||
|
|
struct aes_keys aes;
|
||
|
|
aes_set_key(&aes, aesKey);
|
||
|
|
aes_decrypt(&aes, source, 16);
|
||
|
|
|
||
|
|
if (aesMode == 0x0F)
|
||
|
|
{
|
||
|
|
hdSurEncPhase1_D2_0F_11(source);
|
||
|
|
}
|
||
|
|
else if (aesMode == 0x13)
|
||
|
|
{
|
||
|
|
hdSurEncPhase2_D2_13_15(source);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; i < 2; i++)
|
||
|
|
{
|
||
|
|
memcpy(tmpData, source + i * 8, 8);
|
||
|
|
via3_process_dw(tmpData, ident, desKeyIndex);
|
||
|
|
|
||
|
|
if (i != 0)
|
||
|
|
{
|
||
|
|
pXorVector = source;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
pXorVector = &C1[0];
|
||
|
|
}
|
||
|
|
|
||
|
|
for (j = 0; j < 8; j++)
|
||
|
|
{
|
||
|
|
dw[i * 8 + j] = tmpData[j] ^ pXorVector[j];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (needsAES && aesAfterCore)
|
||
|
|
{
|
||
|
|
if (aesMode == 0x11)
|
||
|
|
{
|
||
|
|
hdSurEncPhase1_D2_0F_11(dw);
|
||
|
|
hdSurEncPhase2_D2_0F_11(dw);
|
||
|
|
}
|
||
|
|
else if (aesMode == 0x15)
|
||
|
|
{
|
||
|
|
hdSurEncPhase1_D2_13_15(dw);
|
||
|
|
}
|
||
|
|
|
||
|
|
struct aes_keys aes;
|
||
|
|
aes_set_key(&aes, aesKey);
|
||
|
|
aes_decrypt(&aes, dw, 16);
|
||
|
|
|
||
|
|
if (aesMode == 0x11)
|
||
|
|
{
|
||
|
|
hdSurEncPhase1_D2_0F_11(dw);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (aesMode == 0x15)
|
||
|
|
{
|
||
|
|
hdSurEncPhase2_D2_13_15(dw);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ident == 0x030B00)
|
||
|
|
{
|
||
|
|
if (doFinalMix)
|
||
|
|
{
|
||
|
|
via3_final_mix(dw);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!is_valid_dcw(dw) || !is_valid_dcw(dw + 8))
|
||
|
|
{
|
||
|
|
return EMU_CHECKSUM_ERROR;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return EMU_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
int8_t viaccess_ecm(uint8_t *ecm, uint8_t *dw)
|
||
|
|
{
|
||
|
|
int8_t doFinalMix = 0;
|
||
|
|
|
||
|
|
uint8_t nanoCmd = 0, nanoLen = 0, version = 0, providerKeyLen = 0;
|
||
|
|
uint8_t desKeyIndex = 0, aesMode = 0, aesKeyIndex = 0xFF;
|
||
|
|
uint16_t i = 0, keySelectPos = 0, ecmLen = SCT_LEN(ecm);
|
||
|
|
uint32_t currentIdent = 0;
|
||
|
|
|
||
|
|
for (i = 4; i + 2 < ecmLen; )
|
||
|
|
{
|
||
|
|
nanoCmd = ecm[i++];
|
||
|
|
nanoLen = ecm[i++];
|
||
|
|
|
||
|
|
if (i + nanoLen > ecmLen)
|
||
|
|
{
|
||
|
|
return EMU_NOT_SUPPORTED;
|
||
|
|
}
|
||
|
|
|
||
|
|
switch (nanoCmd)
|
||
|
|
{
|
||
|
|
case 0x40:
|
||
|
|
if (nanoLen < 0x03)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
version = ecm[i];
|
||
|
|
if (nanoLen == 3)
|
||
|
|
{
|
||
|
|
currentIdent = ((ecm[i] << 16) | (ecm[i + 1] << 8)) | (ecm[i + 2] & 0xF0);
|
||
|
|
desKeyIndex = ecm[i + 2] & 0x0F;
|
||
|
|
keySelectPos = i + 3;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
currentIdent = (ecm[i] << 16) | (ecm[i + 1] << 8) | ((ecm[i + 2] >> 4) & 0x0F);
|
||
|
|
desKeyIndex = ecm[i + 3];
|
||
|
|
keySelectPos = i + 4;
|
||
|
|
}
|
||
|
|
providerKeyLen = nanoLen;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 0x90:
|
||
|
|
if (nanoLen < 0x03)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
version = ecm[i];
|
||
|
|
currentIdent = ((ecm[i] << 16) | (ecm[i + 1] << 8)) | (ecm[i + 2] & 0xF0);
|
||
|
|
desKeyIndex = ecm[i + 2] & 0x0F;
|
||
|
|
keySelectPos = i + 4;
|
||
|
|
if ((version == 3) && (nanoLen > 3))
|
||
|
|
{
|
||
|
|
desKeyIndex = ecm[i + (nanoLen - 4)] & 0x0F;
|
||
|
|
}
|
||
|
|
providerKeyLen = nanoLen;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 0x80:
|
||
|
|
nanoLen = 0;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 0xD2:
|
||
|
|
if (nanoLen < 0x02)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
aesMode = ecm[i];
|
||
|
|
aesKeyIndex = ecm[i + 1];
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 0xDD:
|
||
|
|
nanoLen = 0;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 0xEA:
|
||
|
|
if (nanoLen < 0x10)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (version < 2)
|
||
|
|
{
|
||
|
|
return via1_decrypt(ecm, dw, currentIdent, desKeyIndex);
|
||
|
|
}
|
||
|
|
else if (version == 2)
|
||
|
|
{
|
||
|
|
return via26_decrypt(ecm + i, dw, currentIdent, desKeyIndex);
|
||
|
|
}
|
||
|
|
else if (version == 3)
|
||
|
|
{
|
||
|
|
doFinalMix = 0;
|
||
|
|
if (currentIdent == 0x030B00 && providerKeyLen > 3)
|
||
|
|
{
|
||
|
|
if (keySelectPos + 2 >= ecmLen)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (ecm[keySelectPos] == 0x05 && ecm[keySelectPos + 1] == 0x67 &&
|
||
|
|
(ecm[keySelectPos + 2] == 0x00 || ecm[keySelectPos + 2] == 0x01))
|
||
|
|
{
|
||
|
|
if (ecm[keySelectPos + 2] == 0x01)
|
||
|
|
{
|
||
|
|
doFinalMix = 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return via3_decrypt(ecm + i, dw, currentIdent, desKeyIndex, aesKeyIndex, aesMode, doFinalMix);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
i += nanoLen;
|
||
|
|
}
|
||
|
|
|
||
|
|
return EMU_NOT_SUPPORTED;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Viaccess EMM EMU
|
||
|
|
|
||
|
|
int8_t viaccess_emm(uint8_t *emm, uint32_t *keysAdded)
|
||
|
|
{
|
||
|
|
uint8_t nanoCmd = 0, subNanoCmd = 0, *tmp;
|
||
|
|
uint8_t ecmKeyCount = 0, emmKeyIndex = 0, aesMode = 0x0D;
|
||
|
|
uint8_t nanoLen = 0, subNanoLen = 0, haveEmmXorKey = 0, haveNewD0 = 0;
|
||
|
|
uint8_t ecmKeys[6][16], keyD0[2], emmKey[16], emmXorKey[16], provName[17];
|
||
|
|
|
||
|
|
uint16_t i = 0, j = 0, k = 0, emmLen = SCT_LEN(emm);
|
||
|
|
uint32_t ui1, ui2, ui3, ecmKeyIndex[6], provider = 0, ecmProvider = 0;
|
||
|
|
|
||
|
|
char keyName[EMU_MAX_CHAR_KEYNAME], keyValue[36];
|
||
|
|
struct aes_keys aes;
|
||
|
|
|
||
|
|
memset(keyD0, 0, 2);
|
||
|
|
memset(ecmKeyIndex, 0, sizeof(uint32_t) * 6);
|
||
|
|
|
||
|
|
for (i = 3; i + 2 < emmLen; )
|
||
|
|
{
|
||
|
|
nanoCmd = emm[i++];
|
||
|
|
nanoLen = emm[i++];
|
||
|
|
|
||
|
|
if (i + nanoLen > emmLen)
|
||
|
|
{
|
||
|
|
return EMU_NOT_SUPPORTED;
|
||
|
|
}
|
||
|
|
|
||
|
|
switch (nanoCmd)
|
||
|
|
{
|
||
|
|
case 0x90:
|
||
|
|
{
|
||
|
|
if (nanoLen < 3)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
ui1 = emm[i + 2];
|
||
|
|
ui2 = emm[i + 1];
|
||
|
|
ui3 = emm[i];
|
||
|
|
provider = (ui1 | (ui2 << 8) | (ui3 << 16));
|
||
|
|
|
||
|
|
if (provider == 0x00D00040)
|
||
|
|
{
|
||
|
|
ecmProvider = 0x030B00;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
return EMU_NOT_SUPPORTED;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
case 0xD2:
|
||
|
|
{
|
||
|
|
if (nanoLen < 2)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
emmKeyIndex = emm[i + 1];
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
case 0x41:
|
||
|
|
{
|
||
|
|
if (nanoLen < 1)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!get_key(emmKey, provider, 'M', emmKeyIndex, 16, 1))
|
||
|
|
{
|
||
|
|
return EMU_KEY_NOT_FOUND;
|
||
|
|
}
|
||
|
|
|
||
|
|
memset(provName, 0, 17);
|
||
|
|
memset(emmXorKey, 0, 16);
|
||
|
|
|
||
|
|
k = nanoLen < 16 ? nanoLen : 16;
|
||
|
|
|
||
|
|
memcpy(provName, &emm[i], k);
|
||
|
|
aes_set_key(&aes, (char *)emmKey);
|
||
|
|
aes_decrypt(&aes, emmXorKey, 16);
|
||
|
|
|
||
|
|
for (j = 0; j < 16; j++)
|
||
|
|
{
|
||
|
|
provName[j] ^= emmXorKey[j];
|
||
|
|
}
|
||
|
|
provName[k] = 0;
|
||
|
|
|
||
|
|
if (strcmp((char *)provName, "TNTSAT") != 0 &&
|
||
|
|
strcmp((char *)provName, "TNTSATPRO") != 0 &&
|
||
|
|
strcmp((char *)provName, "CSAT V") != 0)
|
||
|
|
{
|
||
|
|
return EMU_NOT_SUPPORTED;
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
case 0xBA:
|
||
|
|
{
|
||
|
|
if (nanoLen < 2)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
get_key(keyD0, ecmProvider, 'D', 0, 2, 0);
|
||
|
|
|
||
|
|
ui1 = (emm[i] << 8) | emm[i + 1];
|
||
|
|
|
||
|
|
if( (uint32_t)((keyD0[0] << 8) | keyD0[1]) < ui1 || (keyD0[0] == 0x00 && keyD0[1] == 0x00))
|
||
|
|
{
|
||
|
|
keyD0[0] = emm[i];
|
||
|
|
keyD0[1] = emm[i + 1];
|
||
|
|
haveNewD0 = 1;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return EMU_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
case 0xBC:
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
case 0x43:
|
||
|
|
{
|
||
|
|
if (nanoLen < 16)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
memcpy(emmXorKey, &emm[i], 16);
|
||
|
|
haveEmmXorKey = 1;
|
||
|
|
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
case 0x44:
|
||
|
|
{
|
||
|
|
if (nanoLen < 3)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!haveEmmXorKey)
|
||
|
|
{
|
||
|
|
memset(emmXorKey, 0, 16);
|
||
|
|
}
|
||
|
|
|
||
|
|
tmp = (uint8_t *)malloc(((nanoLen / 16) + 1) * 16 * sizeof(uint8_t));
|
||
|
|
if (tmp == NULL)
|
||
|
|
{
|
||
|
|
return EMU_OUT_OF_MEMORY;
|
||
|
|
}
|
||
|
|
|
||
|
|
memcpy(tmp, &emm[i], nanoLen);
|
||
|
|
aes_set_key(&aes, (char *)emmKey);
|
||
|
|
|
||
|
|
for (j = 0; j < nanoLen; j += 16)
|
||
|
|
{
|
||
|
|
aes_decrypt(&aes, emmXorKey, 16);
|
||
|
|
|
||
|
|
for (k = 0; k < 16; k++)
|
||
|
|
{
|
||
|
|
tmp[j + k] ^= emmXorKey[k];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
memcpy(&emm[i - 2], tmp, nanoLen);
|
||
|
|
free(tmp);
|
||
|
|
nanoLen = 0;
|
||
|
|
i -= 2;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
case 0x68:
|
||
|
|
{
|
||
|
|
if (ecmKeyCount > 5)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (j = i; j + 2 < i + nanoLen; )
|
||
|
|
{
|
||
|
|
subNanoCmd = emm[j++];
|
||
|
|
subNanoLen = emm[j++];
|
||
|
|
|
||
|
|
if (j + subNanoLen > i + nanoLen)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
switch (subNanoCmd)
|
||
|
|
{
|
||
|
|
case 0xD2:
|
||
|
|
{
|
||
|
|
if (nanoLen < 2)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
aesMode = emm[j];
|
||
|
|
emmKeyIndex = emm[j + 1];
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
case 0x01:
|
||
|
|
{
|
||
|
|
if(nanoLen < 17)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
ecmKeyIndex[ecmKeyCount] = emm[j];
|
||
|
|
memcpy(&ecmKeys[ecmKeyCount], &emm[j + 1], 16);
|
||
|
|
|
||
|
|
if (!get_key(emmKey, provider, 'M', emmKeyIndex, 16, 1))
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (aesMode == 0x0F || aesMode == 0x11)
|
||
|
|
{
|
||
|
|
hdSurEncPhase1_D2_0F_11(ecmKeys[ecmKeyCount]);
|
||
|
|
hdSurEncPhase2_D2_0F_11(ecmKeys[ecmKeyCount]);
|
||
|
|
}
|
||
|
|
else if (aesMode == 0x13 || aesMode == 0x15)
|
||
|
|
{
|
||
|
|
hdSurEncPhase1_D2_13_15(ecmKeys[ecmKeyCount]);
|
||
|
|
}
|
||
|
|
|
||
|
|
aes_set_key(&aes, (char *)emmKey);
|
||
|
|
aes_decrypt(&aes, ecmKeys[ecmKeyCount], 16);
|
||
|
|
|
||
|
|
if (aesMode == 0x0F || aesMode == 0x11)
|
||
|
|
{
|
||
|
|
hdSurEncPhase1_D2_0F_11(ecmKeys[ecmKeyCount]);
|
||
|
|
}
|
||
|
|
else if (aesMode == 0x13 || aesMode == 0x15)
|
||
|
|
{
|
||
|
|
hdSurEncPhase2_D2_13_15(ecmKeys[ecmKeyCount]);
|
||
|
|
}
|
||
|
|
|
||
|
|
ecmKeyCount++;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
j += subNanoLen;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
case 0xF0:
|
||
|
|
{
|
||
|
|
if (nanoLen != 4)
|
||
|
|
{
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
ui1 = ((emm[i + 2] << 8) | (emm[i + 1] << 16) | (emm[i] << 24) | emm[i + 3]);
|
||
|
|
|
||
|
|
if (ccitt32_crc(emm + 3, emmLen - 11) != ui1)
|
||
|
|
{
|
||
|
|
return EMU_CHECKSUM_ERROR;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (haveNewD0)
|
||
|
|
{
|
||
|
|
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
|
||
|
|
emu_set_key('V', ecmProvider, "D0", keyD0, 2, 1, NULL, NULL);
|
||
|
|
|
||
|
|
for (j = 0; j < ecmKeyCount; j++)
|
||
|
|
{
|
||
|
|
snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "E%X", ecmKeyIndex[j]);
|
||
|
|
emu_set_key('V', ecmProvider, keyName, ecmKeys[j], 16, 1, NULL, NULL);
|
||
|
|
|
||
|
|
(*keysAdded)++;
|
||
|
|
cs_hexdump(0, ecmKeys[j], 16, keyValue, sizeof(keyValue));
|
||
|
|
cs_log("Key found in EMM: V %06X %s %s", ecmProvider, keyName, keyValue);
|
||
|
|
}
|
||
|
|
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
i += nanoLen;
|
||
|
|
}
|
||
|
|
|
||
|
|
return EMU_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif // WITH_EMU
|