645 lines
16 KiB
C
645 lines
16 KiB
C
|
|
#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
|