603 lines
12 KiB
C
Executable File
603 lines
12 KiB
C
Executable File
#define MODULE_LOG_PREFIX "emu"
|
|
|
|
#include "globals.h"
|
|
|
|
#ifdef WITH_EMU
|
|
|
|
#include "cscrypt/des.h"
|
|
#include "module-emulator-osemu.h"
|
|
#include "oscam-string.h"
|
|
|
|
static inline void xxor(uint8_t *data, int32_t len, const uint8_t *v1, const uint8_t *v2)
|
|
{
|
|
uint32_t i;
|
|
|
|
switch (len)
|
|
{
|
|
case 16:
|
|
for (i = 0; i < 16; ++i)
|
|
{
|
|
data[i] = v1[i] ^ v2[i];
|
|
}
|
|
break;
|
|
|
|
case 8:
|
|
for (i = 0; i < 8; ++i)
|
|
{
|
|
data[i] = v1[i] ^ v2[i];
|
|
}
|
|
break;
|
|
|
|
case 4:
|
|
for (i = 0; i < 4; ++i)
|
|
{
|
|
data[i] = v1[i] ^ v2[i];
|
|
}
|
|
break;
|
|
|
|
default:
|
|
while (len--)
|
|
{
|
|
*data++ = *v1++ ^ *v2++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Irdeto EMU
|
|
static int8_t get_key(uint8_t *buf, uint32_t ident, char keyName, uint32_t keyIndex,
|
|
uint8_t isCriticalKey, uint32_t *keyRef)
|
|
{
|
|
char keyStr[EMU_MAX_CHAR_KEYNAME];
|
|
|
|
if (*keyRef > 0xFF)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
snprintf(keyStr, EMU_MAX_CHAR_KEYNAME, "%c%X", keyName, keyIndex);
|
|
|
|
if (emu_find_key('I', ident, 0, keyStr, buf, 16, *keyRef > 0 ? 0 : isCriticalKey, *keyRef, 0, NULL))
|
|
{
|
|
(*keyRef)++;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void irdeto2_encrypt(uint8_t *data, const uint8_t *seed, const uint8_t *key, int32_t len)
|
|
{
|
|
int32_t i;
|
|
const uint8_t *tmp = seed;
|
|
uint32_t ks1[32], ks2[32];
|
|
|
|
des_set_key(key, ks1);
|
|
des_set_key(key + 8, ks2);
|
|
|
|
len &= ~7;
|
|
|
|
for (i = 0; i + 7 < len; i += 8)
|
|
{
|
|
xxor(&data[i], 8, &data[i], tmp);
|
|
tmp = &data[i];
|
|
des(&data[i], ks1, 1);
|
|
des(&data[i], ks2, 0);
|
|
des(&data[i], ks1, 1);
|
|
}
|
|
}
|
|
|
|
static void irdeto2_decrypt(uint8_t *data, const uint8_t *seed, const uint8_t *key, int32_t len)
|
|
{
|
|
int32_t i, n = 0;
|
|
uint8_t buf[2][8];
|
|
uint32_t ks1[32], ks2[32];
|
|
|
|
des_set_key(key, ks1);
|
|
des_set_key(key + 8, ks2);
|
|
|
|
len &= ~7;
|
|
|
|
memcpy(buf[n], seed, 8);
|
|
|
|
for (i = 0; i + 7 < len; i += 8, data += 8, n ^= 1)
|
|
{
|
|
memcpy(buf[1 - n], data, 8);
|
|
des(data, ks1, 0);
|
|
des(data, ks2, 1);
|
|
des(data, ks1, 0);
|
|
xxor(data, 8, data, buf[n]);
|
|
}
|
|
}
|
|
|
|
static int8_t calculate_hash(const uint8_t *key, const uint8_t *iv, const uint8_t *data, int32_t len)
|
|
{
|
|
int32_t l, y;
|
|
uint8_t cbuff[32];
|
|
uint32_t ks1[32], ks2[32];
|
|
|
|
des_set_key(key, ks1);
|
|
des_set_key(key + 8, ks2);
|
|
|
|
memset(cbuff, 0, sizeof(cbuff));
|
|
|
|
len -= 8;
|
|
|
|
for (y = 0; y < len; y += 8)
|
|
{
|
|
if (y < len - 8)
|
|
{
|
|
xxor(cbuff, 8, cbuff, &data[y]);
|
|
}
|
|
else
|
|
{
|
|
l = len - y;
|
|
xxor(cbuff, l, cbuff, &data[y]);
|
|
xxor(cbuff + l, 8 - l, cbuff + l, iv + 8);
|
|
}
|
|
|
|
des(cbuff, ks1, 1);
|
|
des(cbuff, ks2, 0);
|
|
des(cbuff, ks1, 1);
|
|
}
|
|
|
|
return memcmp(cbuff, &data[len], 8) == 0;
|
|
}
|
|
|
|
int8_t irdeto2_ecm(uint16_t caid, uint8_t *oecm, uint8_t *dw)
|
|
{
|
|
uint8_t keyNr = 0, length, end, key[16], okeySeed[16], keySeed[16], keyIV[16], tmp[16];
|
|
uint8_t ecmCopy[EMU_MAX_ECM_LEN], *ecm = oecm;
|
|
uint16_t ecmLen = SCT_LEN(ecm);
|
|
uint32_t key0Ref, keySeedRef, keyIVRef, ident, i, j, l;
|
|
|
|
if (ecmLen < 12)
|
|
{
|
|
return EMU_NOT_SUPPORTED;
|
|
}
|
|
|
|
length = ecm[11];
|
|
keyNr = ecm[9];
|
|
ident = ecm[8] | caid << 8;
|
|
|
|
if (ecmLen < length + 12)
|
|
{
|
|
return EMU_NOT_SUPPORTED;
|
|
}
|
|
|
|
key0Ref = 0;
|
|
|
|
while (get_key(key, ident, '0', keyNr, 1, &key0Ref))
|
|
{
|
|
keySeedRef = 0;
|
|
|
|
while (get_key(okeySeed, ident, 'M', 1, 1, &keySeedRef))
|
|
{
|
|
keyIVRef = 0;
|
|
|
|
while (get_key(keyIV, ident, 'M', 2, 1, &keyIVRef))
|
|
{
|
|
memcpy(keySeed, okeySeed, 16);
|
|
memcpy(ecmCopy, oecm, ecmLen);
|
|
|
|
ecm = ecmCopy;
|
|
memset(tmp, 0, 16);
|
|
irdeto2_encrypt(keySeed, tmp, key, 16);
|
|
|
|
ecm += 12;
|
|
irdeto2_decrypt(ecm, keyIV, keySeed, length);
|
|
|
|
i = (ecm[0] & 7) + 1;
|
|
end = length - 8 < 0 ? 0 : length - 8;
|
|
|
|
while (i < end)
|
|
{
|
|
l = ecm[i + 1] ? (ecm[i + 1] & 0x3F) + 2 : 1;
|
|
|
|
switch (ecm[i])
|
|
{
|
|
case 0x10:
|
|
case 0x50:
|
|
if (l == 0x13 && i <= length - 8 - l)
|
|
{
|
|
irdeto2_decrypt(&ecm[i + 3], keyIV, key, 16);
|
|
}
|
|
break;
|
|
|
|
case 0x78:
|
|
if (l == 0x14 && i <= length - 8 - l)
|
|
{
|
|
irdeto2_decrypt(&ecm[i + 4], keyIV, key, 16);
|
|
}
|
|
break;
|
|
}
|
|
i += l;
|
|
}
|
|
|
|
i = (ecm[0] & 7) + 1;
|
|
|
|
if (calculate_hash(keySeed, keyIV, ecm - 6, length + 6))
|
|
{
|
|
while (i < end)
|
|
{
|
|
l = ecm[i + 1] ? (ecm[i + 1] & 0x3F) + 2 : 1;
|
|
|
|
switch (ecm[i])
|
|
{
|
|
case 0x78:
|
|
{
|
|
if (l == 0x14 && i <= length - 8 - l)
|
|
{
|
|
memcpy(dw, &ecm[i + 4], 16);
|
|
|
|
for (j = 0; j < 16; j += 4) // fix dw checksum bytes
|
|
{
|
|
dw[j + 3] = (dw[j] + dw[j + 1] + dw[j + 2]) & 0xFF;
|
|
}
|
|
return EMU_OK;
|
|
}
|
|
}
|
|
}
|
|
i += l;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (keyIVRef == 0)
|
|
{
|
|
return EMU_KEY_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
if (keySeedRef == 0)
|
|
{
|
|
return EMU_KEY_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
if (key0Ref == 0)
|
|
{
|
|
return EMU_KEY_NOT_FOUND;
|
|
}
|
|
|
|
return EMU_NOT_SUPPORTED;
|
|
}
|
|
|
|
// Irdeto2 EMM EMU
|
|
static int8_t do_emm_type_op(uint32_t ident, uint8_t *emm, uint8_t *keySeed, uint8_t *keyIV, uint8_t *keyPMK,
|
|
uint16_t emmLen, uint8_t startOffset, uint8_t length, uint32_t *keysAdded)
|
|
{
|
|
uint8_t tmp[16];
|
|
uint32_t end, i, l;
|
|
char keyName[EMU_MAX_CHAR_KEYNAME], keyValue[36];
|
|
|
|
memset(tmp, 0, 16);
|
|
irdeto2_encrypt(keySeed, tmp, keyPMK, 16);
|
|
irdeto2_decrypt(&emm[startOffset], keyIV, keySeed, length);
|
|
|
|
i = 16;
|
|
end = startOffset + (length - 8 < 0 ? 0 : length - 8);
|
|
|
|
while (i < end)
|
|
{
|
|
l = emm[i + 1] ? (emm[i + 1] & 0x3F) + 2 : 1;
|
|
|
|
switch (emm[i])
|
|
{
|
|
case 0x10:
|
|
case 0x50:
|
|
if (l == 0x13 && i <= startOffset + length - 8 - l)
|
|
{
|
|
irdeto2_decrypt(&emm[i + 3], keyIV, keyPMK, 16);
|
|
}
|
|
break;
|
|
|
|
case 0x78:
|
|
if (l == 0x14 && i <= startOffset + length - 8 - l)
|
|
{
|
|
irdeto2_decrypt(&emm[i + 4], keyIV, keyPMK, 16);
|
|
}
|
|
break;
|
|
}
|
|
i += l;
|
|
}
|
|
|
|
memmove(emm + 6, emm + 7, emmLen - 7);
|
|
|
|
i = 15;
|
|
end = startOffset + (length - 9 < 0 ? 0 : length - 9);
|
|
|
|
if (calculate_hash(keySeed, keyIV, emm + 3, emmLen - 4))
|
|
{
|
|
while (i < end)
|
|
{
|
|
l = emm[i + 1] ? (emm[i + 1] & 0x3F) + 2 : 1;
|
|
|
|
switch (emm[i])
|
|
{
|
|
case 0x10:
|
|
case 0x50:
|
|
{
|
|
if (l == 0x13 && i <= startOffset + length - 9 - l)
|
|
{
|
|
snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "%02X", emm[i + 2] >> 2);
|
|
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
|
|
emu_set_key('I', ident, keyName, &emm[i + 3], 16, 1, NULL, NULL);
|
|
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
|
|
|
|
(*keysAdded)++;
|
|
cs_hexdump(0, &emm[i + 3], 16, keyValue, sizeof(keyValue));
|
|
cs_log("Key found in EMM: I %06X %s %s", ident, keyName, keyValue);
|
|
}
|
|
}
|
|
}
|
|
i += l;
|
|
}
|
|
|
|
if (*keysAdded > 0)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int8_t do_emm_type_pmk(uint32_t ident, uint8_t *emm, uint8_t *keySeed, uint8_t *keyIV, uint8_t *keyPMK,
|
|
uint16_t emmLen, uint8_t startOffset, uint8_t length, uint32_t *keysAdded)
|
|
{
|
|
uint32_t end, i, j, l;
|
|
char keyName[EMU_MAX_CHAR_KEYNAME], keyValue[36];
|
|
|
|
irdeto2_decrypt(&emm[startOffset], keyIV, keySeed, length);
|
|
|
|
i = 13;
|
|
end = startOffset + (length - 8 < 0 ? 0 : length - 8);
|
|
|
|
while (i < end)
|
|
{
|
|
l = emm[i + 1] ? (emm[i + 1] & 0x3F) + 2 : 1;
|
|
|
|
switch (emm[i])
|
|
{
|
|
case 0x10:
|
|
case 0x50:
|
|
if (l == 0x13 && i <= startOffset + length - 8 - l)
|
|
{
|
|
irdeto2_decrypt(&emm[i + 3], keyIV, keyPMK, 16);
|
|
}
|
|
break;
|
|
|
|
case 0x78:
|
|
if (l == 0x14 && i <= startOffset + length - 8 - l)
|
|
{
|
|
irdeto2_decrypt(&emm[i + 4], keyIV, keyPMK, 16);
|
|
}
|
|
break;
|
|
|
|
case 0x68:
|
|
if (l == 0x26 && i <= startOffset + length - 8 - l)
|
|
{
|
|
irdeto2_decrypt(&emm[i + 3], keyIV, keyPMK, 16 * 2);
|
|
}
|
|
break;
|
|
}
|
|
i += l;
|
|
}
|
|
|
|
memmove(emm + 7, emm + 9, emmLen - 9);
|
|
|
|
i = 11;
|
|
end = startOffset + (length - 10 < 0 ? 0 : length - 10);
|
|
|
|
if (calculate_hash(keySeed, keyIV, emm + 3, emmLen - 5))
|
|
{
|
|
while (i < end)
|
|
{
|
|
l = emm[i + 1] ? (emm[i + 1] & 0x3F) + 2 : 1;
|
|
|
|
switch (emm[i])
|
|
{
|
|
case 0x68:
|
|
{
|
|
if (l == 0x26 && i <= startOffset + length - 10 - l)
|
|
{
|
|
for (j = 0; j < 2; j++)
|
|
{
|
|
snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "M%01X", 3 + j);
|
|
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
|
|
emu_set_key('I', ident, keyName, &emm[i + 3 + j * 16], 16, 1, NULL, NULL);
|
|
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
|
|
|
|
(*keysAdded)++;
|
|
cs_hexdump(0, &emm[i + 3 + j * 16], 16, keyValue, sizeof(keyValue));
|
|
cs_log("Key found in EMM: I %06X %s %s", ident, keyName, keyValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
i += l;
|
|
}
|
|
|
|
if (*keysAdded > 0)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static const uint8_t fausto_xor[16] =
|
|
{
|
|
0x22, 0x58, 0xBD, 0x85, 0x2E, 0x8E, 0x52, 0x80,
|
|
0xA3, 0x79, 0x98, 0x69, 0x68, 0xE2, 0xD8, 0x4D
|
|
};
|
|
|
|
int8_t irdeto2_emm(uint16_t caid, uint8_t *oemm, uint32_t *keysAdded)
|
|
{
|
|
uint8_t length, okeySeed[16], keySeed[16], keyIV[16], keyPMK[16], startOffset, emmType;
|
|
uint8_t emmCopy[EMU_MAX_EMM_LEN], *emm = oemm;
|
|
uint16_t emmLen = SCT_LEN(emm);
|
|
uint32_t ident, keySeedRef, keyIVRef, keyPMK0Ref, keyPMK1Ref, keyPMK0ERef, keyPMK1ERef;
|
|
|
|
if (emmLen < 11)
|
|
{
|
|
return EMU_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (emm[3] == 0xC3 || emm[3] == 0xCB)
|
|
{
|
|
emmType = 2;
|
|
startOffset = 11;
|
|
}
|
|
else
|
|
{
|
|
emmType = 1;
|
|
startOffset = 10;
|
|
}
|
|
|
|
ident = emm[startOffset - 2] | caid << 8;
|
|
length = emm[startOffset - 1];
|
|
|
|
if (emmLen < length + startOffset)
|
|
{
|
|
return EMU_NOT_SUPPORTED;
|
|
}
|
|
|
|
keySeedRef = 0;
|
|
|
|
while (get_key(okeySeed, ident, 'M', emmType == 1 ? 0 : 0xA, 1, &keySeedRef))
|
|
{
|
|
keyIVRef = 0;
|
|
|
|
while (get_key(keyIV, ident, 'M', 2, 1, &keyIVRef))
|
|
{
|
|
keyPMK0Ref = 0;
|
|
keyPMK1Ref = 0;
|
|
keyPMK0ERef = 0;
|
|
keyPMK1ERef = 0;
|
|
|
|
while (get_key(keyPMK, ident, 'M', emmType == 1 ? 3 : 0xB, 1, &keyPMK0Ref))
|
|
{
|
|
memcpy(keySeed, okeySeed, 16);
|
|
memcpy(emmCopy, oemm, emmLen);
|
|
emm = emmCopy;
|
|
|
|
if (emmType == 1)
|
|
{
|
|
if (do_emm_type_op(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0)
|
|
{
|
|
return EMU_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (do_emm_type_pmk(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0)
|
|
{
|
|
return EMU_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (emmType == 1)
|
|
{
|
|
while (get_key(keyPMK, ident, 'M', 4, 1, &keyPMK1Ref))
|
|
{
|
|
memcpy(keySeed, okeySeed, 16);
|
|
memcpy(emmCopy, oemm, emmLen);
|
|
emm = emmCopy;
|
|
|
|
if (do_emm_type_op(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0)
|
|
{
|
|
return EMU_OK;
|
|
}
|
|
}
|
|
|
|
while (get_key(keyPMK, ident, 'M', 5, 1, &keyPMK0ERef))
|
|
{
|
|
xxor(keyPMK, 16, keyPMK, fausto_xor);
|
|
memcpy(keySeed, okeySeed, 16);
|
|
memcpy(emmCopy, oemm, emmLen);
|
|
emm = emmCopy;
|
|
|
|
if (do_emm_type_op(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0)
|
|
{
|
|
return EMU_OK;
|
|
}
|
|
}
|
|
|
|
while (get_key(keyPMK, ident, 'M', 6, 1, &keyPMK1ERef))
|
|
{
|
|
xxor(keyPMK, 16, keyPMK, fausto_xor);
|
|
memcpy(keySeed, okeySeed, 16);
|
|
memcpy(emmCopy, oemm, emmLen);
|
|
emm = emmCopy;
|
|
|
|
if (do_emm_type_op(ident, emm, keySeed, keyIV, keyPMK, emmLen, startOffset, length, keysAdded) == 0)
|
|
{
|
|
return EMU_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (keyPMK0Ref == 0 && keyPMK1Ref == 0 && keyPMK0ERef == 0 && keyPMK1ERef == 0)
|
|
{
|
|
return EMU_KEY_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
if (keyIVRef == 0)
|
|
{
|
|
return EMU_KEY_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
if (keySeedRef == 0)
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
return EMU_NOT_SUPPORTED;
|
|
}
|
|
|
|
int8_t irdeto2_get_hexserial(uint16_t caid, uint8_t *hexserial)
|
|
{
|
|
uint32_t i, len;
|
|
KeyDataContainer *KeyDB;
|
|
KeyData *tmpKeyData;
|
|
|
|
KeyDB = emu_get_key_container('I');
|
|
|
|
if (KeyDB == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < KeyDB->keyCount; i++)
|
|
{
|
|
if (KeyDB->EmuKeys[i].provider >> 8 != caid)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (strcmp(KeyDB->EmuKeys[i].keyName, "MC"))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
tmpKeyData = &KeyDB->EmuKeys[i];
|
|
len = tmpKeyData->keyLength;
|
|
|
|
if (len > 3)
|
|
{ len = 3; }
|
|
|
|
memcpy(hexserial + (3 - len), tmpKeyData->key, len);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif // WITH_EMU
|