2796 lines
70 KiB
C
2796 lines
70 KiB
C
#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
|