oscam-2.26.01-11942-802-wit.../module-emulator-director.c

645 lines
16 KiB
C
Raw Normal View History

#define MODULE_LOG_PREFIX "emu"
#include "globals.h"
#ifdef WITH_EMU
#include "cscrypt/des.h"
#include "module-emulator-osemu.h"
#include "oscam-aes.h"
#include "oscam-string.h"
/*************************************************************************************************/
// Shared functions
static uint16_t calculate_checksum(uint8_t *data, uint8_t length)
{
/*
* ECM and EMM checksum calculation
* 1. Combine data in 2 byte groups
* 2. Add them together
* 3. Multiply result by itself (power of 7)
* 4. XOR with fixed value 0x17E3
*/
uint8_t i;
uint16_t checksum = 0;
for (i = 0; i < length; i += 2)
{
checksum += (data[i] << 8) | data[i + 1];
}
checksum = checksum * checksum * checksum * checksum * checksum * checksum * checksum;
checksum ^= 0x17E3;
return checksum;
}
static inline int8_t get_key(uint32_t keyIndex, char *keyName, uint8_t *key, uint32_t keyLength)
{
/*
* keyIndex meaning for:
* ecm keys --> entitlementId
* emm keys --> aeskeyIndex
* aes keys --> keyIndex
*
* keyName meaning for:
* ecm keys --> "01"
* emm keys --> "MK" or "MK01"
* aes keys --> "AES"
*/
return emu_find_key('T', keyIndex, 0, keyName, key, keyLength, 1, 0, 0, NULL);
}
/*************************************************************************************************/
/*
* Director ECM emulator
* Supported versions: v4, v5, v6 (not working correctly)
*/
int8_t director_ecm(uint8_t *ecm, uint8_t *dw)
{
uint8_t nanoType, nanoLength;
uint8_t *nanoData;
uint32_t pos = 3;
uint32_t entitlementId;
uint32_t ks[32];
uint8_t ecmKey[8];
uint16_t ecmLen = SCT_LEN(ecm);
if (ecmLen < 5)
{
return EMU_NOT_SUPPORTED;
}
do
{
nanoType = ecm[pos];
nanoLength = ecm[pos + 1];
if (pos + 2 + nanoLength > ecmLen)
{
break;
}
nanoData = ecm + pos + 2;
// ECM validation
uint16_t payloadChecksum = (nanoData[nanoLength - 2] << 8) | nanoData[nanoLength - 1];
uint16_t calculatedChecksum = calculate_checksum(nanoData, nanoLength - 2);
if (calculatedChecksum != payloadChecksum)
{
cs_log_dbg(D_READER, "ECM checksum error (%.4X instead of %.4X)", calculatedChecksum, payloadChecksum);
return EMU_CHECKSUM_ERROR;
}
// End of ECM validation
switch (nanoType)
{
case 0xEC: // Director v6 (September 2017)
{
if (nanoLength != 0x28)
{
cs_log_dbg(D_READER, "WARNING: nanoType EC length (%d) != %d", nanoLength, 0x28);
break;
}
entitlementId = b2i(4, nanoData);
cs_log_dbg(D_READER, "INFO: Using entitlement id %.4X", entitlementId);
if (!get_key(entitlementId, "01", ecmKey, 8))
{
return EMU_KEY_NOT_FOUND;
}
// Step 1 - Decrypt DES CBC with ecmKey and iv = { 0 } (equal to nanoED)
uint8_t encryptedData[32] = { 0 };
memcpy(encryptedData, nanoData + 6, 32);
uint8_t iv[8] = { 0 };
des_cbc_decrypt(encryptedData, iv, ecmKey, 32);
uint8_t nanoMode = nanoData[5];
if ((nanoMode & 0x20) == 0) // Old algo
{
// Step 2 - Create CW (equal to nano ED)
dw[0] = encryptedData[0x05];
dw[1] = encryptedData[0x19];
dw[2] = encryptedData[0x1D];
dw[3] = (dw[0] + dw[1] + dw[2]) & 0xFF;
dw[4] = encryptedData[0x0B];
dw[5] = encryptedData[0x12];
dw[6] = encryptedData[0x1A];
dw[7] = (dw[4] + dw[5] + dw[6]) & 0xFF;
dw[8] = encryptedData[0x16];
dw[9] = encryptedData[0x03];
dw[10] = encryptedData[0x11];
dw[11] = (dw[8] + dw[9] + dw[10]) & 0xFF;
dw[12] = encryptedData[0x18];
dw[13] = encryptedData[0x10];
dw[14] = encryptedData[0x0E];
dw[15] = (dw[12] + dw[13] + dw[14]) & 0xFF;
return EMU_OK;
}
else // New algo (overencryption with AES)
{
// Step 2 - Prepare data for AES (it is like the creation of CW in nanoED but swapped each 8 bytes)
uint8_t dataEC[16] = { 0 };
dataEC[0] = encryptedData[0x02];
dataEC[1] = encryptedData[0x0E];
dataEC[2] = encryptedData[0x10];
dataEC[3] = encryptedData[0x18];
dataEC[4] = encryptedData[0x09];
dataEC[5] = encryptedData[0x11];
dataEC[6] = encryptedData[0x03];
dataEC[7] = encryptedData[0x16];
dataEC[8] = encryptedData[0x13];
dataEC[9] = encryptedData[0x1A];
dataEC[10] = encryptedData[0x12];
dataEC[11] = encryptedData[0x0B];
dataEC[12] = encryptedData[0x04];
dataEC[13] = encryptedData[0x1D];
dataEC[14] = encryptedData[0x19];
dataEC[15] = encryptedData[0x05];
// Step 3 - Decrypt AES CBC with new aesKey and iv 2EBD816A5E749A708AE45ADDD84333DE
uint8_t aesKeyIndex = nanoMode & 0x1F; // 32 possible AES keys
uint8_t aesKey[16] = { 0 };
char tmpBuffer[33];
cs_hexdump(0, aesKey, 16, tmpBuffer, sizeof(tmpBuffer));
cs_log_dbg(D_READER, "INFO: Using AES key index: %02X, value: %s", aesKeyIndex, tmpBuffer);
if (!get_key(aesKeyIndex, "AES", aesKey, 16))
{
return EMU_KEY_NOT_FOUND;
}
struct aes_keys aes;
aes_set_key(&aes, (char *)aesKey);
uint8_t ivAes[16] = { 0x2E, 0xBD, 0x81, 0x6A, 0x5E, 0x74, 0x9A, 0x70, 0x8A, 0xE4, 0x5A, 0xDD, 0xD8, 0x43, 0x33, 0xDE };
aes_cbc_decrypt(&aes, dataEC, 16, ivAes);
// Step 4 - Create CW (a simple swap)
uint8_t offset;
for (offset = 0; offset < 16; offset++)
{
dw[offset] = dataEC[15 - offset];
}
return EMU_OK;
}
}
case 0xED: // Director v5 (September 2016)
{
if (nanoLength != 0x26)
{
cs_log_dbg(D_READER, "WARNING: nanoType ED length (%d) != %d", nanoLength, 0x26);
break;
}
entitlementId = b2i(4, nanoData);
cs_log_dbg(D_READER, "INFO: Using entitlement id %.4X", entitlementId);
if (!get_key(entitlementId, "01", ecmKey, 8))
{
return EMU_KEY_NOT_FOUND;
}
uint8_t encryptedData[32] = { 0 };
memcpy(encryptedData, nanoData + 4, 32);
uint8_t iv[8] = { 0 };
des_cbc_decrypt(encryptedData, iv, ecmKey, 32);
dw[0] = encryptedData[0x05];
dw[1] = encryptedData[0x19];
dw[2] = encryptedData[0x1D];
dw[3] = (dw[0] + dw[1] + dw[2]) & 0xFF;
dw[4] = encryptedData[0x0B];
dw[5] = encryptedData[0x12];
dw[6] = encryptedData[0x1A];
dw[7] = (dw[4] + dw[5] + dw[6]) & 0xFF;
dw[8] = encryptedData[0x16];
dw[9] = encryptedData[0x03];
dw[10] = encryptedData[0x11];
dw[11] = (dw[8] + dw[9] + dw[10]) & 0xFF;
dw[12] = encryptedData[0x18];
dw[13] = encryptedData[0x10];
dw[14] = encryptedData[0x0E];
dw[15] = (dw[12] + dw[13] + dw[14]) & 0xFF;
return EMU_OK;
}
case 0xEE: // Director v4
{
if (nanoLength != 0x16)
{
cs_log_dbg(D_READER, "WARNING: nanoType EE length (%d) != %d", nanoLength, 0x16);
break;
}
entitlementId = b2i(4, nanoData);
cs_log_dbg(D_READER, "INFO: Using entitlement id %.4X", entitlementId);
if (!get_key(entitlementId, "01", ecmKey, 8))
{
return EMU_KEY_NOT_FOUND;
}
memcpy(dw, nanoData + 4 + 8, 8); // even
memcpy(dw + 8, nanoData + 4, 8); // odd
des_set_key(ecmKey, ks);
des(dw, ks, 0);
des(dw + 8, ks, 0);
dw[3] = (dw[0] + dw[1] + dw[2]) & 0xFF;
dw[7] = (dw[4] + dw[5] + dw[6]) & 0xFF;
dw[11] = (dw[8] + dw[9] + dw[10]) & 0xFF;
dw[15] = (dw[12] + dw[13] + dw[14]) & 0xFF;
return EMU_OK;
}
default:
cs_log_dbg(D_READER, "WARNING: nanoType %.2X not supported", nanoType);
return EMU_NOT_SUPPORTED;
}
pos += 2 + nanoLength;
} while (pos < ecmLen);
return EMU_NOT_SUPPORTED;
}
/*************************************************************************************************/
/*
* Director EMM emulator
* Supported versions: v4, v5, v6 (same as v5)
*/
static const uint8_t MixTable[] =
{
0x12, 0x78, 0x4B, 0x19, 0x13, 0x80, 0x2F, 0x84, 0x86, 0x4C, 0x09, 0x53, 0x15, 0x79, 0x6B, 0x49,
0x10, 0x4D, 0x33, 0x43, 0x18, 0x37, 0x83, 0x38, 0x82, 0x1B, 0x6E, 0x24, 0x2A, 0x85, 0x3C, 0x3D,
0x5A, 0x58, 0x55, 0x5D, 0x20, 0x41, 0x65, 0x51, 0x0C, 0x45, 0x63, 0x7F, 0x0F, 0x46, 0x21, 0x7C,
0x2C, 0x61, 0x7E, 0x0A, 0x42, 0x57, 0x35, 0x16, 0x87, 0x3B, 0x4F, 0x40, 0x34, 0x22, 0x26, 0x74,
0x32, 0x69, 0x44, 0x7A, 0x6A, 0x6D, 0x0D, 0x56, 0x23, 0x2B, 0x5C, 0x72, 0x76, 0x36, 0x28, 0x25,
0x2E, 0x52, 0x5B, 0x6C, 0x7D, 0x30, 0x0B, 0x5E, 0x47, 0x1F, 0x7B, 0x31, 0x3E, 0x11, 0x77, 0x1E,
0x60, 0x75, 0x54, 0x27, 0x50, 0x17, 0x70, 0x59, 0x1A, 0x2D, 0x4A, 0x67, 0x3A, 0x5F, 0x68, 0x08,
0x4E, 0x3F, 0x29, 0x6F, 0x81, 0x71, 0x39, 0x64, 0x48, 0x66, 0x73, 0x14, 0x0E, 0x1D, 0x62, 0x1C
};
/*
static void rotate_bytes(uint8_t *in, int8_t n)
{
if (n > 1)
{
uint8_t *e = in + n - 1;
do
{
uint8_t temp = *in;
*in++ = *e;
*e-- = temp;
}
while (in < e);
}
}
*/
static void decrypt_ecm_key(uint8_t *emmKey, uint8_t *tagData, uint8_t *ecmKey)
{
uint8_t temp, *e, *payLoad, iv[8] = { 0 };
//rotate_bytes(emmKey, 8);
e = emmKey + 8 - 1;
do
{
temp = *emmKey;
*emmKey++ = *e;
*e-- = temp;
}
while (emmKey < e);
payLoad = tagData + 4 + 5;
des_cbc_decrypt(payLoad, iv, emmKey, 16);
ecmKey[0] = payLoad[0x0F];
ecmKey[1] = payLoad[0x01];
ecmKey[2] = payLoad[0x0B];
ecmKey[3] = payLoad[0x03];
ecmKey[4] = payLoad[0x0E];
ecmKey[5] = payLoad[0x04];
ecmKey[6] = payLoad[0x0A];
ecmKey[7] = payLoad[0x08];
}
static int8_t parse_emm_nano_tags(uint8_t *data, uint32_t length, uint8_t keyIndex, uint32_t *keysAdded)
{
uint8_t tagType, tagLength, *tagData, blockIndex, emmKey[8], tagDataDecrypted[16][8];
uint32_t pos = 0, entitlementId, ks[32];
int32_t i, k;
char keyValue[17];
if (length < 2)
{
return EMU_NOT_SUPPORTED;
}
while (pos < length)
{
tagType = data[pos];
tagLength = data[pos+1];
if (pos + 2 + tagLength > length)
{
return EMU_CORRUPT_DATA;
}
tagData = data + pos + 2;
switch (tagType)
{
case 0xE4: // EMM_TAG_SECURITY_TABLE_DESCRIPTOR (ram emm keys)
{
uint8_t tagMode = data[pos + 2];
switch (tagMode)
{
case 0x01: // keySet 01 (MK01)
{
if (tagLength != 0x8A)
{
cs_log_dbg(D_READER, "WARNING: nanoTag E4 length (%d) != %d", tagLength, 0x8A);
return EMU_NOT_SUPPORTED;
}
if (!get_key(keyIndex, "MK01", emmKey, 8))
{
return EMU_KEY_NOT_FOUND;
}
uint8_t iv[8] = { 0 };
uint8_t *tagPayload = tagData + 2;
des_cbc_decrypt(tagPayload, iv, emmKey, 136);
for (k = 0; k < 16; k++) // loop 16 keys
{
for (i = 0; i < 8; i++) // loop 8 bytes of key
{
tagDataDecrypted[k][i] = tagPayload[MixTable[8 * k + i]];
}
}
blockIndex = tagData[1] & 0x03;
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
for (i = 0; i < 16; i++)
{
emu_set_key('T', (blockIndex << 4) + i, "MK01", tagDataDecrypted[i], 8, 0, NULL, NULL);
}
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
}
break;
case 0xFF: // keySet FF (MK)
{
if (tagLength != 0x82)
{
cs_log_dbg(D_READER, "WARNING: nanoTag E4 length (%d) != %d", tagLength, 0x82);
return EMU_NOT_SUPPORTED;
}
if (!get_key(keyIndex, "MK", emmKey, 8))
{
return EMU_KEY_NOT_FOUND;
}
des_set_key(emmKey, ks);
for (i = 0; i < 16; i++)
{
des(tagData + 2 + (i * 8), ks, 0);
}
blockIndex = tagData[1] & 0x03;
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
for (i = 0; i < 16; i++)
{
emu_set_key('T', (blockIndex << 4) + i, "MK", tagData + 2 + (i * 8), 8, 0, NULL, NULL);
}
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
}
break;
default:
cs_log_dbg(D_READER, "WARNING: nanoTag E4 mode %.2X not supported", tagMode);
return EMU_NOT_SUPPORTED;
}
break;
}
case 0xE1: // EMM_TAG_EVENT_ENTITLEMENT_DESCRIPTOR (ecm keys)
{
uint8_t tagMode = data[pos + 2 + 4];
switch (tagMode)
{
case 0x00: // ecm keys from mode FF
{
if (tagLength != 0x12)
{
cs_log_dbg(D_READER, "WARNING: nanoTag E1 length (%d) != %d", tagLength, 0x12);
return EMU_NOT_SUPPORTED;
}
entitlementId = b2i(4, tagData);
if (!get_key(keyIndex, "MK", emmKey, 8))
{
return EMU_KEY_NOT_FOUND;
}
des_set_key(emmKey, ks);
des(tagData + 4 + 5, ks, 0);
uint8_t ecmKeyChk[1] = { 0 };
memcpy(ecmKeyChk, tagData + 4 + 5 + 7, 1);
if (ecmKeyChk[0] != 0x00) // check if key looks valid (last byte 0x00)
{
cs_log_dbg(D_READER, "Key rejected from EMM (looks invalid)");
return EMU_KEY_REJECTED;
}
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
if (emu_update_key('T', entitlementId, "01", tagData + 4 + 5, 8, 1, NULL))
{
(*keysAdded)++;
cs_hexdump(0, tagData + 4 + 5, 8, keyValue, sizeof(keyValue));
cs_log("Key found in EMM: T %.8X 01 %s", entitlementId, keyValue);
}
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
}
break;
case 0x01: // ecm keys from mode 01
{
if (tagLength != 0x1A)
{
cs_log_dbg(D_READER, "WARNING: nanoTag E1 length (%d) != %d", tagLength, 0x1A);
return EMU_NOT_SUPPORTED;
}
entitlementId = b2i(4, tagData);
if (!get_key(keyIndex, "MK01", emmKey, 8))
{
return EMU_KEY_NOT_FOUND;
}
uint8_t ecmKey[8] = { 0 };
decrypt_ecm_key(emmKey, tagData, ecmKey);
if (ecmKey[7] != 0x00) // check if key looks valid (last byte 0x00)
{
cs_log_dbg(D_READER, "Key rejected from EMM (looks invalid)");
return EMU_KEY_REJECTED;
}
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
if (emu_update_key('T', entitlementId, "01", ecmKey, 8, 1, NULL))
{
(*keysAdded)++;
cs_hexdump(0, ecmKey, 8, keyValue, sizeof(keyValue));
cs_log("Key found in EMM: T %.8X 01 %s", entitlementId, keyValue);
}
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
}
break;
default:
cs_log_dbg(D_READER, "WARNING: nanoTag E1 mode %.2X not supported", tagMode);
return EMU_NOT_SUPPORTED;
}
break;
}
default:
cs_log_dbg(D_READER, "WARNING: nanoTag %.2X not supported", tagType);
return EMU_NOT_SUPPORTED;
}
pos += 2 + tagLength;
}
return EMU_OK;
}
static int8_t parse_emm_nano_data(uint8_t *data, uint32_t *nanoLength, uint32_t maxLength,
uint8_t keyIndex, uint32_t *keysAdded)
{
uint32_t pos = 0;
uint16_t sectionLength;
int8_t ret = EMU_OK;
if (maxLength < 2)
{
(*nanoLength) = 0;
return EMU_NOT_SUPPORTED;
}
sectionLength = ((data[pos] << 8) | data[pos + 1]) & 0x0FFF;
if (pos + 2 + sectionLength > maxLength)
{
(*nanoLength) = pos;
return EMU_CORRUPT_DATA;
}
ret = parse_emm_nano_tags(data + pos + 2, sectionLength, keyIndex, keysAdded);
pos += 2 + sectionLength;
(*nanoLength) = pos;
return ret;
}
int8_t director_emm(uint8_t *emm, uint32_t *keysAdded)
{
uint8_t keyIndex, ret = EMU_OK;
uint16_t emmLen = SCT_LEN(emm);
uint32_t pos = 3;
uint32_t permissionDataType;
uint32_t nanoLength = 0;
while (pos < emmLen && !ret)
{
permissionDataType = emm[pos];
switch (permissionDataType)
{
case 0x00:
break;
case 0x01:
pos += 0x0A;
break;
case 0x02:
pos += 0x26;
break;
default:
cs_log_dbg(D_READER, "ERROR: unknown permissionDataType %.2X (pos: %d)", permissionDataType, pos);
return EMU_NOT_SUPPORTED;
}
if (pos + 6 >= emmLen)
{
return EMU_CORRUPT_DATA;
}
keyIndex = emm[pos + 1];
// EMM validation
// Copy payload checksum bytes and then set them to zero,
// so they do not affect the calculated checksum.
uint16_t payloadChecksum = (emm[pos + 2] << 8) | emm[pos + 3];
memset(emm + pos + 2, 0, 2);
uint16_t calculatedChecksum = calculate_checksum(emm + 3, emmLen - 3);
if (calculatedChecksum != payloadChecksum)
{
cs_log_dbg(D_READER, "EMM checksum error (%.4X instead of %.4X)", calculatedChecksum, payloadChecksum);
return EMU_CHECKSUM_ERROR;
}
// End of EMM validation
pos += 0x04;
ret = parse_emm_nano_data(emm + pos, &nanoLength, emmLen - pos, keyIndex, keysAdded);
pos += nanoLength;
}
return ret;
}
#endif // WITH_EMU