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

2796 lines
70 KiB
C
Raw Permalink Normal View History

#define MODULE_LOG_PREFIX "emu"
#include "globals.h"
#ifdef WITH_EMU
#include "cscrypt/des.h"
#include "module-streamrelay.h"
#include "module-emulator-osemu.h"
#include "module-emulator-powervu.h"
#include "oscam-string.h"
#include "oscam-time.h"
static inline uint8_t get_bit(uint8_t byte, uint8_t bitnb)
{
return ((byte & (1 << bitnb)) ? 1 : 0);
}
static inline uint8_t set_bit(uint8_t val, uint8_t bitnb, uint8_t biton)
{
return (biton ? (val | (1 << bitnb)) : (val & ~(1 << bitnb)));
}
static uint8_t crc8_calc(uint8_t *data, int len)
{
int i;
uint8_t crc = 0;
uint8_t crcTable[256] =
{
0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
};
for (i = 0; i < len; i++)
{
crc = crcTable[data[i] ^ crc];
}
return crc;
}
static void pad_data(uint8_t *data, int len, uint8_t *dataPadded)
{
int i;
uint8_t pad[] =
{
0x01, 0x02, 0x22, 0x04, 0x20, 0x2A, 0x1F, 0x03,
0x04, 0x06, 0x02, 0x0C, 0x2B, 0x2B, 0x01, 0x7B
};
for (i = 0; i < len; i++)
{
dataPadded[i] = data[i];
}
dataPadded[len] = 0x01;
for (i = len + 1; i < 0x2F; i++)
{
dataPadded[i] = 0x00;
}
dataPadded[0x2F] = len;
for (i = 0; i < 16; i++)
{
dataPadded[0x30 + i] = pad[i];
}
}
static void hash_mode_01_custom_md5(uint8_t *data, uint8_t *hash)
{
int i, j, s;
uint32_t a, b, c, d, f = 0, g;
uint32_t T[] =
{
0x783E16F6, 0xC267AC13, 0xA2B17F12, 0x6B8A31A4,
0xF910654D, 0xB702DBCB, 0x266CEF60, 0x5145E47C,
0xB92E00D6, 0xE80A4A64, 0x8A07FA77, 0xBA7D89A9,
0xEBED8022, 0x653AAF2B, 0xF118B03B, 0x6CC16544,
0x96EB6583, 0xF4E27E35, 0x1ABB119E, 0x068D3EF2,
0xDAEAA8A5, 0x3C312A3D, 0x59538388, 0xA100772F,
0xAB0165CE, 0x979959E7, 0x5DD8F53D, 0x189662BA,
0xFD021A9C, 0x6BC2D338, 0x1EFF667E, 0x40C66888,
0x6E9F07FF, 0x0CEF442F, 0x82D20190, 0x4E8CAEAC,
0x0F7CB305, 0x2E73FBE7, 0x1CE884A2, 0x7A60BD52,
0xC348B30D, 0x081CE3AA, 0xA12220E7, 0x38C7EC79,
0xCBD8DD3A, 0x62B4FBA5, 0xAD2A63DB, 0xE4D0852E,
0x53DE980F, 0x9C8DDA59, 0xA6B4CEDE, 0xB48A7692,
0x0E2C46A4, 0xEB9367CB, 0x165D72EE, 0x75532B45,
0xB9CA8E97, 0x08C8837B, 0x966F917B, 0x527515B4,
0xF27A5E5D, 0xB71E6267, 0x7603D7E6, 0x9837DD69
}; // CUSTOM T
uint8_t r[] =
{
0x06, 0x0A, 0x0F, 0x15, 0x05, 0x09, 0x0E, 0x14,
0x04, 0x0B, 0x10, 0x17, 0x07, 0x0C, 0x11, 0x16
}; // STANDARD REORDERED
uint8_t tIdxInit[] = { 0, 1, 5, 0 }; // STANDARD
uint8_t tIdxIncr[] = { 1, 5, 3, 7 }; // STANDARD
uint32_t h[] = { 0xEAD81D2E, 0xCE4DC6E9, 0xF9B5C301, 0x10325476 }; // CUSTOM h0, h1, h2, STANDARD h3
uint32_t dataLongs[16];
for (i = 0; i < 16; i++)
{
dataLongs[i] = (data[4 * i + 0] << 0) + (data[4 * i + 1] << 8) +
(data[4 * i + 2] << 16) + (data[4 * i + 3] << 24);
}
a = h[0];
b = h[1];
c = h[2];
d = h[3];
for (i = 0; i < 4; i++)
{
g = tIdxInit[i];
for (j = 0; j < 16; j++)
{
if (i == 0)
{
f = (b & c) | (~b & d);
}
else if (i == 1)
{
f = (b & d) | (~d & c);
}
else if (i == 2)
{
f = (b ^ c ^ d);
}
else if (i == 3)
{
f = (~d | b) ^ c;
}
f = dataLongs[g] + a + T[16 * i + j] + f;
s = r[4 * i + (j & 3)];
f = (f << s) | (f >> (32 - s));
a = d;
d = c;
c = b;
b += f;
g = (g + tIdxIncr[i]) & 0xF;
}
}
h[0] += a;
h[1] += b;
h[2] += c;
h[3] += d;
for (i = 0; i < 4; i++)
{
hash[4 * i + 0] = h[i] >> 0;
hash[4 * i + 1] = h[i] >> 8;
hash[4 * i + 2] = h[i] >> 16;
hash[4 * i + 3] = h[i] >> 24;
}
}
static void hash_mode_02(uint8_t *data, uint8_t *hash)
{
int i;
uint32_t a, b, c, d, e, f = 0, tmp;
uint32_t h[] = { 0x81887F3A, 0x36CCA480, 0x99056FB1, 0x79705BAE };
uint32_t dataLongs[80];
for (i = 0; i < 16; i++)
{
dataLongs[i] = (data[4 * i + 0] << 24) + (data[4 * i + 1] << 16) +
(data[4 * i + 2] << 8) + (data[4 * i + 3] << 0);
}
for (i = 0; i < 64; i++)
{
dataLongs[16 + i] = dataLongs[16 + i - 2];
dataLongs[16 + i] ^= dataLongs[16 + i - 7];
dataLongs[16 + i] ^= dataLongs[16 + i - 13];
dataLongs[16 + i] ^= dataLongs[16 + i - 16];
}
a = dataLongs[0];
b = dataLongs[1];
c = dataLongs[2];
d = dataLongs[3];
e = dataLongs[4];
for (i = 0; i < 80; i++)
{
if (i < 0x15) f = (b & c) | (~b & d);
else if (i < 0x28) f = (b ^ c ^ d);
else if (i < 0x3D) f = (b & c) | (c & d) | (b & d);
else if (i < 0x50) f = (b ^ c ^ d);
tmp = a;
a = e + f + (a << 5) + (a >> 27) + h[i / 0x14] + dataLongs[i];
e = d;
d = c;
c = (b << 30) + (b >> 2);
b = tmp;
}
dataLongs[0] += a;
dataLongs[1] += b;
dataLongs[2] += c;
dataLongs[3] += d;
for (i = 0; i < 4; i++)
{
hash[4 * i + 0] = dataLongs[i] >> 24;
hash[4 * i + 1] = dataLongs[i] >> 16;
hash[4 * i + 2] = dataLongs[i] >> 8;
hash[4 * i + 3] = dataLongs[i] >> 0;
}
}
static void hash_mode_03(uint8_t *data, uint8_t *hash)
{
int i, j, k, s, s2, tmp;
uint32_t a, b, c, d, f = 0, g;
uint32_t a2, b2, c2, d2, f2 = 0, g2;
uint32_t T[] = { 0xC88F3F2E, 0x967506BA, 0xDA877A7B, 0x0DECCDFE };
uint32_t T2[] = { 0x01F42668, 0x39C7CDA5, 0xD490E2FE, 0x9965235D };
uint8_t r[] =
{
0x0B, 0x0E, 0x0F, 0x0C, 0x05, 0x08, 0x07, 0x09,
0x0B, 0x0D, 0x0E, 0x0F, 0x06, 0x07, 0x09, 0x08,
0x07, 0x06, 0x08, 0x0D, 0x0B, 0x09, 0x07, 0x0F,
0x07, 0x0C, 0x0F, 0x09, 0x0B, 0x07, 0x0D, 0x0C
};
uint8_t tIdxIncr[] =
{
0x07, 0x04, 0x0D, 0x01, 0x0A, 0x06, 0x0F, 0x03,
0x0C, 0x00, 0x09, 0x05, 0x02, 0x0E, 0x0B, 0x08,
0x05, 0x0D, 0x02, 0x00, 0x04, 0x09, 0x03, 0x08,
0x01, 0x0A, 0x07, 0x0B, 0x06, 0x0F, 0x0C, 0x0E
};
uint32_t h[] = { 0xC8616857, 0x9D3F5B8E, 0x4D7B8F76, 0x97BC8D80 };
uint32_t dataLongs[80];
uint32_t result[4];
for (i = 0; i < 16; i++)
{
dataLongs[i] = (data[4 * i + 0] << 24) + (data[4 * i + 1] << 16) +
(data[4 * i + 2] << 8) + (data[4 * i + 3] << 0);
}
a = h[0];
b = h[1];
c = h[2];
d = h[3];
a2 = h[3];
b2 = h[2];
c2 = h[1];
d2 = h[0];
for (i = 0; i < 4; i++)
{
for (j = 0; j < 16; j++)
{
tmp = j;
for (k = 0; k < i; k++)
{
tmp = tIdxIncr[tmp];
}
g = 0x0F - tmp;
g2 = tmp;
if (i == 0) f = (b & d) | (~d & c);
else if (i == 1) f = (~c | b) ^ d;
else if (i == 2) f = (~b & d) | (b & c);
else if (i == 3) f = (b ^ c ^ d);
if (i == 0) f2 = (b2 ^ c2 ^ d2);
else if (i == 1) f2 = (~b2 & d2) | (b2 & c2);
else if (i == 2) f2 = (~c2 | b2) ^ d2;
else if (i == 3) f2 = (b2 & d2) | (~d2 & c2);
f = dataLongs[g] + a + T[i] + f;
s = r[0x0F + (((i & 1) ^ 1) << 4) - j];
f = (f << s) | (f >> (32 - s));
f2 = dataLongs[g2] + a2 + T2[i] + f2;
s2 = r[((i & 1) << 4) + j];
f2 = (f2 << s2) | (f2 >> (32 - s2));
a = d;
d = (c << 10) | (c >> 22);
c = b;
b = f;
a2 = d2;
d2 = (c2 << 10) | (c2 >> 22);
c2 = b2;
b2 = f2;
}
}
result[0] = h[3] + b + a2;
result[1] = h[2] + c + b2;
result[2] = h[1] + d + c2;
result[3] = h[0] + a + d2;
for (i = 0; i < 4; i++)
{
hash[4 * i + 0] = result[i] >> 0;
hash[4 * i + 1] = result[i] >> 8;
hash[4 * i + 2] = result[i] >> 16;
hash[4 * i + 3] = result[i] >> 24;
}
}
static const uint8_t table04[] =
{
0x02, 0x03, 0x07, 0x0B, 0x0D, 0x08, 0x00, 0x01, 0x2B, 0x2D, 0x28, 0x20, 0x21, 0x0A, 0x0C, 0x0E,
0x22, 0x36, 0x23, 0x27, 0x29, 0x24, 0x25, 0x26, 0x2A, 0x3C, 0x3E, 0x3F, 0x0F, 0x2C, 0x2E, 0x2F,
0x12, 0x13, 0x17, 0x1B, 0x1C, 0x18, 0x10, 0x11, 0x19, 0x14, 0x15, 0x16, 0x1A, 0x09, 0x04, 0x05,
0x32, 0x33, 0x37, 0x3B, 0x06, 0x1C, 0x1E, 0x1F, 0x3D, 0x38, 0x30, 0x31, 0x39, 0x34, 0x35, 0x3A
};
static const uint8_t table05[] =
{
0x08, 0x09, 0x0A, 0x03, 0x04, 0x3F, 0x27, 0x28, 0x29, 0x2A, 0x05, 0x0B, 0x1B, 0x1C, 0x1C, 0x1E,
0x20, 0x0C, 0x0D, 0x22, 0x23, 0x24, 0x00, 0x01, 0x02, 0x06, 0x07, 0x25, 0x26, 0x0E, 0x0F, 0x21,
0x10, 0x11, 0x12, 0x2E, 0x2F, 0x13, 0x14, 0x15, 0x2B, 0x2C, 0x2D, 0x16, 0x17, 0x18, 0x19, 0x1A,
0x30, 0x31, 0x37, 0x3B, 0x3C, 0x3D, 0x3E, 0x1F, 0x38, 0x39, 0x32, 0x33, 0x34, 0x35, 0x36, 0x3A
};
static const uint8_t table06[] =
{
0x00, 0x01, 0x02, 0x06, 0x07, 0x08, 0x03, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x04, 0x05, 0x09, 0x0D,
0x20, 0x21, 0x22, 0x26, 0x27, 0x3A, 0x3B, 0x3C, 0x3E, 0x3F, 0x10, 0x11, 0x12, 0x16, 0x17, 0x28,
0x18, 0x13, 0x14, 0x15, 0x19, 0x1C, 0x1A, 0x1B, 0x1C, 0x1E, 0x1F, 0x23, 0x24, 0x25, 0x29, 0x2D,
0x30, 0x31, 0x32, 0x36, 0x37, 0x38, 0x33, 0x34, 0x0A, 0x0B, 0x0C, 0x0E, 0x0F, 0x35, 0x39, 0x3D
};
static const uint8_t table07[] =
{
0x10, 0x11, 0x12, 0x17, 0x1C, 0x1E, 0x0E, 0x38, 0x39, 0x3A, 0x13, 0x14, 0x29, 0x2A, 0x16, 0x1F,
0x00, 0x01, 0x02, 0x3C, 0x3D, 0x3E, 0x3F, 0x07, 0x08, 0x09, 0x03, 0x04, 0x05, 0x06, 0x3B, 0x0A,
0x20, 0x21, 0x22, 0x19, 0x1A, 0x1B, 0x1C, 0x0B, 0x0C, 0x15, 0x23, 0x24, 0x25, 0x26, 0x18, 0x0F,
0x30, 0x31, 0x2B, 0x33, 0x34, 0x35, 0x36, 0x37, 0x27, 0x28, 0x2C, 0x2D, 0x2E, 0x2F, 0x32, 0x0D
};
static const uint8_t table08[] =
{
0x10, 0x11, 0x1E, 0x17, 0x18, 0x19, 0x12, 0x13, 0x14, 0x1C, 0x1C, 0x15, 0x0D, 0x05, 0x06, 0x0A,
0x00, 0x01, 0x0E, 0x07, 0x08, 0x09, 0x02, 0x2D, 0x25, 0x26, 0x2A, 0x2B, 0x2F, 0x03, 0x04, 0x0C,
0x20, 0x21, 0x2E, 0x27, 0x28, 0x29, 0x30, 0x31, 0x3E, 0x37, 0x38, 0x39, 0x22, 0x23, 0x24, 0x2C,
0x32, 0x33, 0x34, 0x3C, 0x3D, 0x35, 0x36, 0x3A, 0x3B, 0x0B, 0x0F, 0x16, 0x1A, 0x1B, 0x1F, 0x3F
};
static const uint8_t table09[] =
{
0x20, 0x21, 0x24, 0x22, 0x23, 0x2A, 0x2B, 0x33, 0x35, 0x38, 0x39, 0x36, 0x2D, 0x2C, 0x2E, 0x2F,
0x00, 0x01, 0x04, 0x02, 0x25, 0x28, 0x08, 0x09, 0x06, 0x07, 0x0A, 0x0B, 0x0D, 0x0C, 0x0E, 0x0F,
0x10, 0x11, 0x14, 0x12, 0x13, 0x15, 0x19, 0x16, 0x29, 0x26, 0x03, 0x17, 0x1A, 0x1C, 0x1C, 0x1E,
0x30, 0x31, 0x34, 0x32, 0x37, 0x3A, 0x3B, 0x3D, 0x3C, 0x3E, 0x3F, 0x1B, 0x05, 0x18, 0x27, 0x1F
};
static const uint8_t table0A[] =
{
0x00, 0x04, 0x05, 0x0B, 0x0C, 0x06, 0x09, 0x0A, 0x0E, 0x0D, 0x0F, 0x25, 0x15, 0x1B, 0x1C, 0x16,
0x10, 0x11, 0x01, 0x02, 0x03, 0x07, 0x08, 0x12, 0x13, 0x17, 0x18, 0x14, 0x23, 0x27, 0x28, 0x24,
0x30, 0x31, 0x32, 0x33, 0x37, 0x38, 0x34, 0x35, 0x3B, 0x3C, 0x20, 0x21, 0x22, 0x2B, 0x2C, 0x26,
0x36, 0x39, 0x3A, 0x3E, 0x3D, 0x19, 0x1A, 0x1E, 0x1C, 0x1F, 0x3F, 0x29, 0x2A, 0x2E, 0x2D, 0x2F
};
static void hash_modes_04_to_0A_tables(uint8_t *data, uint8_t *hash, const uint8_t *table)
{
int i;
for (i = 0; i < 16; i++)
{
hash[i] = table[i];
hash[i] ^= data[table[i]];
hash[i] ^= table[16 + i];
hash[i] ^= data[table[16 + i]];
hash[i] ^= table[32 + i];
hash[i] ^= data[table[32 + i]];
hash[i] ^= table[48 + i];
hash[i] ^= data[table[48 + i]];
}
}
static const uint8_t table0F[] = { 0xC7, 0x45, 0x15, 0x71, 0x61, 0x07, 0x05, 0x47 };
static const uint8_t table10[] = { 0x0F, 0x47, 0x2B, 0x6C, 0xAD, 0x0F, 0xB3, 0xEA };
static const uint8_t table11[] = { 0xB1, 0x46, 0xD1, 0x66, 0x5D, 0x28, 0x59, 0xD2 };
static const uint8_t table12[] = { 0x0B, 0x4B, 0xD7, 0x68, 0x5F, 0xAD, 0x4B, 0xBB };
static const uint8_t table13[] = { 0x4F, 0x4E, 0xE1, 0x6A, 0x21, 0xD3, 0xF7, 0xA6 };
static const uint8_t table14[] = { 0xDD, 0x39, 0xB9, 0x65, 0x03, 0x91, 0xF1, 0xAC };
static const uint8_t table15[] = { 0x3F, 0x50, 0xB5, 0x6F, 0x37, 0xC9, 0x13, 0x5D };
static const uint8_t table16[] = { 0xF9, 0x5C, 0xFD, 0x72, 0x19, 0x42, 0x23, 0x6B };
static const uint8_t table17[] = { 0xDF, 0x60, 0x93, 0x64, 0x33, 0x16, 0xB3, 0x8A };
static const uint8_t table18[] = { 0x09, 0x64, 0x5F, 0x6B, 0xFB, 0x21, 0x19, 0xE4 };
static void hash_modes_0F_to_18_tables(uint8_t *data, uint8_t *hash, const uint8_t *table)
{
int i;
uint32_t t[4], tmp;
memset(hash, 0x00, 16);
t[0] = (table[1] << 8) + table[0];
t[1] = (table[3] << 8) + table[2];
t[2] = (table[5] << 8) + table[4];
t[3] = (table[7] << 8) + table[6];
for (i = 0; i < 60; i += 4)
{
t[0] = ((t[0] & 0xFFFF) * t[2]) + (t[0] >> 16);
t[1] = ((t[1] & 0xFFFF) * t[3]) + (t[1] >> 16);
tmp = t[0] + t[1];
hash[(i + 0) & 0x0F] = hash[(i + 0) & 0x0F] ^ data[i + 0] ^ (tmp >> 24);
hash[(i + 1) & 0x0F] = hash[(i + 1) & 0x0F] ^ data[i + 1] ^ (tmp >> 16);
hash[(i + 2) & 0x0F] = hash[(i + 2) & 0x0F] ^ data[i + 2] ^ (tmp >> 8);
hash[(i + 3) & 0x0F] = hash[(i + 3) & 0x0F] ^ data[i + 3] ^ (tmp >> 0);
}
}
static const uint8_t table19[] = { 0x02, 0x03, 0x05, 0x10 };
static const uint8_t table1A[] = { 0x01, 0x05, 0x08, 0x10 };
static const uint8_t table1B[] = { 0x03, 0x07, 0x08, 0x10 };
static const uint8_t table1C[] = { 0x03, 0x05, 0x0A, 0x10 };
static const uint8_t table1D[] = { 0x03, 0x07, 0x0A, 0x10 };
static const uint8_t table1E[] = { 0x01, 0x05, 0x0B, 0x10 };
static const uint8_t table1F[] = { 0x06, 0x07, 0x0B, 0x10 };
static const uint8_t table20[] = { 0x01, 0x08, 0x0B, 0x10 };
static const uint8_t table21[] = { 0x01, 0x07, 0x0C, 0x10 };
static const uint8_t table22[] = { 0x05, 0x0B, 0x0C, 0x10 };
static const uint8_t table23[] = { 0x0B, 0x0C, 0x0D, 0x10 };
static const uint8_t table24[] = { 0x07, 0x09, 0x0E, 0x10 };
static const uint8_t table25[] = { 0x01, 0x04, 0x0F, 0x10 };
static const uint8_t table26[] = { 0x07, 0x08, 0x0F, 0x10 };
static const uint8_t table27[] = { 0x02, 0x0B, 0x0F, 0x10 };
static void hash_modes_19_to_27_tables_3(uint8_t *data, uint8_t *hash, const uint8_t *table)
{
int i;
uint8_t val, it[4];
uint16_t seed = 0xFFFF, tmp;
memset(hash, 0x00, 16);
for (i = 0; i < 4; i++)
{
it[i] = 0x10 - table[i];
}
for (i = 0; i < 16; i++)
{
val = ((seed >> it[0]) ^ (seed >> it[1]) ^ (seed >> it[2]) ^ (seed >> it[3])) & 0x01;
if (val == 0x00)
{
seed = seed >> 1;
}
else
{
seed = (seed >> 1) | 0x8000;
}
tmp = seed + (data[i] << 8) + data[i + 32];
val = ((seed >> it[0]) ^ (seed >> it[1]) ^ (seed >> it[2]) ^ (seed >> it[3])) & 0x01;
if (val == 0x00)
{
seed = seed >> 1;
}
else
{
seed = (seed >> 1) | 0x8000;
}
tmp = tmp + seed + (data[i + 16] << 8) + data[i + 48];
hash[i & 0x0F] ^= tmp >> 8;
hash[(i + 1) & 0x0F] ^= tmp;
}
}
static void create_hash(uint8_t *data, int len, uint8_t *hash, int mode)
{
if ((mode > 0x27) || (mode == 0x0B) || (mode == 0x0C) ||
(mode == 0x0D) || (mode == 0x0E) || (mode == 0))
{
memset(hash, 0, 16);
return;
}
uint8_t dataPadded[64];
pad_data(data, len, dataPadded);
switch (mode)
{
case 1:
hash_mode_01_custom_md5(dataPadded, hash);
break;
case 2:
hash_mode_02(dataPadded, hash);
break;
case 3:
hash_mode_03(dataPadded, hash);
break;
case 4:
hash_modes_04_to_0A_tables(dataPadded, hash, table04);
break;
case 5:
hash_modes_04_to_0A_tables(dataPadded, hash, table05);
break;
case 6:
hash_modes_04_to_0A_tables(dataPadded, hash, table06);
break;
case 7:
hash_modes_04_to_0A_tables(dataPadded, hash, table07);
break;
case 8:
hash_modes_04_to_0A_tables(dataPadded, hash, table08);
break;
case 9:
hash_modes_04_to_0A_tables(dataPadded, hash, table09);
break;
case 10:
hash_modes_04_to_0A_tables(dataPadded, hash, table0A);
break;
case 15:
hash_modes_0F_to_18_tables(dataPadded, hash, table0F);
break;
case 16:
hash_modes_0F_to_18_tables(dataPadded, hash, table10);
break;
case 17:
hash_modes_0F_to_18_tables(dataPadded, hash, table11);
break;
case 18:
hash_modes_0F_to_18_tables(dataPadded, hash, table12);
break;
case 19:
hash_modes_0F_to_18_tables(dataPadded, hash, table13);
break;
case 20:
hash_modes_0F_to_18_tables(dataPadded, hash, table14);
break;
case 21:
hash_modes_0F_to_18_tables(dataPadded, hash, table15);
break;
case 22:
hash_modes_0F_to_18_tables(dataPadded, hash, table16);
break;
case 23:
hash_modes_0F_to_18_tables(dataPadded, hash, table17);
break;
case 24:
hash_modes_0F_to_18_tables(dataPadded, hash, table18);
break;
case 25:
hash_modes_19_to_27_tables_3(dataPadded, hash, table19);
break;
case 26:
hash_modes_19_to_27_tables_3(dataPadded, hash, table1A);
break;
case 27:
hash_modes_19_to_27_tables_3(dataPadded, hash, table1B);
break;
case 28:
hash_modes_19_to_27_tables_3(dataPadded, hash, table1C);
break;
case 29:
hash_modes_19_to_27_tables_3(dataPadded, hash, table1D);
break;
case 30:
hash_modes_19_to_27_tables_3(dataPadded, hash, table1E);
break;
case 31:
hash_modes_19_to_27_tables_3(dataPadded, hash, table1F);
break;
case 32:
hash_modes_19_to_27_tables_3(dataPadded, hash, table20);
break;
case 33:
hash_modes_19_to_27_tables_3(dataPadded, hash, table21);
break;
case 34:
hash_modes_19_to_27_tables_3(dataPadded, hash, table22);
break;
case 35:
hash_modes_19_to_27_tables_3(dataPadded, hash, table23);
break;
case 36:
hash_modes_19_to_27_tables_3(dataPadded, hash, table24);
break;
case 37:
hash_modes_19_to_27_tables_3(dataPadded, hash, table25);
break;
case 38:
hash_modes_19_to_27_tables_3(dataPadded, hash, table26);
break;
case 39:
hash_modes_19_to_27_tables_3(dataPadded, hash, table27);
break;
default:
cs_log("A new hash mode [%d] is in use.", mode);
break;
}
}
static void create_hash_mode_03(uint8_t *data, uint8_t *hash)
{
int i, j, c;
uint8_t buffer0[16], buffer1[8], buffer2[8], tmpBuff1[4], tmpBuff2[4];
uint8_t table[] =
{
0x68, 0xCE, 0xE7, 0x71, 0xCC, 0x3A, 0x0B, 0x6E, 0x2A, 0x43, 0x17, 0x07, 0x5A, 0xD9, 0x14, 0x5B,
0xB0, 0x8E, 0xA8, 0x7F, 0xD8, 0xA2, 0xCF, 0x73, 0xC2, 0xB9, 0x5D, 0x46, 0xDD, 0x2C, 0xE2, 0x2D,
0xFD, 0x50, 0xE9, 0x7C, 0x28, 0x72, 0x9B, 0xAA, 0xEC, 0x24, 0x74, 0xAB, 0x00, 0x1C, 0x8B, 0x65,
0x38, 0x13, 0x22, 0x82, 0xAC, 0x9A, 0x4D, 0x2B, 0xEA, 0x04, 0x31, 0x84, 0x32, 0x3D, 0x36, 0x53,
0x5F, 0x42, 0x96, 0xDE, 0x47, 0x08, 0x51, 0x4B, 0x3E, 0xD1, 0x1E, 0x12, 0xD2, 0x1F, 0x7D, 0x26,
0xCD, 0x57, 0x8C, 0xB6, 0xD3, 0xF8, 0x11, 0xAD, 0x6A, 0x88, 0x95, 0x21, 0xE8, 0xBF, 0x6B, 0x27,
0xBE, 0xA3, 0x33, 0xB8, 0x9E, 0xB3, 0x6C, 0xC3, 0x06, 0xC7, 0x6F, 0x99, 0x97, 0xDA, 0x09, 0xAF,
0xAE, 0xCB, 0x79, 0x37, 0x55, 0x85, 0x8D, 0x2F, 0x8A, 0x70, 0xA1, 0x7A, 0x66, 0x29, 0x67, 0x0F,
0xEB, 0x9C, 0xC8, 0xC4, 0xD6, 0x4C, 0xDF, 0x1A, 0xC0, 0x01, 0x64, 0xBC, 0x4E, 0xE1, 0x54, 0xD7,
0x4F, 0xB7, 0x5E, 0xCA, 0xF0, 0x91, 0xE4, 0x59, 0x4A, 0xC6, 0x83, 0x8F, 0xBD, 0x61, 0xFF, 0x56,
0x92, 0xF1, 0x5C, 0x77, 0xC9, 0x20, 0xF4, 0xE5, 0x10, 0x69, 0x03, 0x1D, 0xD5, 0x45, 0xF6, 0x0E,
0xEF, 0xA0, 0xE3, 0x58, 0xFC, 0xED, 0x80, 0x16, 0xEE, 0xFA, 0x02, 0xF5, 0xB4, 0x0A, 0xE0, 0x0C,
0xF7, 0xF9, 0xBA, 0x7E, 0x18, 0x78, 0x19, 0xB5, 0x0D, 0x44, 0x34, 0xD4, 0xDC, 0x30, 0x6D, 0x3B,
0x63, 0x41, 0x48, 0x40, 0xA7, 0xA5, 0xC5, 0x98, 0x76, 0x3F, 0xC1, 0x25, 0x93, 0x49, 0xD0, 0x62,
0x2E, 0x75, 0xDB, 0x94, 0xF3, 0x52, 0x05, 0x81, 0xFB, 0xBB, 0xA6, 0x89, 0x39, 0xA4, 0xF2, 0xA9,
0xFE, 0x60, 0x3C, 0x15, 0xB1, 0x35, 0x86, 0x9D, 0x9F, 0x90, 0x1B, 0xE6, 0x7B, 0x23, 0x87, 0xB2
};
for (i = 0; i < 4; i++)
{
buffer0[0 + i] = data[12 + i];
buffer0[4 + i] = data[8 + i];
buffer0[8 + i] = data[4 + i];
buffer0[12 + i] = data[0 + i];
}
for (c = 0; c < 12; c++)
{
for (i = 0; i < 4; i++)
{
buffer1[0 + i] = buffer0[8 + i] ^ buffer0[12 + i];
buffer1[4 + i] = buffer0[0 + i] ^ buffer0[4 + i];
}
for (i = 0; i < 8; i++)
{
buffer1[i] = table[buffer1[i] ^ data[16 + 16 * (c % 3) + i]];
}
for (j = 0; j < 8; j++)
{
buffer2[j] = 0;
for (i = 0; i < 8; i++)
{
buffer2[j] ^= buffer1[i] * (j * i + 1);
}
}
for (i = 0; i < 8; i++)
{
buffer2[i] = table[buffer2[i] ^ data[24 + 16 * (c % 3) + i]] ^ data[16 + 16 * (c % 3) + i];
}
for (i = 0; i < 4; i++)
{
buffer0[12 + i] ^= buffer2[0 + i];
buffer0[8 + i] ^= buffer2[0 + i];
buffer0[4 + i] ^= buffer2[4 + i];
buffer0[0 + i] ^= buffer2[4 + i];
}
tmpBuff1[0] = buffer0[14];
tmpBuff1[1] = buffer0[15];
tmpBuff1[2] = buffer0[12] ^ buffer0[14];
tmpBuff1[3] = buffer0[13] ^ buffer0[15];
tmpBuff2[0] = buffer0[6];
tmpBuff2[1] = buffer0[7];
tmpBuff2[2] = buffer0[4] ^ buffer0[6];
tmpBuff2[3] = buffer0[5] ^ buffer0[7];
for (i = 0; i < 4; i++)
{
buffer0[12 + i] = tmpBuff1[i];
buffer0[4 + i] = tmpBuff2[i];
}
}
for (i = 0; i < 4; i++)
{
hash[0 + i] = buffer0[12 + i] ^ data[0 + i];
hash[4 + i] = buffer0[8 + i] ^ data[4 + i];
hash[8 + i] = buffer0[4 + i] ^ data[8 + i];
hash[12 + i] = buffer0[0 + i] ^ data[12 + i];
}
}
static void create_data_cw_mode_03(uint8_t *seed, int lenSeed, uint8_t *basecw,
uint8_t val, uint8_t *ecmBody, uint8_t *data)
{
int idxData = 8, idxSeed = 0, idxBase = 0;
uint8_t padding[] =
{
0x4A, 0x56, 0x7F, 0x16, 0xFC, 0x1F, 0x5B, 0x95,
0x19, 0xEF, 0x75, 0x14, 0x0E, 0x9E, 0x17, 0x3C,
0xF5, 0xB7, 0xA0, 0x93, 0xA3, 0x0F, 0xFA, 0x38,
0x7A, 0x34, 0x6C, 0xDC, 0xFB, 0xB0, 0x24, 0x42,
0x74, 0x72, 0x1C, 0xDC, 0x1E, 0xA1, 0x6D, 0xAB,
0xC8, 0x44, 0x53, 0xEF, 0x56, 0x00, 0xE9, 0x97,
0x48, 0x77, 0xF8, 0x00, 0x8E, 0x0B, 0x78, 0xA2
};
memcpy(data + 8, padding, 56);
data[0] = ecmBody[0x0F];
data[1] = ecmBody[0x09];
data[2] = ecmBody[0x10];
data[3] = ecmBody[0x11];
data[4] = ecmBody[0x05];
data[5] = ecmBody[0x07];
data[6] = ecmBody[0x08];
data[7] = ecmBody[0x0A];
while (idxBase < 7)
{
if ((idxBase == 0) || (idxBase == 2) || (idxBase == 5))
{
data[idxData++] = val;
}
if (idxSeed < lenSeed)
{
data[idxData++] = seed[idxSeed++];
}
data[idxData++] = basecw[idxBase++];
}
}
static void create_data_unmask_mode_03(uint8_t *ecmBody, uint8_t *data)
{
uint8_t padding[] =
{
0xB1, 0x7C, 0xD2, 0xA7, 0x5E, 0x45, 0x6C, 0x36,
0xF0, 0xB6, 0x81, 0xF3, 0x25, 0x06, 0x65, 0x06,
0x6B, 0xBF, 0x4C, 0xE7, 0xED, 0x6E, 0x85, 0x00,
0xCC, 0xF2, 0x61, 0x48, 0x62, 0x24, 0x0E, 0x3C,
0x05, 0x89, 0xA5, 0x39, 0x5A, 0x4E, 0x9B, 0xC8,
0x14, 0x78, 0xEA, 0xB6, 0xFB, 0xF8, 0x10, 0xE6,
0x61, 0xF5, 0x3A, 0xBC, 0x5B, 0x79, 0x09, 0x97
};
memcpy(data + 8, padding, 56);
data[0] = ecmBody[0x17];
data[1] = ecmBody[0x26];
data[2] = ecmBody[0x19];
data[3] = ecmBody[0x21];
data[4] = ecmBody[0x26];
data[5] = ecmBody[0x31];
data[6] = ecmBody[0x21];
data[7] = ecmBody[0x27];
}
static void hash_04_add(uint32_t *buffer, int a, int b, int c, int d, int e, int f)
{
uint32_t tmp1 = (buffer[a] & 1) + (buffer[b] & 1);
uint32_t tmp2 = (buffer[a] >> 1) + (buffer[b] >> 1) + (tmp1 >> 1);
buffer[e] = buffer[c] + buffer[d] + (tmp2 >> 31);
buffer[f] = tmp2 + tmp2 + (tmp1 & 1);
}
static void hash_04_shift(uint32_t *buffer, int a, int b, uint8_t shift)
{
uint32_t tmp1 = (buffer[a] >> (32 - shift)) + (buffer[b] << shift);
uint32_t tmp2 = (buffer[b] >> (32 - shift)) + (buffer[a] << shift);
buffer[b] = tmp1;
buffer[a] = tmp2;
}
static void hash_04_xor(uint32_t *buffer, int a, int b, int c, int d)
{
buffer[a] ^= buffer[b];
buffer[c] ^= buffer[d];
}
static void hash_04_swap(uint32_t *buffer, int a, int b)
{
uint32_t tmp = buffer[a];
buffer[a] = buffer[b];
buffer[b] = tmp;
}
static void hash_04_core(uint32_t *buffer)
{
hash_04_add(buffer, 0, 6, 7, 1, 7, 6);
hash_04_shift(buffer, 5, 4, 0x0D);
hash_04_xor(buffer, 4, 2, 5, 3);
hash_04_swap(buffer, 7, 6);
hash_04_add(buffer, 6, 2, 3, 7, 3, 2);
hash_04_shift(buffer, 1, 0, 0x10);
hash_04_xor(buffer, 0, 4, 1, 5);
hash_04_add(buffer, 6, 2, 3, 7, 7, 6);
hash_04_shift(buffer, 1, 0, 0x15);
hash_04_add(buffer, 6, 0, 1, 7, 1, 0);
hash_04_xor(buffer, 2, 4, 3, 5);
hash_04_shift(buffer, 5, 4, 0x11);
hash_04_xor(buffer, 4, 2, 5, 3);
hash_04_swap(buffer, 3, 2);
}
static void create_hash_mode_04(uint8_t *data, uint8_t *hash)
{
int i, j;
uint32_t d0, d1, h0, h1, h2, h3;
uint32_t buffer[] =
{
0x1F253724, 0x3E8136B3, 0x9677CEDF, 0x25B5E75A,
0x9494BC16, 0xCFD3FB34, 0xF37C75BB, 0x97D4632E
};
for (j = 0; j < 64; j += 8)
{
d0 = (data[j + 3] << 24) + (data[j + 2] << 16) + (data[j + 1] << 8) + data[j + 0];
d1 = (data[j + 7] << 24) + (data[j + 6] << 16) + (data[j + 5] << 8) + data[j + 4];
buffer[0] ^= d0;
buffer[1] ^= d1;
for (i = 0; i < 2; i++)
{
hash_04_core(buffer);
}
buffer[6] ^= d0;
buffer[7] ^= d1;
}
buffer[1] ^= 0x40000000;
buffer[0] ^= 0x00000000;
for (i = 0; i < 2; i++)
{
hash_04_core(buffer);
}
buffer[7] ^= 0x40000000;
buffer[6] ^= 0x00000000;
buffer[2] ^= 0xEE;
for (i = 0; i < 4; i++)
{
hash_04_core(buffer);
}
h0 = buffer[0] ^ buffer[2] ^ buffer[4] ^ buffer[6];
h1 = buffer[1] ^ buffer[3] ^ buffer[5] ^ buffer[7];
hash[0] = (uint8_t) h0;
hash[1] = (uint8_t) (h0 >> 8);
hash[2] = (uint8_t) (h0 >> 16);
hash[3] = (uint8_t) (h0 >> 24);
hash[4] = (uint8_t) h1;
hash[5] = (uint8_t) (h1 >> 8);
hash[6] = (uint8_t) (h1 >> 16);
hash[7] = (uint8_t) (h1 >> 24);
buffer[4] ^= 0xDD;
for (i = 0; i < 4; i++)
{
hash_04_core(buffer);
}
h2 = buffer[0] ^ buffer[2] ^ buffer[4] ^ buffer[6];
h3 = buffer[1] ^ buffer[3] ^ buffer[5] ^ buffer[7];
hash[8] = (uint8_t) h2;
hash[9] = (uint8_t) (h2 >> 8);
hash[10] = (uint8_t) (h2 >> 16);
hash[11] = (uint8_t) (h2 >> 24);
hash[12] = (uint8_t) h3;
hash[13] = (uint8_t) (h3 >> 8);
hash[14] = (uint8_t) (h3 >> 16);
hash[15] = (uint8_t) (h3 >> 24);
}
static void create_data_cw_mode_04(uint8_t *seed, int lenSeed, uint8_t *basecw,
uint8_t val, uint8_t *ecmBody, uint8_t *data)
{
uint8_t padding[] =
{
0x18, 0xD6, 0x24, 0xA8, 0xDE, 0x14, 0xD8, 0x30,
0x3C, 0xB2, 0x24, 0x54, 0x17, 0x5A, 0x28, 0x61,
0xBC, 0xB9, 0x29, 0xAD, 0xA5, 0x13, 0xD4, 0x24,
0x6D, 0x61, 0x40, 0xC8, 0xFD, 0x27, 0xD7, 0xFF,
0x3E, 0x84, 0x50, 0xC2, 0x47, 0x4C, 0xD5, 0xC5,
0xF2, 0x79, 0xAD, 0x02, 0xC5, 0x05, 0x7B, 0xFD,
0x60, 0x4A, 0x16, 0xE5, 0xAA, 0x0E, 0x97, 0x1C
};
memcpy(data + 8, padding, 56);
data[0] = ecmBody[0x0E];
data[1] = ecmBody[0x0A];
data[2] = ecmBody[0x0C];
data[3] = ecmBody[0x04];
data[4] = ecmBody[0x10];
data[5] = ecmBody[0x08];
data[6] = ecmBody[0x05];
data[7] = ecmBody[0x0F];
int idxData = 8, idxSeed = 0, idxBase = 0;
while (idxBase < 7)
{
if ((idxBase == 0) || (idxBase == 1) || (idxBase == 2))
{
data[idxData++] = val;
}
if (idxSeed < lenSeed)
{
data[idxData++] = seed[idxSeed++];
}
data[idxData++] = basecw[idxBase++];
}
}
static void create_data_unmask_mode_04(uint8_t *ecmBody, uint8_t *data)
{
uint8_t padding[] =
{
0x0E, 0x4A, 0x85, 0x85, 0xF9, 0xC0, 0xCC, 0x00,
0xBA, 0x9B, 0x98, 0x35, 0x4C, 0xD2, 0xC1, 0x6C,
0x87, 0x32, 0x9B, 0x82, 0x31, 0x5B, 0x1D, 0xB4,
0xB8, 0x98, 0x74, 0xFF, 0x31, 0x66, 0x08, 0x79,
0x47, 0xCE, 0x96, 0x4D, 0xE9, 0x52, 0xCF, 0x8F,
0xEC, 0x5C, 0x07, 0xBC, 0x09, 0xA2, 0x82, 0x78,
0x3D, 0xB9, 0xFF, 0x3F, 0x76, 0x72, 0x6F, 0x9C
};
memcpy(data + 8, padding, 56);
data[0] = ecmBody[0x17];
data[1] = ecmBody[0x2B];
data[2] = ecmBody[0x1D];
data[3] = ecmBody[0x2D];
data[4] = ecmBody[0x0B];
data[5] = ecmBody[0x06];
data[6] = ecmBody[0x2F];
data[7] = ecmBody[0x1E];
}
static uint8_t get_mode_cw(uint8_t *extraData)
{
uint64_t data = ((uint32_t)extraData[0] << 24) + (extraData[1] << 16) + (extraData[2] << 8) + extraData[3];
uint64_t t1 = (data * 0x76E9DEA7) >> 50;
uint64_t t2 = (t1 * 0x51EB851F) >> 36;
uint64_t t3 = t2 * 0x32;
uint8_t r = t1 - t3;
return r;
}
static uint8_t get_mode_unmask(uint8_t *extraData)
{
uint64_t data = ((uint32_t)extraData[0] << 24) + (extraData[1] << 16) + (extraData[2] << 8) + extraData[3];
uint64_t t1 = (data * 0xB9CD6BE5) >> 45;
uint64_t t2 = (t1 * 0x51EB851F) >> 36;
uint64_t t3 = t2 * 0x32;
uint8_t r = t1 - t3;
return r;
}
static void create_data_ecm_emm(uint8_t *emmEcm, uint8_t *pos, int lenHeader, int len, uint8_t *data)
{
int i;
for (i = 0; i < len; i++)
{
data[i] = emmEcm[lenHeader + pos[i]];
}
}
static uint8_t create_data_cw(uint8_t *seed, uint8_t lenSeed, uint8_t *baseCw,
uint8_t val, uint8_t *seedEcmCw, uint8_t *data)
{
int i;
for (i = 0; i < lenSeed; i++)
{
data[i] = seed[i];
}
for (i = 0; i < 7; i++)
{
data[lenSeed + i] = baseCw[i];
}
data[lenSeed + 7] = val;
for (i = 0; i < 16; i++)
{
data[lenSeed + 7 + 1 + i] = seedEcmCw[i];
}
return lenSeed + 7 + 1 + 0x10;
}
static uint8_t unmask_ecm(uint8_t *ecm, uint8_t *seedEcmCw, uint8_t *modeCW)
{
int i, l;
uint8_t data[64], mask[16];
uint8_t hashModeEcm, hashModeCw, modeUnmask = 0;
uint32_t crc;
uint8_t sourcePos[] =
{
0x04, 0x05, 0x06, 0x07, 0x0A, 0x0B, 0x0C, 0x0D,
0x0E, 0x0F, 0x10, 0x17, 0x1C, 0x1D, 0x1F, 0x23,
0x24, 0x25, 0x26, 0x27, 0x29, 0x2C, 0x2D, 0x2E
};
uint8_t destPos[] =
{
0x08, 0x09, 0x11, 0x18, 0x19, 0x1A, 0x1B, 0x1E,
0x20, 0x21, 0x22, 0x28, 0x2A, 0x2B, 0x2F, 0x30
};
uint8_t seedCwPos[] = { 0x07, 0x0A, 0x04, 0x0D, 0x05, 0x0E, 0x06, 0x0B, 0x10, 0x0C, 0x0F };
// Create seed for CW decryption
memset(seedEcmCw, 0, 16);
int extraBytesLen = ecm[9];
int startOffset = extraBytesLen + 10;
for (i = 0; i < 11; i++)
{
seedEcmCw[i] = ecm[startOffset + seedCwPos[i]];
}
*modeCW = 0;
if (extraBytesLen > 0)
{
*modeCW = get_mode_cw(ecm + 10);
}
// Read hash mode CW
hashModeCw = ecm[28 + extraBytesLen] ^ crc8_calc(seedEcmCw, 16);
// Create mask for ECM decryption
create_data_ecm_emm(ecm, sourcePos, startOffset, 24, data);
hashModeEcm = ecm[8] ^ crc8_calc(data, 24);
if (extraBytesLen > 0)
{
modeUnmask = get_mode_unmask(ecm + 10);
}
if (modeUnmask == 0x03)
{
ecm[startOffset + 0x21] -= ecm[startOffset + 0x07];
ecm[startOffset + 0x26] -= ecm[startOffset + 0x05];
ecm[startOffset + 0x26] -= ecm[startOffset + 0x08];
ecm[startOffset + 0x19] -= ecm[startOffset + 0x06];
ecm[startOffset + 0x31] -= ecm[startOffset + 0x09];
ecm[startOffset + 0x27] -= ecm[startOffset + 0x0C];
ecm[startOffset + 0x21] -= ecm[startOffset + 0x0B];
ecm[startOffset + 0x17] -= ecm[startOffset + 0x04];
create_data_unmask_mode_03(ecm + startOffset, data);
create_hash_mode_03(data, mask);
// Unmask body
ecm[startOffset + 0x06] ^= mask[0x02];
ecm[startOffset + 0x0B] ^= mask[0x06];
ecm[startOffset + 0x0C] ^= mask[0x07];
ecm[startOffset + 0x0D] ^= mask[0x08];
ecm[startOffset + 0x0E] ^= mask[0x09];
ecm[startOffset + 0x0F] ^= mask[0x0A];
ecm[startOffset + 0x11] ^= mask[0x0B];
ecm[startOffset + 0x18] ^= mask[0x0C];
ecm[startOffset + 0x2D] ^= mask[0x0A];
ecm[startOffset + 0x07] ^= mask[0x03];
ecm[startOffset + 0x1B] ^= mask[0x0D];
ecm[startOffset + 0x30] ^= mask[0x0C];
ecm[startOffset + 0x1C] ^= mask[0x0E];
ecm[startOffset + 0x1E] ^= mask[0x00];
ecm[startOffset + 0x04] ^= mask[0x00];
ecm[startOffset + 0x05] ^= mask[0x01];
ecm[startOffset + 0x1F] ^= mask[0x01];
ecm[startOffset + 0x2C] ^= mask[0x09];
ecm[startOffset + 0x20] ^= mask[0x02];
ecm[startOffset + 0x1D] ^= mask[0x0F];
ecm[startOffset + 0x23] ^= mask[0x04];
ecm[startOffset + 0x09] ^= mask[0x05];
ecm[startOffset + 0x22] ^= mask[0x03];
ecm[startOffset + 0x24] ^= mask[0x05];
ecm[startOffset + 0x08] ^= mask[0x04];
ecm[startOffset + 0x28] ^= mask[0x06];
ecm[startOffset + 0x29] ^= mask[0x07];
ecm[startOffset + 0x2A] ^= mask[0x08];
ecm[startOffset + 0x2E] ^= mask[0x0B];
for (i = 0; i < ecm[9]; i++)
{
ecm[10 + i] = 0x00;
}
}
else if (modeUnmask == 0x04)
{
ecm[startOffset + 0x1E] -= ecm[startOffset + 0x0D];
ecm[startOffset + 0x1D] -= ecm[startOffset + 0x07];
ecm[startOffset + 0x2B] -= ecm[startOffset + 0x05];
ecm[startOffset + 0x2D] -= ecm[startOffset + 0x08];
ecm[startOffset + 0x17] -= ecm[startOffset + 0x04];
ecm[startOffset + 0x2F] -= ecm[startOffset + 0x0C];
ecm[startOffset + 0x06] -= ecm[startOffset + 0x0A];
ecm[startOffset + 0x0B] -= ecm[startOffset + 0x09];
create_data_unmask_mode_04(ecm + startOffset, data);
create_hash_mode_04(data, mask);
// Unmask body
ecm[startOffset + 0x04] ^= mask[0x00];
ecm[startOffset + 0x05] ^= mask[0x01];
ecm[startOffset + 0x07] ^= mask[0x02];
ecm[startOffset + 0x08] ^= mask[0x03];
ecm[startOffset + 0x09] ^= mask[0x04];
ecm[startOffset + 0x0A] ^= mask[0x05];
ecm[startOffset + 0x0C] ^= mask[0x06];
ecm[startOffset + 0x0D] ^= mask[0x07];
ecm[startOffset + 0x0E] ^= mask[0x08];
ecm[startOffset + 0x10] ^= mask[0x09];
ecm[startOffset + 0x11] ^= mask[0x0A];
ecm[startOffset + 0x18] ^= mask[0x0B];
ecm[startOffset + 0x1A] ^= mask[0x0C];
ecm[startOffset + 0x1B] ^= mask[0x0D];
ecm[startOffset + 0x1C] ^= mask[0x0E];
ecm[startOffset + 0x1F] ^= mask[0x0F];
ecm[startOffset + 0x22] ^= mask[0x00];
ecm[startOffset + 0x24] ^= mask[0x01];
ecm[startOffset + 0x25] ^= mask[0x02];
ecm[startOffset + 0x26] ^= mask[0x03];
ecm[startOffset + 0x27] ^= mask[0x04];
ecm[startOffset + 0x28] ^= mask[0x05];
ecm[startOffset + 0x29] ^= mask[0x06];
ecm[startOffset + 0x2A] ^= mask[0x07];
ecm[startOffset + 0x2C] ^= mask[0x08];
ecm[startOffset + 0x2E] ^= mask[0x09];
ecm[startOffset + 0x31] ^= mask[0x0A];
for (i = 0; i < ecm[9]; i++)
{
ecm[10 + i] = 0x00;
}
}
else
{
create_hash(data, 24, mask, hashModeEcm);
// Unmask body
for (i = 0; i < 16; i++)
{
ecm[startOffset + destPos[i]] ^= mask[i & 0x0F];
}
}
// Fix header
ecm[3] &= 0x0F;
ecm[3] |= 0x30;
ecm[8] = 0x00;
ecm[28 + extraBytesLen] = 0x00;
// Fix CRC (optional)
l = (((ecm[1] << 8) + ecm[2]) & 0xFFF) + 3 - 4;
crc = ccitt32_crc(ecm, l);
ecm[l + 0] = crc >> 24;
ecm[l + 1] = crc >> 16;
ecm[l + 2] = crc >> 8;
ecm[l + 3] = crc >> 0;
for (i = 0; i < 11; i++)
{
seedEcmCw[i] = ecm[startOffset + seedCwPos[i]];
}
return hashModeCw;
}
static void create_cw(uint8_t *seed, uint8_t lenSeed, uint8_t *baseCw, uint8_t val, uint8_t *seedEcmCw,
uint8_t *cw, int modeDesCsa, int hashMode, int modeCW, uint8_t *ecmBody)
{
int i;
uint8_t data[64], hash[16], lenData;
uint8_t tableFixParity[] =
{
0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x07, 0x07,
0x08, 0x08, 0x0B, 0x0B, 0x0D, 0x0D, 0x0E, 0x0E,
0x10, 0x10, 0x13, 0x13, 0x15, 0x15, 0x16, 0x16,
0x19, 0x19, 0x1A, 0x1A, 0x1C, 0x1C, 0x1F, 0x1F,
0x20, 0x20, 0x23, 0x23, 0x25, 0x25, 0x26, 0x26,
0x29, 0x29, 0x2A, 0x2A, 0x2C, 0x2C, 0x2F, 0x2F,
0x31, 0x31, 0x32, 0x32, 0x34, 0x34, 0x37, 0x37,
0x38, 0x38, 0x3B, 0x3B, 0x3D, 0x3D, 0x3E, 0x3E,
0x40, 0x40, 0x43, 0x43, 0x45, 0x45, 0x46, 0x46,
0x49, 0x49, 0x4A, 0x4A, 0x4C, 0x4C, 0x4F, 0x4F,
0x51, 0x51, 0x52, 0x52, 0x54, 0x54, 0x57, 0x57,
0x58, 0x58, 0x5B, 0x5B, 0x5D, 0x5D, 0x5E, 0x5E,
0x61, 0x61, 0x62, 0x62, 0x64, 0x64, 0x67, 0x67,
0x68, 0x68, 0x6B, 0x6B, 0x6D, 0x6D, 0x6E, 0x6E,
0x70, 0x70, 0x73, 0x73, 0x75, 0x75, 0x76, 0x76,
0x79, 0x79, 0x7A, 0x7A, 0x7C, 0x7C, 0x7F, 0x7F,
0x80, 0x80, 0x83, 0x83, 0x85, 0x85, 0x86, 0x86,
0x89, 0x89, 0x8A, 0x8A, 0x8C, 0x8C, 0x8F, 0x8F,
0x91, 0x91, 0x92, 0x92, 0x94, 0x94, 0x97, 0x97,
0x98, 0x98, 0x9B, 0x9B, 0x9D, 0x9D, 0x9E, 0x9E,
0xA1, 0xA1, 0xA2, 0xA2, 0xA4, 0xA4, 0xA7, 0xA7,
0xA8, 0xA8, 0xAB, 0xAB, 0xAD, 0xAD, 0xAE, 0xAE,
0xB0, 0xB0, 0xB3, 0xB3, 0xB5, 0xB5, 0xB6, 0xB6,
0xB9, 0xB9, 0xBA, 0xBA, 0xBC, 0xBC, 0xBF, 0xBF,
0xC1, 0xC1, 0xC2, 0xC2, 0xC4, 0xC4, 0xC7, 0xC7,
0xC8, 0xC8, 0xCB, 0xCB, 0xCD, 0xCD, 0xCE, 0xCE,
0xD0, 0xD0, 0xD3, 0xD3, 0xD5, 0xD5, 0xD6, 0xD6,
0xD9, 0xD9, 0xDA, 0xDA, 0xDC, 0xDC, 0xDF, 0xDF,
0xE0, 0xE0, 0xE3, 0xE3, 0xE5, 0xE5, 0xE6, 0xE6,
0xE9, 0xE9, 0xEA, 0xEA, 0xEC, 0xEC, 0xEF, 0xEF,
0xF1, 0xF1, 0xF2, 0xF2, 0xF4, 0xF4, 0xF7, 0xF7,
0xF8, 0xF8, 0xFB, 0xFB, 0xFD, 0xFD, 0xFE, 0xFE
};
if (modeCW == 0x03)
{
create_data_cw_mode_03(seed, lenSeed, baseCw, val, ecmBody, data);
create_hash_mode_03(data, hash);
cw[0] = hash[0x09];
cw[1] = hash[0x01];
cw[2] = hash[0x0F];
cw[3] = hash[0x0E];
cw[4] = hash[0x04];
cw[5] = hash[0x02];
cw[6] = hash[0x05];
cw[7] = hash[0x0D];
}
else if (modeCW == 0x04)
{
create_data_cw_mode_04(seed, lenSeed, baseCw, val, ecmBody, data);
create_hash_mode_04(data, hash);
cw[0] = hash[0x08];
cw[1] = hash[0x0F];
cw[2] = hash[0x02];
cw[3] = hash[0x0A];
cw[4] = hash[0x06];
cw[5] = hash[0x03];
cw[6] = hash[0x09];
cw[7] = hash[0x0D];
}
else
{
lenData = create_data_cw(seed, lenSeed, baseCw, val, seedEcmCw, data);
create_hash(data, lenData, hash, hashMode);
for (i = 0; i < 8; i++)
{
cw[i] = hash[i];
}
}
if (modeDesCsa == 0) // DES - Fix Parity Bits
{
for (i = 0; i < 8; i++)
{
cw[i] = tableFixParity[cw[i]];
}
}
else if (modeDesCsa == 1) // CSA - Fix Checksums
{
cw[3] = cw[0] + cw[1] + cw[2];
cw[7] = cw[4] + cw[5] + cw[6];
}
}
static uint32_t create_channel_hash(uint16_t caid, uint16_t tsid, uint16_t onid, uint32_t ens)
{
uint8_t buffer[8];
uint32_t channel_hash = 0;
if (ens)
{
i2b_buf(2, tsid, buffer);
i2b_buf(2, onid, buffer + 2);
i2b_buf(4, ens, buffer + 4);
channel_hash = crc32(caid, buffer, sizeof(buffer));
}
return channel_hash;
}
static uint16_t get_channel_group(uint32_t channel_hash)
{
uint8_t tmp[2];
uint16_t group = 0;
if (channel_hash && emu_find_key('P', channel_hash, 0x00000000, "GROUP", tmp, 2, 0, 0, 0, NULL))
{
group = b2i(2, tmp);
}
return group;
}
static inline int8_t get_ecm_key(uint8_t *key, uint32_t provider, uint32_t ignore_mask, uint8_t keyIndex, uint32_t keyRef)
{
return emu_find_key('P', provider, ignore_mask, keyIndex == 1 ? "01" : "00", key, 7, 0, keyRef, 0, NULL);
}
static inline int8_t get_emm_key(uint8_t *key, char *uniqueAddress, uint32_t keyRef, uint32_t *groupId)
{
return emu_find_key('P', 0, 0xFFFFFFFF, uniqueAddress, key, 7, 0, keyRef, 0, groupId);
}
static const uint8_t PowerVu_A0_S_1[16] =
{
0x33, 0xA4, 0x44, 0x3C, 0xCA, 0x2E, 0x75, 0x7B,
0xBC, 0xE6, 0xE5, 0x35, 0xA0, 0x55, 0xC9, 0xA2
};
static const uint8_t PowerVu_A0_S_2[16] =
{
0x5A, 0xB0, 0x2C, 0xBC, 0xDA, 0x32, 0xE6, 0x92,
0x40, 0x53, 0x6E, 0xF9, 0x69, 0x11, 0x1E, 0xFB
};
static const uint8_t PowerVu_A0_S_3[16] =
{
0x4E, 0x18, 0x9B, 0x19, 0x79, 0xFB, 0x01, 0xFA,
0xE3, 0xE1, 0x28, 0x3D, 0x32, 0xE4, 0x92, 0xEA
};
static const uint8_t PowerVu_A0_S_4[16] =
{
0x05, 0x6F, 0x37, 0x66, 0x35, 0xE1, 0x58, 0xD0,
0xB4, 0x6A, 0x97, 0xAE, 0xD8, 0x91, 0x27, 0x56
};
static const uint8_t PowerVu_A0_S_5[16] =
{
0x7B, 0x26, 0xAD, 0x34, 0x3D, 0x77, 0x39, 0x51,
0xE0, 0xE0, 0x48, 0x8C, 0x39, 0xF5, 0xE8, 0x47
};
static const uint8_t PowerVu_A0_S_6[16] =
{
0x74, 0xFA, 0x4D, 0x79, 0x42, 0x39, 0xD1, 0xA4,
0x99, 0xA3, 0x97, 0x07, 0xDF, 0x14, 0x3A, 0xC4
};
static const uint8_t PowerVu_A0_S_7[16] =
{
0xC6, 0x1E, 0x3C, 0x24, 0x11, 0x08, 0x5D, 0x6A,
0xEB, 0x97, 0xB9, 0x25, 0xA7, 0xFA, 0xE9, 0x1A
};
static const uint8_t PowerVu_A0_S_8[16] =
{
0x9A, 0xAD, 0x72, 0xD7, 0x7C, 0x68, 0x3B, 0x55,
0x1D, 0x4A, 0xA2, 0xB0, 0x38, 0xB9, 0x56, 0xD0
};
static const uint8_t PowerVu_A0_S_9[32] =
{
0x61, 0xDA, 0x5F, 0xB7, 0xEB, 0xC6, 0x3F, 0x6C,
0x09, 0xF3, 0x64, 0x38, 0x33, 0x08, 0xAA, 0x15,
0xCC, 0xEF, 0x22, 0x64, 0x01, 0x2C, 0x12, 0xDE,
0xF4, 0x6E, 0x3C, 0xCD, 0x1A, 0x64, 0x63, 0x7C
};
static const uint8_t PowerVu_00_S_1[16] =
{
0x97, 0x13, 0xEB, 0x6B, 0x04, 0x5E, 0x60, 0x3A,
0xD9, 0xCC, 0x91, 0xC2, 0x5A, 0xFD, 0xBA, 0x0C
};
static const uint8_t PowerVu_00_S_2[16] =
{
0x61, 0x3C, 0x03, 0xB0, 0xB5, 0x6F, 0xF8, 0x01,
0xED, 0xE0, 0xE5, 0xF3, 0x78, 0x0F, 0x0A, 0x73
};
static const uint8_t PowerVu_00_S_3[16] =
{
0xFD, 0xDF, 0xD2, 0x97, 0x06, 0x14, 0x91, 0xB5,
0x36, 0xAD, 0xBC, 0xE1, 0xB3, 0x00, 0x66, 0x41
};
static const uint8_t PowerVu_00_S_4[16] =
{
0x8B, 0xD9, 0x18, 0x0A, 0xED, 0xEE, 0x61, 0x34,
0x1A, 0x79, 0x80, 0x8C, 0x1E, 0x7F, 0xC5, 0x9F
};
static const uint8_t PowerVu_00_S_5[16] =
{
0xB0, 0xA1, 0xF2, 0xB8, 0xEA, 0x72, 0xDD, 0xD3,
0x30, 0x65, 0x2B, 0x1E, 0xE9, 0xE1, 0x45, 0x29
};
static const uint8_t PowerVu_00_S_6[16] =
{
0x5D, 0xCA, 0x53, 0x75, 0xB2, 0x24, 0xCE, 0xAF,
0x21, 0x54, 0x9E, 0xBE, 0x02, 0xA9, 0x4C, 0x5D
};
static const uint8_t PowerVu_00_S_7[16] =
{
0x42, 0x66, 0x72, 0x83, 0x1B, 0x2D, 0x22, 0xC9,
0xF8, 0x4D, 0xBA, 0xCD, 0xBB, 0x20, 0xBD, 0x6B
};
static const uint8_t PowerVu_00_S_8[16] =
{
0xC4, 0x0C, 0x6B, 0xD3, 0x6D, 0x94, 0x7E, 0x53,
0xCE, 0x96, 0xAC, 0x40, 0x2C, 0x7A, 0xD3, 0xA9
};
static const uint8_t PowerVu_00_S_9[32] =
{
0x31, 0x82, 0x4F, 0x9B, 0xCB, 0x6F, 0x9D, 0xB7,
0xAE, 0x68, 0x0B, 0xA0, 0x93, 0x15, 0x32, 0xE2,
0xED, 0xE9, 0x47, 0x29, 0xC2, 0xA8, 0x92, 0xEF,
0xBA, 0x27, 0x22, 0x57, 0x76, 0x54, 0xC0, 0x59
};
static uint8_t powervu_sbox(uint8_t *input, uint8_t mode)
{
uint8_t s_index, bit, last_index, last_bit;
uint8_t const *Sbox1, *Sbox2, *Sbox3, *Sbox4, *Sbox5, *Sbox6, *Sbox7, *Sbox8, *Sbox9;
if (mode)
{
Sbox1 = PowerVu_A0_S_1;
Sbox2 = PowerVu_A0_S_2;
Sbox3 = PowerVu_A0_S_3;
Sbox4 = PowerVu_A0_S_4;
Sbox5 = PowerVu_A0_S_5;
Sbox6 = PowerVu_A0_S_6;
Sbox7 = PowerVu_A0_S_7;
Sbox8 = PowerVu_A0_S_8;
Sbox9 = PowerVu_A0_S_9;
}
else
{
Sbox1 = PowerVu_00_S_1;
Sbox2 = PowerVu_00_S_2;
Sbox3 = PowerVu_00_S_3;
Sbox4 = PowerVu_00_S_4;
Sbox5 = PowerVu_00_S_5;
Sbox6 = PowerVu_00_S_6;
Sbox7 = PowerVu_00_S_7;
Sbox8 = PowerVu_00_S_8;
Sbox9 = PowerVu_00_S_9;
}
bit = (get_bit(input[2], 0) << 2) | (get_bit(input[3], 4) << 1) | (get_bit(input[5], 3));
s_index = (get_bit(input[0], 0) << 3) | (get_bit(input[2], 6) << 2) | (get_bit(input[2], 4) << 1) | (get_bit(input[5], 7));
last_bit = get_bit(Sbox1[s_index], 7 - bit);
bit = (get_bit(input[5], 0) << 2) | (get_bit(input[4], 0) << 1) | (get_bit(input[6], 2));
s_index = (get_bit(input[2], 1) << 3) | (get_bit(input[2], 2) << 2) | (get_bit(input[5], 5) << 1) | (get_bit(input[5], 1));
last_bit = last_bit | (get_bit(Sbox2[s_index], 7 - bit) << 1);
bit = (get_bit(input[6], 0) << 2) | (get_bit(input[1], 7) << 1) | (get_bit(input[6], 7));
s_index = (get_bit(input[1], 3) << 3) | (get_bit(input[3], 7) << 2) | (get_bit(input[1], 5) << 1) | (get_bit(input[5], 2));
last_bit = last_bit | (get_bit(Sbox3[s_index], 7 - bit) << 2);
bit = (get_bit(input[1], 0) << 2) | (get_bit(input[2], 7) << 1) | (get_bit(input[2], 5));
s_index = (get_bit(input[6], 3) << 3) | (get_bit(input[6], 4) << 2) | (get_bit(input[6], 6) << 1) | (get_bit(input[3], 5));
last_index = get_bit(Sbox4[s_index], 7 - bit);
bit = (get_bit(input[3], 3) << 2) | (get_bit(input[4], 6) << 1) | (get_bit(input[3], 2));
s_index = (get_bit(input[3], 1) << 3) | (get_bit(input[4], 5) << 2) | (get_bit(input[3], 0) << 1) | (get_bit(input[4], 7));
last_index = last_index | (get_bit(Sbox5[s_index], 7 - bit) << 1);
bit = (get_bit(input[5], 4) << 2) | (get_bit(input[4], 4) << 1) | (get_bit(input[1], 2));
s_index = (get_bit(input[2], 3) << 3) | (get_bit(input[6], 5) << 2) | (get_bit(input[1], 4) << 1) | (get_bit(input[4], 1));
last_index = last_index | (get_bit(Sbox6[s_index], 7 - bit) << 2);
bit = (get_bit(input[0], 6) << 2) | (get_bit(input[0], 7) << 1) | (get_bit(input[0], 4));
s_index = (get_bit(input[0], 5) << 3) | (get_bit(input[0], 3) << 2) | (get_bit(input[0], 1) << 1) | (get_bit(input[0], 2));
last_index = last_index | (get_bit(Sbox7[s_index], 7 - bit) << 3);
bit = (get_bit(input[4], 2) << 2) | (get_bit(input[4], 3) << 1) | (get_bit(input[1], 1));
s_index = (get_bit(input[1], 6) << 3) | (get_bit(input[6], 1) << 2) | (get_bit(input[5], 6) << 1) | (get_bit(input[3], 6));
last_index = last_index | (get_bit(Sbox8[s_index], 7 - bit) << 4);
return (get_bit(Sbox9[last_index & 0x1F], 7 - last_bit) & 1) ? 1 : 0;
}
static void powervu_decrypt(uint8_t *data, uint32_t length, uint8_t *key, uint8_t sbox)
{
uint32_t i;
int32_t j, k;
uint8_t curByte, tmpBit;
for (i = 0; i < length; i++)
{
curByte = data[i];
for (j = 7; j >= 0; j--)
{
data[i] = set_bit(data[i], j, (get_bit(curByte, j) ^ powervu_sbox(key, sbox)) ^ get_bit(key[0], 7));
tmpBit = get_bit(data[i], j) ^ (get_bit(key[6], 0));
if (tmpBit)
{
key[3] ^= 0x10;
}
for (k = 6; k > 0; k--)
{
key[k] = (key[k] >> 1) | (key[k - 1] << 7);
}
key[0] = (key[0] >> 1);
key[0] = set_bit(key[0], 7, tmpBit);
}
}
}
static void expand_des_key(unsigned char *key)
{
uint8_t i, j, parity;
uint8_t tmpKey[7];
memcpy(tmpKey, key, 7);
key[0] = (tmpKey[0] & 0xFE);
key[1] = ((tmpKey[0] << 7) | ((tmpKey[1] >> 1) & 0xFE));
key[2] = ((tmpKey[1] << 6) | ((tmpKey[2] >> 2) & 0xFE));
key[3] = ((tmpKey[2] << 5) | ((tmpKey[3] >> 3) & 0xFE));
key[4] = ((tmpKey[3] << 4) | ((tmpKey[4] >> 4) & 0xFE));
key[5] = ((tmpKey[4] << 3) | ((tmpKey[5] >> 5) & 0xFE));
key[6] = ((tmpKey[5] << 2) | ((tmpKey[6] >> 6) & 0xFE));
key[7] = (tmpKey[6] << 1);
for (i = 0; i < 8; i++)
{
parity = 1;
for (j = 1; j < 8; j++)
{
if ((key[i] >> j) & 0x1)
{
parity = ~parity & 0x01;
}
}
key[i] |= parity;
}
}
static uint8_t get_conv_cw_index(uint8_t ecmTag)
{
switch (ecmTag)
{
case PVU_CONVCW_VID_ECM:
return PVU_CW_VID;
case PVU_CONVCW_HSD_ECM:
return PVU_CW_HSD;
case PVU_CONVCW_A1_ECM:
return PVU_CW_A1;
case PVU_CONVCW_A2_ECM:
return PVU_CW_A2;
case PVU_CONVCW_A3_ECM:
return PVU_CW_A3;
case PVU_CONVCW_A4_ECM:
return PVU_CW_A4;
case PVU_CONVCW_UTL_ECM:
return PVU_CW_UTL;
case PVU_CONVCW_VBI_ECM:
return PVU_CW_VBI;
default:
return PVU_CW_VBI;
}
}
static uint16_t get_seed_iv(uint8_t seedType, uint8_t *ecm)
{
switch (seedType)
{
case PVU_CW_VID:
return ((ecm[0x10] & 0x1F) << 3) | 0;
case PVU_CW_HSD:
return ((ecm[0x12] & 0x1F) << 3) | 2;
case PVU_CW_A1:
return ((ecm[0x11] & 0x3F) << 3) | 1;
case PVU_CW_A2:
return ((ecm[0x13] & 0x3F) << 3) | 1;
case PVU_CW_A3:
return ((ecm[0x19] & 0x3F) << 3) | 1;
case PVU_CW_A4:
return ((ecm[0x1A] & 0x3F) << 3) | 1;
case PVU_CW_UTL:
return ((ecm[0x14] & 0x0F) << 3) | 4;
case PVU_CW_VBI:
return (((ecm[0x15] & 0xF8) >> 3) << 3) | 5;
default:
return 0;
}
}
static uint8_t expand_seed(uint8_t seedType, uint8_t *seed)
{
uint8_t seedLength = 0, i;
switch (seedType)
{
case PVU_CW_VID:
case PVU_CW_HSD:
seedLength = 4;
break;
case PVU_CW_A1:
case PVU_CW_A2:
case PVU_CW_A3:
case PVU_CW_A4:
seedLength = 3;
break;
case PVU_CW_UTL:
case PVU_CW_VBI:
seedLength = 2;
break;
default:
return seedLength;
}
for (i = seedLength; i < 7; i++)
{
seed[i] = seed[i % seedLength];
}
return seedLength;
}
static void calculate_seed(uint8_t seedType, uint8_t *ecm, uint8_t *seedBase,
uint8_t *key, uint8_t *seed, uint8_t sbox)
{
uint16_t tmpSeed;
tmpSeed = get_seed_iv(seedType, ecm + 23);
seed[0] = (tmpSeed >> 2) & 0xFF;
seed[1] = ((tmpSeed & 0x3) << 6) | (seedBase[0] >> 2);
seed[2] = ( seedBase[0] << 6) | (seedBase[1] >> 2);
seed[3] = ( seedBase[1] << 6) | (seedBase[2] >> 2);
seed[4] = ( seedBase[2] << 6) | (seedBase[3] >> 2);
seed[5] = ( seedBase[3] << 6);
powervu_decrypt(seed, 6, key, sbox);
seed[0] = (seed[1] << 2) | (seed[2] >> 6);
seed[1] = (seed[2] << 2) | (seed[3] >> 6);
seed[2] = (seed[3] << 2) | (seed[4] >> 6);
seed[3] = (seed[4] << 2) | (seed[5] >> 6);
}
static void calculate_cw(uint8_t seedType, uint8_t *seed, uint8_t csaUsed, uint8_t *convolvedCw,
uint8_t *cw, uint8_t *baseCw, uint8_t *seedEcmCw, uint8_t hashModeCw,
uint8_t needsUnmasking, uint8_t xorMode, int modeCW, uint8_t* ecmBody)
{
int32_t k;
uint8_t seedLength, val = 0;
seedLength = expand_seed(seedType, seed);
if (needsUnmasking && (((modeCW >= 0x00) && (hashModeCw > 0) && (hashModeCw <= 0x27) &&
(hashModeCw != 0x0B) && (hashModeCw != 0x0C) && (hashModeCw != 0x0D) && (hashModeCw != 0x0E)) ||
(modeCW == 0x03) || (modeCW == 0x04)))
{
switch (seedType)
{
case PVU_CW_VID:
val = 0;
break;
case PVU_CW_A1:
case PVU_CW_A2:
case PVU_CW_A3:
case PVU_CW_A4:
val = 1;
break;
case PVU_CW_HSD:
val = 2;
break;
case PVU_CW_UTL:
val = 4;
break;
case PVU_CW_VBI:
val = 5;
break;
}
create_cw(seed, seedLength, baseCw, val, seedEcmCw, cw, csaUsed, hashModeCw, modeCW, ecmBody);
if (csaUsed)
{
cw[0] = cw[0] ^ convolvedCw[0];
cw[1] = cw[1] ^ convolvedCw[1];
cw[2] = cw[2] ^ convolvedCw[2];
cw[3] = cw[3] ^ convolvedCw[3];
cw[4] = cw[4] ^ convolvedCw[4];
cw[5] = cw[5] ^ convolvedCw[5];
cw[6] = cw[6] ^ convolvedCw[6];
cw[7] = cw[7] ^ convolvedCw[7];
cw[3] = cw[0] + cw[1] + cw[2];
cw[7] = cw[4] + cw[5] + cw[6];
}
}
else
{
if (csaUsed)
{
for (k = 0; k < 7; k++)
{
seed[k] ^= baseCw[k];
}
cw[0] = seed[0] ^ convolvedCw[0];
cw[1] = seed[1] ^ convolvedCw[1];
cw[2] = seed[2] ^ convolvedCw[2];
cw[3] = seed[3] ^ convolvedCw[3];
cw[4] = seed[3] ^ convolvedCw[4];
cw[5] = seed[4] ^ convolvedCw[5];
cw[6] = seed[5] ^ convolvedCw[6];
cw[7] = seed[6] ^ convolvedCw[7];
}
else
{
if (xorMode == 0)
{
for (k = 0; k < 7; k++)
{
cw[k] = seed[k] ^ baseCw[k];
}
}
if (xorMode == 1)
{
for (k = 0; k < 3; k++)
{
cw[k] = seed[k] ^ baseCw[k];
}
for (k = 3; k < 7; k++)
{
cw[k] = baseCw[k];
}
}
expand_des_key(cw);
}
}
}
#ifdef MODULE_STREAMRELAY
int8_t powervu_ecm(uint8_t *ecm, uint8_t *dw, EXTENDED_CW *cw_ex, uint16_t srvid, uint16_t caid, uint16_t tsid, uint16_t onid, uint32_t ens, emu_stream_client_key_data *cdata)
#else
int8_t powervu_ecm(uint8_t *ecm, uint8_t *dw, EXTENDED_CW *cw_ex, uint16_t srvid, uint16_t caid, uint16_t tsid, uint16_t onid, uint32_t ens)
#endif
{
uint32_t i, j, k;
uint32_t ecmCrc32, keyRef0, keyRef1, keyRef2, channel_hash, group_id = 0;
uint16_t ecmLen = SCT_LEN(ecm);
uint16_t nanoLen, channelId, ecmSrvid;
uint8_t keyIndex, sbox, decrypt_ok, calculateAll, hashModeCw = 0, needsUnmasking, xorMode;
uint8_t nanoCmd, nanoChecksum, keyType, fixedKey, oddKey, bid, csaUsed, modeCW = 0, offsetBody;
uint8_t ecmKey[7], tmpEcmKey[7], seedBase[4], baseCw[7], seed[8][8], cw[8][8], convolvedCw[8][8];
uint8_t ecmPart1[14], ecmPart2[27], unmaskedEcm[ecmLen], seedEcmCw[16];
//char tmpBuffer1[512];
char tmpBuffer2[17];
#ifdef MODULE_STREAMRELAY
emu_stream_cw_item *cw_item;
int8_t update_global_key = 0;
int8_t update_global_keys[EMU_STREAM_SERVER_MAX_CONNECTIONS];
memset(update_global_keys, 0, sizeof(update_global_keys));
#else
#define EMU_STREAM_MAX_AUDIO_SUB_TRACKS 4
#endif
if (ecmLen < 7)
{
return EMU_NOT_SUPPORTED;
}
needsUnmasking = (ecm[3] & 0xF0) == 0x50;
//cs_log_dbg(D_ATR, "ecm1: %s", cs_hexdump(0, ecm, ecmLen, tmpBuffer1, sizeof(tmpBuffer1)));
if (needsUnmasking)
{
hashModeCw = unmask_ecm(ecm, seedEcmCw, &modeCW);
}
//cs_log_dbg(D_ATR, "needsUnmasking=%d", needsUnmasking);
//cs_log_dbg(D_ATR, "ecm2: %s", cs_hexdump(0, ecm, ecmLen, tmpBuffer1, sizeof(tmpBuffer1)));
memcpy(unmaskedEcm, ecm, ecmLen);
ecmCrc32 = b2i(4, ecm + ecmLen - 4);
if (ccitt32_crc(ecm, ecmLen - 4) != ecmCrc32)
{
return EMU_CHECKSUM_ERROR;
}
ecmLen -= 4;
for (i = 0; i < 8; i++)
{
memset(convolvedCw[i], 0, 8);
}
for (i = 3; i + 3 < ecmLen; )
{
nanoLen = (((ecm[i] & 0x0F) << 8) | ecm[i + 1]);
i += 2;
if (nanoLen > 0)
{
nanoLen--;
}
nanoCmd = ecm[i++];
if (i + nanoLen > ecmLen)
{
return EMU_NOT_SUPPORTED;
}
switch (nanoCmd)
{
case 0x27:
if (nanoLen < 15)
{
break;
}
nanoChecksum = 0;
for (j = 4; j < 15; j++)
{
nanoChecksum += ecm[i + j];
}
if (nanoChecksum != 0)
{
break;
}
keyType = get_conv_cw_index(ecm[i + 4]);
memcpy(convolvedCw[keyType], &ecm[i + 6], 8);
break;
default:
break;
}
i += nanoLen;
}
for (i = 3; i + 3 < ecmLen; )
{
nanoLen = (((ecm[i] & 0x0F) << 8) | ecm[i + 1]);
i += 2;
if (nanoLen > 0)
{
nanoLen--;
}
nanoCmd = ecm[i++];
if (i + nanoLen > ecmLen)
{
return EMU_NOT_SUPPORTED;
}
switch (nanoCmd)
{
case 0x20:
{
if (nanoLen < 54)
{
break;
}
offsetBody = i + 4 + ecm[i + 3];
i += ecm[i + 3]; // Extra Data Length
csaUsed = get_bit(ecm[i + 7], 7);
fixedKey = !get_bit(ecm[i + 6], 5);
oddKey = get_bit(ecm[i + 6], 4);
xorMode = get_bit(ecm[i + 6], 0);
bid = (get_bit(ecm[i + 7], 1) << 1) | get_bit(ecm[i + 7], 0);
sbox = get_bit(ecm[i + 6], 3);
keyIndex = (fixedKey << 3) | (bid << 2) | oddKey;
channelId = b2i(2, ecm + i + 23);
ecmSrvid = (channelId >> 4) | ((channelId & 0xF) << 12);
cs_log_dbg(D_ATR, "csaUsed: %d, xorMode: %d, ecmSrvid: %04X (%d), hashModeCw: %d, modeCW: %d",
csaUsed, xorMode, ecmSrvid, srvid, hashModeCw, modeCW);
channel_hash = create_channel_hash(caid, tsid, onid, ens);
group_id = get_channel_group(channel_hash);
cs_log_dbg(D_ATR, "channel hash: %08X, group id: %04X", channel_hash, group_id);
decrypt_ok = 0;
memcpy(ecmPart1, ecm + i + 8, 14);
memcpy(ecmPart2, ecm + i + 27, 27);
keyRef0 = 0;
keyRef1 = 0;
keyRef2 = 0;
do
{
if (!group_id || !get_ecm_key(ecmKey, group_id << 16, 0x0000FFFF, keyIndex, keyRef0++))
{
if (!get_ecm_key(ecmKey, ecmSrvid, 0xFFFF0000, keyIndex, keyRef1++))
{
if (!get_ecm_key(ecmKey, channelId, 0xFFFF0000, keyIndex, keyRef2++))
{
cs_log("Key not found or invalid: P ****%04X %02X", ecmSrvid, keyIndex);
if (group_id) // Print only if there is a matching "GROUP" entry
{
cs_log("Key not found or invalid: P %04XFFFF %02X", group_id, keyIndex);
}
return EMU_KEY_NOT_FOUND;
}
}
}
powervu_decrypt(ecm + i + 8, 14, ecmKey, sbox);
if ((ecm[i + 6] != ecm[i + 6 + 7]) || (ecm[i + 6 + 8] != ecm[i + 6 + 15]))
{
memcpy(ecm + i + 8, ecmPart1, 14);
continue;
}
memcpy(tmpEcmKey, ecmKey, 7);
powervu_decrypt(ecm + i + 27, 27, ecmKey, sbox);
if ((ecm[i + 23] != ecm[i + 23 + 29]) || (ecm[i + 23 + 1] != ecm[i + 23 + 30]))
{
memcpy(ecm + i + 8, ecmPart1, 14);
memcpy(ecm + i + 27, ecmPart2, 27);
continue;
}
decrypt_ok = 1;
}
while (!decrypt_ok);
memcpy(seedBase, ecm + i + 6 + 2, 4);
#ifdef MODULE_STREAMRELAY
if (cdata == NULL)
{
SAFE_MUTEX_LOCK(&emu_fixed_key_srvid_mutex);
for (j = 0; j < EMU_STREAM_SERVER_MAX_CONNECTIONS; j++)
{
if (!stream_server_has_ecm[j] && emu_stream_cur_srvid[j] == srvid)
{
update_global_key = 1;
update_global_keys[j] = 1;
}
}
SAFE_MUTEX_UNLOCK(&emu_fixed_key_srvid_mutex);
}
calculateAll = cdata != NULL || update_global_key || cw_ex != NULL;
#else
calculateAll = cw_ex != NULL;
#endif
if (calculateAll) // Calculate all seeds
{
for (j = 0; j < 8; j++)
{
memcpy(ecmKey, tmpEcmKey, 7);
calculate_seed(j, ecm + i, seedBase, ecmKey, seed[j], sbox);
}
}
else // Calculate only video seed
{
memcpy(ecmKey, tmpEcmKey, 7);
calculate_seed(PVU_CW_VID, ecm + i, seedBase, ecmKey, seed[PVU_CW_VID], sbox);
}
memcpy(baseCw, ecm + i + 6 + 8, 7);
if (calculateAll) // Calculate all CWs
{
for (j = 0; j < 8; j++)
{
calculate_cw(j, seed[j], csaUsed, convolvedCw[j], cw[j], baseCw, seedEcmCw,
hashModeCw, needsUnmasking, xorMode, modeCW, unmaskedEcm + offsetBody);
if (csaUsed)
{
for (k = 0; k < 8; k += 4)
{
cw[j][k + 3] = ((cw[j][k] + cw[j][k + 1] + cw[j][k + 2]) & 0xFF);
}
}
cs_log_dbg(D_ATR, "calculated cw %d: %s", j,
cs_hexdump(0, cw[j], 8, tmpBuffer2, sizeof(tmpBuffer2)));
}
//cs_log_dbg(D_ATR, "csaUsed=%d, cw: %s cdata=%x, cw_ex=%x",
// csaUsed, cs_hexdump(3, cw[0], 8, tmpBuffer1, sizeof(tmpBuffer1)),
// (unsigned int)cdata, (unsigned int)cw_ex);
#ifdef MODULE_STREAMRELAY
if (update_global_key)
{
for (j = 0; j < EMU_STREAM_SERVER_MAX_CONNECTIONS; j++)
{
if (update_global_keys[j])
{
cw_item = (emu_stream_cw_item *)malloc(sizeof(emu_stream_cw_item));
if (cw_item != NULL)
{
cw_item->csa_used = csaUsed;
cw_item->is_even = ecm[0] == 0x80 ? 1 : 0;
cs_ftime(&cw_item->write_time);
add_ms_to_timeb(&cw_item->write_time, cfg.emu_stream_ecm_delay);
memcpy(cw_item->cw, cw, sizeof(cw));
ll_append(ll_emu_stream_delayed_keys[j], cw_item);
}
}
}
}
if (cdata != NULL)
{
for (j = 0; j < EMU_STREAM_MAX_AUDIO_SUB_TRACKS + 2; j++)
{
if (csaUsed)
{
if (ecm[0] == 0x80)
{
if (has_dvbcsa_ecm)
{
dvbcsa_bs_key_set(cw[j], key_data[cdata->connid].key[j][EVEN]);
}
}
else
{
if (has_dvbcsa_ecm)
{
dvbcsa_bs_key_set(cw[j], key_data[cdata->connid].key[j][ODD]);
}
}
cdata->csa_used = 1;
}
else
{
if (ecm[0] == 0x80)
{
des_set_key(cw[j], cdata->pvu_des_ks[j][0]);
}
else
{
des_set_key(cw[j], cdata->pvu_des_ks[j][1]);
}
cdata->csa_used = 0;
}
}
}
#endif
if (cw_ex != NULL)
{
cw_ex->mode = CW_MODE_MULTIPLE_CW;
if (csaUsed)
{
cw_ex->algo = CW_ALGO_CSA;
cw_ex->algo_mode = CW_ALGO_MODE_CBC;
}
else
{
cw_ex->algo = CW_ALGO_DES;
cw_ex->algo_mode = CW_ALGO_MODE_ECB;
}
for (j = 0; j < EMU_STREAM_MAX_AUDIO_SUB_TRACKS; j++)
{
memset(cw_ex->audio[j], 0, 16);
if (ecm[0] == 0x80)
{
memcpy(cw_ex->audio[j], cw[PVU_CW_A1 + j], 8);
}
else
{
memcpy(&cw_ex->audio[j][8], cw[PVU_CW_A1 + j], 8);
}
}
memset(cw_ex->data, 0, 16);
if (ecm[0] == 0x80)
{
memcpy(cw_ex->data, cw[PVU_CW_HSD], 8);
}
else
{
memcpy(&cw_ex->data[8], cw[PVU_CW_HSD], 8);
}
}
}
else // Calculate only video CW
{
calculate_cw(PVU_CW_VID, seed[PVU_CW_VID], csaUsed, convolvedCw[PVU_CW_VID],
cw[PVU_CW_VID], baseCw, seedEcmCw, hashModeCw, needsUnmasking,
xorMode, modeCW, unmaskedEcm + offsetBody);
if (csaUsed)
{
for (k = 0; k < 8; k += 4)
{
cw[PVU_CW_VID][k + 3] = ((cw[PVU_CW_VID][k] + cw[PVU_CW_VID][k + 1] + cw[PVU_CW_VID][k + 2]) & 0xFF);
}
}
cs_log_dbg(D_ATR, "calculated video only cw: %s",
cs_hexdump(0, cw[PVU_CW_VID], 8, tmpBuffer2, sizeof(tmpBuffer2)));
}
memset(dw, 0, 16);
if (ecm[0] == 0x80)
{
memcpy(dw, cw[PVU_CW_VID], 8);
}
else
{
memcpy(&dw[8], cw[PVU_CW_VID], 8);
}
return EMU_OK;
}
default:
break;
}
i += nanoLen;
}
return EMU_NOT_SUPPORTED;
}
// PowerVu EMM EMU
static void create_data_unmask_emm_mode_03(uint8_t *emmBody, uint8_t *data)
{
int i;
uint8_t padding[] =
{
0xB3, 0x60, 0x35, 0xC8, 0x5C, 0x26, 0xC1, 0xD0,
0x88, 0x86, 0x57, 0xB6, 0x45, 0xA7, 0xDF, 0x7E,
0xF0, 0xA8, 0x49, 0xFB, 0x79, 0x6C, 0xAF, 0xB0
};
memcpy(data + 40, padding, 24);
for (i = 0; i < 5; i++)
{
data[0 + i * 8] = emmBody[0x18 + i * 0x1B];
data[1 + i * 8] = emmBody[0x16 + i * 0x1B];
data[2 + i * 8] = emmBody[0x07 + i * 0x1B];
data[3 + i * 8] = emmBody[0x0B + i * 0x1B];
data[4 + i * 8] = emmBody[0x06 + i * 0x1B];
data[5 + i * 8] = emmBody[0x19 + i * 0x1B];
data[6 + i * 8] = emmBody[0x15 + i * 0x1B];
data[7 + i * 8] = emmBody[0x03 + i * 0x1B];
}
}
static void create_data_unmask_emm_mode_04(uint8_t *emmBody, uint8_t *data)
{
int i;
uint8_t padding[] =
{
0x56, 0xC7, 0x05, 0x66, 0xC7, 0x4E, 0xC1, 0xA0,
0x9E, 0xD1, 0xFE, 0x92, 0xE8, 0xCD, 0x5F, 0xAF,
0xCF, 0xE5, 0xE9, 0x9E, 0x7A, 0x38, 0xAC, 0x68
};
memcpy(data + 0x28, padding, 0x18);
for (i = 0; i < 5; i++)
{
data[0 + i * 8] = emmBody[0x06 + i * 0x1B];
data[1 + i * 8] = emmBody[0x19 + i * 0x1B];
data[2 + i * 8] = emmBody[0x16 + i * 0x1B];
data[3 + i * 8] = emmBody[0x0A + i * 0x1B];
data[4 + i * 8] = emmBody[0x13 + i * 0x1B];
data[5 + i * 8] = emmBody[0x05 + i * 0x1B];
data[6 + i * 8] = emmBody[0x14 + i * 0x1B];
data[7 + i * 8] = emmBody[0x18 + i * 0x1B];
}
}
static uint8_t get_mode_unmask_emm(uint8_t *extraData)
{
uint16_t data = ((uint16_t)extraData[0] << 8) + extraData[1];
if (data == 0)
{
return 0x00;
}
switch (data & 0x0881)
{
case 0x0080:
case 0x0881:
return 0x01;
case 0x0001:
case 0x0880:
return 0x02;
case 0x0800:
case 0x0081:
return 0x03;
case 0x0000:
case 0x0801:
switch (data & 0x9020)
{
case 0x8000:
case 0x9000:
return 0x04;
case 0x0020:
case 0x9020:
return 0x05;
case 0x0000:
case 0x1000:
return 0x06;
case 0x1020:
case 0x8020:
switch (data & 0x2014)
{
case 0x2004:
case 0x2010:
return 0x07;
case 0x0000:
case 0x0004:
return 0x08;
case 0x0014:
case 0x2014:
return 0x09;
case 0x0010:
case 0x2000:
return 0x00;
}
break;
}
break;
}
return 0x00;
}
static void unmask_emm(uint8_t *emm)
{
uint32_t crc, i, l;
uint8_t hashModeEmm, modeUnmask, data[64], mask[16];
uint8_t sourcePos[] =
{
0x03, 0x0C, 0x0D, 0x11, 0x15, 0x18, 0x1D, 0x1F, 0x25, 0x2A,
0x32, 0x35, 0x3A, 0x3B, 0x3E, 0x42, 0x47, 0x48, 0x53, 0x58,
0x5C, 0x61, 0x66, 0x69, 0x71, 0x72, 0x78, 0x7B, 0x81, 0x84
};
uint8_t destPos[] =
{
0x02, 0x08, 0x0B, 0x0E, 0x13, 0x16, 0x1E, 0x23, 0x28, 0x2B,
0x2F, 0x33, 0x38, 0x3C, 0x40, 0x44, 0x4A, 0x4D, 0x54, 0x57,
0x5A, 0x63, 0x68, 0x6A, 0x70, 0x75, 0x76, 0x7D, 0x82, 0x85
};
// Create Mask for ECM decryption
create_data_ecm_emm(emm, sourcePos, 19, 30, data);
hashModeEmm = emm[8] ^ crc8_calc(data, 30);
modeUnmask = get_mode_unmask_emm(emm + 16);
if ((modeUnmask == 0x00) || (modeUnmask > 4))
{
create_hash(data, 30, mask, hashModeEmm);
// Unmask Body
for (i = 0; i < 30; i++)
{
emm[19 + destPos[i]] ^= mask[i & 0x0F];
}
}
else if (modeUnmask == 0x03)
{
for (i = 0; i < 5; i++)
{
emm[0x13 + 0x03 + i * 0x1B] -= emm[0x13 + 0x0D + i * 0x1B];
emm[0x13 + 0x06 + i * 0x1B] -= emm[0x13 + 0x1A + i * 0x1B];
emm[0x13 + 0x07 + i * 0x1B] -= emm[0x13 + 0x10 + i * 0x1B];
emm[0x13 + 0x0B + i * 0x1B] -= emm[0x13 + 0x17 + i * 0x1B];
emm[0x13 + 0x15 + i * 0x1B] -= emm[0x13 + 0x05 + i * 0x1B];
emm[0x13 + 0x16 + i * 0x1B] -= emm[0x13 + 0x0F + i * 0x1B];
emm[0x13 + 0x18 + i * 0x1B] -= emm[0x13 + 0x14 + i * 0x1B];
emm[0x13 + 0x19 + i * 0x1B] -= emm[0x13 + 0x04 + i * 0x1B];
}
create_data_unmask_emm_mode_03(emm + 0x13, data);
create_hash_mode_03(data, mask);
for (i = 0; i < 5; i++)
{
emm[0x13 + 0x14 + i * 0x1B] ^= mask[0x00];
emm[0x13 + 0x0F + i * 0x1B] ^= mask[0x01];
emm[0x13 + 0x10 + i * 0x1B] ^= mask[0x02];
emm[0x13 + 0x17 + i * 0x1B] ^= mask[0x03];
emm[0x13 + 0x1A + i * 0x1B] ^= mask[0x04];
emm[0x13 + 0x04 + i * 0x1B] ^= mask[0x05];
emm[0x13 + 0x05 + i * 0x1B] ^= mask[0x06];
emm[0x13 + 0x0D + i * 0x1B] ^= mask[0x07];
emm[0x13 + 0x09 + i * 0x1B] ^= mask[0x08];
emm[0x13 + 0x0A + i * 0x1B] ^= mask[0x09];
emm[0x13 + 0x0E + i * 0x1B] ^= mask[0x0A];
emm[0x13 + 0x11 + i * 0x1B] ^= mask[0x0B];
emm[0x13 + 0x12 + i * 0x1B] ^= mask[0x0C];
emm[0x13 + 0x13 + i * 0x1B] ^= mask[0x0D];
emm[0x13 + 0x08 + i * 0x1B] ^= mask[0x0E];
emm[0x13 + 0x0C + i * 0x1B] ^= mask[0x0F];
}
}
else if (modeUnmask == 0x04)
{
for (i = 0; i < 5; i++)
{
emm[0x13 + 0x05 + i * 0x1B] -= emm[0x13 + 0x04 + i * 0x1B];
emm[0x13 + 0x06 + i * 0x1B] -= emm[0x13 + 0x0B + i * 0x1B];
emm[0x13 + 0x0A + i * 0x1B] -= emm[0x13 + 0x17 + i * 0x1B];
emm[0x13 + 0x13 + i * 0x1B] -= emm[0x13 + 0x1A + i * 0x1B];
emm[0x13 + 0x14 + i * 0x1B] -= emm[0x13 + 0x0E + i * 0x1B];
emm[0x13 + 0x16 + i * 0x1B] -= emm[0x13 + 0x15 + i * 0x1B];
emm[0x13 + 0x18 + i * 0x1B] -= emm[0x13 + 0x08 + i * 0x1B];
emm[0x13 + 0x19 + i * 0x1B] -= emm[0x13 + 0x12 + i * 0x1B];
}
create_data_unmask_emm_mode_04(emm + 0x13, data);
create_hash_mode_04(data, mask);
for (i = 0; i < 5; i++)
{
emm[0x13 + 0x0B + i * 0x1B] ^= mask[0x00];
emm[0x13 + 0x12 + i * 0x1B] ^= mask[0x01];
emm[0x13 + 0x15 + i * 0x1B] ^= mask[0x02];
emm[0x13 + 0x17 + i * 0x1B] ^= mask[0x03];
emm[0x13 + 0x1A + i * 0x1B] ^= mask[0x04];
emm[0x13 + 0x04 + i * 0x1B] ^= mask[0x05];
emm[0x13 + 0x0E + i * 0x1B] ^= mask[0x06];
emm[0x13 + 0x08 + i * 0x1B] ^= mask[0x07];
emm[0x13 + 0x09 + i * 0x1B] ^= mask[0x08];
emm[0x13 + 0x0C + i * 0x1B] ^= mask[0x09];
emm[0x13 + 0x03 + i * 0x1B] ^= mask[0x0A];
emm[0x13 + 0x0F + i * 0x1B] ^= mask[0x0B];
emm[0x13 + 0x10 + i * 0x1B] ^= mask[0x0C];
emm[0x13 + 0x07 + i * 0x1B] ^= mask[0x0D];
emm[0x13 + 0x0D + i * 0x1B] ^= mask[0x0E];
emm[0x13 + 0x11 + i * 0x1B] ^= mask[0x0F];
}
}
else
{
cs_log("A new unknown emm mode [%d] is in use.", modeUnmask);
}
// Fix Header
emm[3] &= 0x0F;
emm[3] |= 0x10;
emm[8] = 0x00;
// Fix CRC (optional)
l = (((emm[1] << 8) + emm[2]) & 0xFFF) + 3 - 4;
crc = ccitt32_crc(emm, l);
emm[l + 0] = crc >> 24;
emm[l + 1] = crc >> 16;
emm[l + 2] = crc >> 8;
emm[l + 3] = crc >> 0;
}
static int8_t update_ecm_keys_by_group(uint32_t groupId, uint8_t keyIndex, uint8_t *Key, uint32_t uniqueAddress)
{
int8_t ret = 0;
uint8_t oldKey[7];
uint32_t foundProvider = 0, keyRef = 0;
char indexStr[3], uaInfo[13];
snprintf(indexStr, 3, "%02X", keyIndex);
snprintf(uaInfo, 13, "UA: %08X", uniqueAddress);
SAFE_MUTEX_LOCK(&emu_key_data_mutex);
while (emu_find_key('P', groupId << 16 & 0xFFFF0000, 0x0000FFFF, indexStr, oldKey, 7, 0, keyRef, 0, &foundProvider))
{
keyRef++;
if (memcmp(oldKey, Key, 7) == 0) // New ECM key already in the db
{
continue;
}
if (emu_set_key('P', foundProvider, indexStr, Key, 7, 1, uaInfo, NULL))
{
ret = 1;
}
}
SAFE_MUTEX_UNLOCK(&emu_key_data_mutex);
return ret;
}
int8_t powervu_emm(uint8_t *emm, uint32_t *keysAdded)
{
uint8_t emmInfo, emmType, decryptOk = 0;
uint8_t emmKey[7], tmpEmmKey[7], tmp[26];
uint16_t emmLen = SCT_LEN(emm);
uint32_t i, uniqueAddress, groupId, keyRef = 0;
//uint32_t emmCrc32;
char keyName[EMU_MAX_CHAR_KEYNAME], keyValue[16];
if (emmLen < 50)
{
return EMU_NOT_SUPPORTED;
}
// Check if unmasking is needed
if ((emm[3] & 0xF0) == 0x50)
{
unmask_emm(emm);
}
// looks like checksum does not work for all EMMs
//emmCrc32 = b2i(4, emm+emmLen-4);
//
//if(ccitt32_crc(emm, emmLen-4) != emmCrc32)
//{
// return EMU_CHECKSUM_ERROR;
//}
emmLen -= 4;
uniqueAddress = b2i(4, emm + 12);
snprintf(keyName, EMU_MAX_CHAR_KEYNAME, "%.8X", uniqueAddress);
do
{
if (!get_emm_key(emmKey, keyName, keyRef++, &groupId))
{
//cs_log_dbg(D_ATR, "EMM key for UA %s is missing", keyName);
return EMU_KEY_NOT_FOUND;
}
for (i = 19; i + 27 <= emmLen; i += 27)
{
emmInfo = emm[i];
if (!get_bit(emmInfo, 7))
{
continue;
}
//keyNb = emm[i] & 0x0F;
memcpy(tmp, emm + i + 1, 26);
memcpy(tmpEmmKey, emmKey, 7);
powervu_decrypt(emm + i + 1, 26, tmpEmmKey, 0);
if ((emm[13] != emm[i + 24]) || (emm[14] != emm[i + 25]) || (emm[15] != emm[i + 26]))
{
memcpy(emm + i + 1, tmp, 26);
memcpy(tmpEmmKey, emmKey, 7);
powervu_decrypt(emm + i + 1, 26, tmpEmmKey, 1);
if ((emm[13] != emm[i + 24]) || (emm[14] != emm[i + 25]) || (emm[15] != emm[i + 26]))
{
memcpy(emm + i + 1, tmp, 26);
memcpy(tmpEmmKey, emmKey, 7);
continue;
}
}
decryptOk = 1;
emmType = emm[i + 2] & 0x7F;
if (emmType > 1)
{
continue;
}
if (emm[i + 3] == 0 && emm[i + 4] == 0)
{
cs_hexdump(0, &emm[i + 3], 7, keyValue, sizeof(keyValue));
cs_log("Key found in EMM: P %04X**** %02X %s -> REJECTED (looks invalid) UA: %08X",
groupId, emmType, keyValue, uniqueAddress);
continue;
}
update_ecm_keys_by_group(groupId, emmType, &emm[i + 3], uniqueAddress);
(*keysAdded)++;
cs_hexdump(0, &emm[i + 3], 7, keyValue, sizeof(keyValue));
cs_log("Key found in EMM: P %04X**** %02X %s ; UA: %08X", groupId, emmType, keyValue, uniqueAddress);
}
} while (!decryptOk);
return EMU_OK;
}
int8_t powervu_get_hexserials(uint8_t hexserials[][4], uint32_t maxCount, uint16_t srvid)
{
//srvid == 0xFFFF -> get all
int8_t alreadyAdded;
uint8_t tmp[4];
uint32_t i, j, k, groupid, length, count = 0;
KeyDataContainer *KeyDB;
KeyDB = emu_get_key_container('P');
if (KeyDB == NULL)
{
return 0;
}
for (i = 0; i < KeyDB->keyCount && count < maxCount; i++)
{
if (KeyDB->EmuKeys[i].provider <= 0x0000FFFF) // skip EMM keys
{
continue;
}
if (srvid != 0xFFFF && (KeyDB->EmuKeys[i].provider & 0x0000FFFF) != srvid)
{
continue;
}
// This "groupid" has an ECM key with our "srvid"
// (in ECM keys "groupid" is top 16 bits)
groupid = KeyDB->EmuKeys[i].provider >> 16;
for (j = 0; j < KeyDB->keyCount && count < maxCount; j++)
{
// Skip EMM keys belonging to other groups
// (in EMM keys "groupid" is bottom 16 bits)
if (KeyDB->EmuKeys[j].provider != groupid)
{
continue;
}
length = cs_strlen(KeyDB->EmuKeys[j].keyName);
if (length < 3)
{
continue;
}
if (length > 8)
{
length = 8;
}
memset(tmp, 0, 4);
char_to_bin(tmp + (4 - (length / 2)), KeyDB->EmuKeys[j].keyName, length);
for (k = 0, alreadyAdded = 0; k < count; k++)
{
if (!memcmp(hexserials[k], tmp, 4))
{
alreadyAdded = 1;
break;
}
}
if (!alreadyAdded)
{
memcpy(hexserials[count], tmp, 4);
count++;
}
}
}
return count;
}
int8_t powervu_get_hexserials_new(uint8_t hexserials[][4], uint32_t maxCount, uint16_t caid,
uint16_t tsid, uint16_t onid, uint32_t ens)
{
int8_t alreadyAdded;
uint8_t tmp[4];
uint32_t i, j, channel_hash, group_id, length, count = 0;
KeyDataContainer *KeyDB;
KeyDB = emu_get_key_container('P');
if (KeyDB == NULL)
{
return 0;
}
channel_hash = create_channel_hash(caid, tsid, onid, ens);
group_id = get_channel_group(channel_hash);
if (group_id == 0) // No group found for this hash
{
return 0;
}
for (i = 0; i < KeyDB->keyCount && count < maxCount; i++)
{
// Skip EMM keys belonging to other groups
// (in EMM keys "groupid" is bottom 16 bits)
if (KeyDB->EmuKeys[i].provider != group_id)
{
continue;
}
length = cs_strlen(KeyDB->EmuKeys[i].keyName);
if (length < 3)
{
continue;
}
if (length > 8)
{
length = 8;
}
memset(tmp, 0, 4);
char_to_bin(tmp + (4 - (length / 2)), KeyDB->EmuKeys[i].keyName, length);
for (j = 0, alreadyAdded = 0; j < count; j++)
{
if (!memcmp(hexserials[j], tmp, 4))
{
alreadyAdded = 1;
break;
}
}
if (!alreadyAdded)
{
memcpy(hexserials[count], tmp, 4);
count++;
}
}
return count;
}
#endif // WITH_EMU