flycast/core/hw/naomi/decrypt.cpp

816 lines
20 KiB
C++

/* Encryption/decryption 315-5881 chip support */
/* used on Naomi M2 ROM boards */
/* Borrowed from yabause */
/* Based on mame 315-5881_crypt.cpp and stvprot.cpp */
#include <stdlib.h>
#include "naomi_cart.h"
typedef struct sbox_s {
u8 table[64];
u8 inputs[6]; // positions of the inputs bits, -1 means no input except from key
u8 outputs[2]; // positions of the output bits
} sbox;
#define FN1GK 38
#define FN2GK 32
#define BUFFER_SIZE 2
#define LINE_SIZE 512
#define FLAG_COMPRESSED 0x20000
#define BIT(A, B) ((A >> B)&0X1)
static const sbox fn1_sboxes[4][4] = {
{ // 1st round
{
{
0,3,2,2,1,3,1,2,3,2,1,2,1,2,3,1,3,2,2,0,2,1,3,0,0,3,2,3,2,1,2,0,
2,3,1,1,2,2,1,1,1,0,2,3,3,0,2,1,1,1,1,1,3,0,3,2,1,0,1,2,0,3,1,3,
},
{3,4,5,7,255,255},
{0,4}
},
{
{
2,2,2,0,3,3,0,1,2,2,3,2,3,0,2,2,1,1,0,3,3,2,0,2,0,1,0,1,2,3,1,1,
0,1,3,3,1,3,3,1,2,3,2,0,0,0,2,2,0,3,1,3,0,3,2,2,0,3,0,3,1,1,0,2,
},
{0,1,2,5,6,7},
{1,6}
},
{
{
0,1,3,0,3,1,1,1,1,2,3,1,3,0,2,3,3,2,0,2,1,1,2,1,1,3,1,0,0,2,0,1,
1,3,1,0,0,3,2,3,2,0,3,3,0,0,0,0,1,2,3,3,2,0,3,2,1,0,0,0,2,2,3,3,
},
{0,2,5,6,7,255},
{2,3}
},
{
{
3,2,1,2,1,2,3,2,0,3,2,2,3,1,3,3,0,2,3,0,3,3,2,1,1,1,2,0,2,2,0,1,
1,3,3,0,0,3,0,3,0,2,1,3,2,1,0,0,0,1,1,2,0,1,0,0,0,1,3,3,2,0,3,3,
},
{1,2,3,4,6,7},
{5,7}
},
},
{ // 2nd round
{
{
3,3,1,2,0,0,2,2,2,1,2,1,3,1,1,3,3,0,0,3,0,3,3,2,1,1,3,2,3,2,1,3,
2,3,0,1,3,2,0,1,2,1,3,1,2,2,3,3,3,1,2,2,0,3,1,2,2,1,3,0,3,0,1,3,
},
{0,1,3,4,5,7},
{0,4}
},
{
{
2,0,1,0,0,3,2,0,3,3,1,2,1,3,0,2,0,2,0,0,0,2,3,1,3,1,1,2,3,0,3,0,
3,0,2,0,0,2,2,1,0,2,3,3,1,3,1,0,1,3,3,0,0,1,3,1,0,2,0,3,2,1,0,1,
},
{0,1,3,4,6,255},
{1,5}
},
{
{
2,2,2,3,1,1,0,1,3,3,1,1,2,2,2,0,0,3,2,3,3,0,2,1,2,2,3,0,1,3,0,0,
3,2,0,3,2,0,1,0,0,1,2,2,3,3,0,2,2,1,3,1,1,1,1,2,0,3,1,0,0,2,3,2,
},
{1,2,5,6,7,6},
{2,7}
},
{
{
0,1,3,3,3,1,3,3,1,0,2,0,2,0,0,3,1,2,1,3,1,2,3,2,2,0,1,3,0,3,3,3,
0,0,0,2,1,1,2,3,2,2,3,1,1,2,0,2,0,2,1,3,1,1,3,3,1,1,3,0,2,3,0,0,
},
{2,3,4,5,6,7},
{3,6}
},
},
{ // 3rd round
{
{
0,0,1,0,1,0,0,3,2,0,0,3,0,1,0,2,0,3,0,0,2,0,3,2,2,1,3,2,2,1,1,2,
0,0,0,3,0,1,1,0,0,2,1,0,3,1,2,2,2,0,3,1,3,0,1,2,2,1,1,1,0,2,3,1,
},
{1,2,3,4,5,7},
{0,5}
},
{
{
1,2,1,0,3,1,1,2,0,0,2,3,2,3,1,3,2,0,3,2,2,3,1,1,1,1,0,3,2,0,0,1,
1,0,0,1,3,1,2,3,0,0,2,3,3,0,1,0,0,2,3,0,1,2,0,1,3,3,3,1,2,0,2,1,
},
{0,2,4,5,6,7},
{1,6}
},
{
{
0,3,0,2,1,2,0,0,1,1,0,0,3,1,1,0,0,3,0,0,2,3,3,2,3,1,2,0,0,2,3,0,
// unused?
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
},
{0,2,4,6,7,255},
{2,3}
},
{
{
0,0,1,0,0,1,0,2,3,3,0,3,3,2,3,0,2,2,2,0,3,2,0,3,1,0,0,3,3,0,0,0,
2,2,1,0,2,0,3,2,0,0,3,1,3,3,0,0,2,1,1,2,1,0,1,1,0,3,1,2,0,2,0,3,
},
{0,1,2,3,6,255},
{4,7}
},
},
{ // 4th round
{
{
0,3,3,3,3,3,2,0,0,1,2,0,2,2,2,2,1,1,0,2,2,1,3,2,3,2,0,1,2,3,2,1,
3,2,2,3,1,0,1,0,0,2,0,1,2,1,2,3,1,2,1,1,2,2,1,0,1,3,2,3,2,0,3,1,
},
{0,1,3,4,5,6},
{0,5}
},
{
{
0,3,0,0,2,0,3,1,1,1,2,2,2,1,3,1,2,2,1,3,2,2,3,3,0,3,1,0,3,2,0,1,
3,0,2,0,1,0,2,1,3,3,1,2,2,0,2,3,3,2,3,0,1,1,3,3,0,2,1,3,0,2,2,3,
},
{0,1,2,3,5,7},
{1,7}
},
{
{
0,1,2,3,3,3,3,1,2,0,2,3,2,1,0,1,2,2,1,2,0,3,2,0,1,1,0,1,3,1,3,1,
3,1,0,0,1,0,0,0,0,1,2,2,1,1,3,3,1,2,3,3,3,2,3,0,2,2,1,3,3,0,2,0,
},
{2,3,4,5,6,7},
{2,3}
},
{
{
0,2,1,1,3,2,0,3,1,0,1,0,3,2,1,1,2,2,0,3,1,0,1,2,2,2,3,3,0,0,0,0,
1,2,1,0,2,1,2,2,2,3,2,3,0,1,3,0,0,1,3,0,0,1,1,0,1,0,0,0,0,2,0,1,
},
{0,1,2,4,6,7},
{4,6}
},
},
};
static const sbox fn2_sboxes[4][4] = {
{ // 1st round
{
{
3,3,0,1,0,1,0,0,0,3,0,0,1,3,1,2,0,3,3,3,2,1,0,1,1,1,2,2,2,3,2,2,
2,1,3,3,1,3,1,1,0,0,1,2,0,2,2,1,1,2,3,1,2,1,3,1,2,2,0,1,3,0,2,2,
},
{1,3,4,5,6,7},
{0,7}
},
{
{
0,1,3,0,1,1,2,3,2,0,0,3,2,1,3,1,3,3,0,0,1,0,0,3,0,3,3,2,3,2,0,1,
3,2,3,2,2,1,3,1,1,1,0,3,3,2,2,1,1,2,0,2,0,1,1,0,1,0,1,1,2,0,3,0,
},
{0,3,5,6,5,0},
{1,2}
},
{
{
0,2,2,1,0,1,2,1,2,0,1,2,3,3,0,1,3,1,1,2,1,2,1,3,3,2,3,3,2,1,0,1,
0,1,0,2,0,1,1,3,2,0,3,2,1,1,1,3,2,3,0,2,3,0,2,2,1,3,0,1,1,2,2,2,
},
{0,2,3,4,7,255},
{3,4}
},
{
{
2,3,1,3,2,0,1,2,0,0,3,3,3,3,3,1,2,0,2,1,2,3,0,2,0,1,0,3,0,2,1,0,
2,3,0,1,3,0,3,2,3,1,2,0,3,1,1,2,0,3,0,0,2,0,2,1,2,2,3,2,1,2,3,1,
},
{1,2,5,6,255,255},
{5,6}
},
},
{ // 2nd round
{
{
2,3,1,3,1,0,3,3,3,2,3,3,2,0,0,3,2,3,0,3,1,1,2,3,1,1,2,2,0,1,0,0,
2,1,0,1,2,0,1,2,0,3,1,1,2,3,1,2,0,2,0,1,3,0,1,0,2,2,3,0,3,2,3,0,
},
{0,1,4,5,6,7},
{0,7}
},
{
{
0,2,2,0,2,2,0,3,2,3,2,1,3,2,3,3,1,1,0,0,3,0,2,1,1,3,3,2,3,2,0,1,
1,2,3,0,1,0,3,0,3,1,0,2,1,2,0,3,2,3,1,2,2,0,3,2,3,0,0,1,2,3,3,3,
},
{0,2,3,6,7,255},
{1,5}
},
{
{
1,0,3,0,0,1,2,1,0,0,1,0,0,0,2,3,2,2,0,2,0,1,3,0,2,0,1,3,2,3,0,1,
1,2,2,2,1,3,0,3,0,1,1,0,3,2,3,3,2,0,0,3,1,2,1,3,3,2,1,0,2,1,2,3,
},
{2,3,4,6,7,2},
{2,3}
},
{
{
2,3,1,3,1,1,2,3,3,1,1,0,1,0,2,3,2,1,0,0,2,2,0,1,0,2,2,2,0,2,1,0,
3,1,2,3,1,3,0,2,1,0,1,0,0,1,2,2,3,2,3,1,3,2,1,1,2,0,2,1,3,3,1,0,
},
{1,2,3,4,5,6},
{4,6}
},
},
{ // 3rd round
{
{
0,3,0,1,3,0,0,2,1,0,1,3,2,2,2,0,3,3,3,0,2,2,0,3,0,0,2,3,0,3,2,1,
3,3,0,3,0,2,3,3,1,1,1,0,2,2,1,1,3,0,3,1,2,0,2,0,0,0,3,2,1,1,0,0,
},
{1,4,5,6,7,5},
{0,5}
},
{
{
0,3,0,1,3,0,3,1,3,2,2,2,3,0,3,2,2,1,2,2,0,3,2,2,0,0,2,1,1,3,2,3,
2,3,3,1,2,0,1,2,2,1,0,0,0,0,2,3,1,2,0,3,1,3,1,2,3,2,1,0,3,0,0,2,
},
{0,2,3,4,6,7},
{1,7}
},
{
{
2,2,0,3,0,3,1,0,1,1,2,3,2,3,1,0,0,0,3,2,2,0,2,3,1,3,2,0,3,3,1,3,
// unused?
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
},
{1,2,4,7,2,255},
{2,4}
},
{
{
0,2,3,1,3,1,1,0,0,1,3,0,2,1,3,3,2,0,2,1,1,2,3,3,0,0,0,2,0,2,3,0,
3,3,3,3,2,3,3,2,3,0,1,0,2,3,3,2,0,1,3,1,0,1,2,3,3,0,2,0,3,0,3,3,
},
{0,1,2,3,5,7},
{3,6}
},
},
{ // 4th round
{
{
0,1,1,0,0,1,0,2,3,3,0,1,2,3,0,2,1,0,3,3,2,0,3,0,0,2,1,0,1,0,1,3,
0,3,3,1,2,0,3,0,1,3,2,0,3,3,1,3,0,2,3,3,2,1,1,2,2,1,2,1,2,0,1,1,
},
{0,1,2,4,7,255},
{0,5}
},
{
{
2,0,0,2,3,0,2,3,3,1,1,1,2,1,1,0,0,2,1,0,0,3,1,0,0,3,3,0,1,0,1,2,
0,2,0,2,0,1,2,3,2,1,1,0,3,3,3,3,3,3,1,0,3,0,0,2,0,3,2,0,2,2,0,1,
},
{0,1,3,5,6,255},
{1,3}
},
{
{
0,1,1,2,1,3,1,1,0,0,3,1,1,1,2,0,3,2,0,1,1,2,3,3,3,0,3,0,0,2,0,3,
3,2,0,0,3,2,3,1,2,3,0,3,2,0,1,2,2,2,0,2,0,1,2,2,3,1,2,2,1,1,1,1,
},
{0,2,3,4,5,7},
{2,7}
},
{
{
0,1,2,0,3,3,0,3,2,1,3,3,0,3,1,1,3,2,3,2,3,0,0,0,3,0,2,2,3,2,2,3,
2,2,3,1,2,3,1,2,0,3,0,2,3,1,0,0,3,2,1,2,1,2,1,3,1,0,2,3,3,1,3,2,
},
{2,3,4,5,6,7},
{4,6}
},
},
};
static const int fn1_game_key_scheduling[FN1GK][2] = {
{1,29}, {1,71}, {2,4}, {2,54}, {3,8}, {4,56}, {4,73}, {5,11},
{6,51}, {7,92}, {8,89}, {9,9}, {9,39}, {9,58}, {10,90}, {11,6},
{12,64}, {13,49}, {14,44}, {15,40}, {16,69}, {17,15}, {18,23}, {18,43},
{19,82}, {20,81}, {21,32}, {22,5}, {23,66}, {24,13}, {24,45}, {25,12},
{25,35}, {26,61}, {27,10}, {27,59}, {28,25}, {29,86}
};
static const int fn2_game_key_scheduling[FN2GK][2] = {
{0,0}, {1,3}, {2,11}, {3,20}, {4,22}, {5,23}, {6,29}, {7,38},
{8,39}, {9,55}, {9,86}, {9,87}, {10,50}, {11,57}, {12,59}, {13,61},
{14,63}, {15,67}, {16,72}, {17,83}, {18,88}, {19,94}, {20,35}, {21,17},
{22,6}, {23,85}, {24,16}, {25,25}, {26,92}, {27,47}, {28,28}, {29,90}
};
static const int fn1_sequence_key_scheduling[20][2] = {
{0,52}, {1,34}, {2,17}, {3,36}, {4,84}, {4,88}, {5,57}, {6,48},
{6,68}, {7,76}, {8,83}, {9,30}, {10,22}, {10,41}, {11,38}, {12,55},
{13,74}, {14,19}, {14,80}, {15,26}
};
static const u8 trees[9][2][32] = {
{
{0x01,0x10,0x0f,0x05,0xc4,0x13,0x87,0x0a,0xcc,0x81,0xce,0x0c,0x86,0x0e,0x84,0xc2,
0x11,0xc1,0xc3,0xcf,0x15,0xc8,0xcd,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
{0xc7,0x02,0x03,0x04,0x80,0x06,0x07,0x08,0x09,0xc9,0x0b,0x0d,0x82,0x83,0x85,0xc0,
0x12,0xc6,0xc5,0x14,0x16,0xca,0xcb,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
},
{
{0x02,0x80,0x05,0x04,0x81,0x10,0x15,0x82,0x09,0x83,0x0b,0x0c,0x0d,0xdc,0x0f,0xde,
0x1c,0xcf,0xc5,0xdd,0x86,0x16,0x87,0x18,0x19,0x1a,0xda,0xca,0xc9,0x1e,0xce,0xff,},
{0x01,0x17,0x03,0x0a,0x08,0x06,0x07,0xc2,0xd9,0xc4,0xd8,0xc8,0x0e,0x84,0xcb,0x85,
0x11,0x12,0x13,0x14,0xcd,0x1b,0xdb,0xc7,0xc0,0xc1,0x1d,0xdf,0xc3,0xc6,0xcc,0xff,},
},
{
{0xc6,0x80,0x03,0x0b,0x05,0x07,0x82,0x08,0x15,0xdc,0xdd,0x0c,0xd9,0xc2,0x14,0x10,
0x85,0x86,0x18,0x16,0xc5,0xc4,0xc8,0xc9,0xc0,0xcc,0xff,0xff,0xff,0xff,0xff,0xff,},
{0x01,0x02,0x12,0x04,0x81,0x06,0x83,0xc3,0x09,0x0a,0x84,0x11,0x0d,0x0e,0x0f,0x19,
0xca,0xc1,0x13,0xd8,0xda,0xdb,0x17,0xde,0xcd,0xcb,0xff,0xff,0xff,0xff,0xff,0xff,},
},
{
{0x01,0x80,0x0d,0x04,0x05,0x15,0x83,0x08,0xd9,0x10,0x0b,0x0c,0x84,0x0e,0xc0,0x14,
0x12,0xcb,0x13,0xca,0xc8,0xc2,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
{0xc5,0x02,0x03,0x07,0x81,0x06,0x82,0xcc,0x09,0x0a,0xc9,0x11,0xc4,0x0f,0x85,0xd8,
0xda,0xdb,0xc3,0xdc,0xdd,0xc1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
},
{
{0x01,0x80,0x06,0x0c,0x05,0x81,0xd8,0x84,0x09,0xdc,0x0b,0x0f,0x0d,0x0e,0x10,0xdb,
0x11,0xca,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
{0xc4,0x02,0x03,0x04,0xcb,0x0a,0x07,0x08,0xd9,0x82,0xc8,0x83,0xc0,0xc1,0xda,0xc2,
0xc9,0xc3,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
},
{
{0x01,0x02,0x06,0x0a,0x83,0x0b,0x07,0x08,0x09,0x82,0xd8,0x0c,0xd9,0xda,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
{0xc3,0x80,0x03,0x04,0x05,0x81,0xca,0xc8,0xdb,0xc9,0xc0,0xc1,0x0d,0xc2,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
},
{
{0x01,0x02,0x03,0x04,0x81,0x07,0x08,0xd8,0xda,0xd9,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
{0xc2,0x80,0x05,0xc9,0xc8,0x06,0x82,0xc0,0x09,0xc1,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
},
{
{0x01,0x80,0x04,0xc8,0xc0,0xd9,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
{0xc1,0x02,0x03,0x81,0x05,0xd8,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
},
{
{0x01,0xd8,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
{0xc0,0x80,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,},
},
};
static const int fn2_sequence_key_scheduling[16] = {77,34,8,42,36,27,69,66,13,9,79,31,49,7,24,64};
static const int fn2_middle_result_scheduling[16] = {1,10,44,68,74,78,81,95,2,4,30,40,41,51,53,58};
static int feistel_function(int input, const sbox *sboxes, uint32_t subkeys)
{
int k,m;
int aux;
int result=0;
for (m=0; m<4; ++m) { // 4 sboxes
for (k=0, aux=0; k<6; ++k)
if (sboxes[m].inputs[k]!=255)
aux |= BIT(input, sboxes[m].inputs[k]) << k;
aux = sboxes[m].table[(aux^subkeys)&0x3f];
for (k=0; k<2; ++k)
result |= BIT(aux,k) << sboxes[m].outputs[k];
subkeys >>=6;
}
return result;
}
/**************************
This implementation is an "educational" version. It must be noted that it can be speed-optimized in a number of ways.
The most evident one is to factor out the parts of the key-scheduling that must only be done once (like the game-key &
sequence-key parts) as noted in the comments inlined in the function. More sophisticated speed-ups can be gained by
noticing that the weak key-scheduling would allow to create some pregenerated look-up tables for doing most of the work
of the function. Even so, it would still be pretty slow, so caching techniques could be a wiser option here.
**************************/
static u32 cryptoKey = 0;
static u32 cryptoSubKey = 0;
static u32 cryptoAddr = 0;
static u8 cryptoReady = 0;
static int bufferBit = 0;
static int bufferBit2 = 0;
static u16 dec_hist = 0;
static u32 dec_header = 0;
static u32 buffer_pos = 0;
static u32 line_buffer_pos = 0;
static u32 line_buffer_size = 0;
static u8 done_compression = 0;
static u32 block_pos = 0;
static u32 block_size = 0;
static u32 block_numlines = 0;
static u16 buffer2a = 0;
static u8 buffer2[2];
static u8 buffer[BUFFER_SIZE];
static u8 line_buffer[LINE_SIZE];
static u8 line_buffer_prev[LINE_SIZE];
void cryptoReset()
{
cryptoKey = 0;
cryptoSubKey = 0;
cryptoAddr = 0;
cryptoReady = 0;
bufferBit = 0;
bufferBit2 = 0;
memset(buffer, 0, BUFFER_SIZE);
memset(line_buffer, 0, LINE_SIZE);
memset(line_buffer_prev, 0, LINE_SIZE);
dec_hist = 0;
dec_header = 0;
buffer_pos = 0;
line_buffer_pos = 0;
line_buffer_size = 0;
}
void cyptoSetKey(u32 privKey)
{
cryptoKey = privKey;
}
void cyptoSetLowAddr(u16 val)
{
cryptoAddr = (cryptoAddr & 0xffff0000) | val;
cryptoReady = 0;
}
void cyptoSetHighAddr(u16 val)
{
cryptoAddr = (cryptoAddr & 0x0000ffff) | (val<<16);
cryptoReady = 0;
bufferBit = 7;
bufferBit2 = 15;
}
void cyptoSetSubkey(u16 subKey)
{
cryptoSubKey = subKey;
cryptoReady = 0;
}
static u16 bitswap16(u16 in, const u8* vec)
{
u16 ret = 0;
for (int i = 0; i<16; i++) {
ret |= ((in >> vec[i])&0x1)<<(15-i);
}
return ret;
}
static const u8 vec1[16] = {5, 12, 14, 13, 9, 3, 6, 4, 8, 1, 15, 11, 0, 7, 10, 2};
static const u8 vec2[16] = {14, 3, 8, 12, 13, 7, 15, 4, 6, 2, 9, 5, 11, 0, 1, 10};
static const u8 vec3[16] = {15, 7, 6, 14, 13, 12, 5, 4, 3, 2, 11, 10, 9, 1, 0, 8};
static u16 block_decrypt(uint32_t game_key, uint16_t sequence_key, uint16_t counter, uint16_t data)
{
int j;
int aux, aux2;
int A, B;
int middle_result;
u32 fn1_subkeys[4];
u32 fn2_subkeys[4];
/* Game-key scheduling; this could be done just once per game at initialization time */
memset(fn1_subkeys, 0, sizeof(u32) * 4);
memset(fn2_subkeys, 0, sizeof(u32) * 4);
for (j = 0; j < FN1GK; ++j) {
if (BIT(game_key, fn1_game_key_scheduling[j][0]) != 0) {
aux = fn1_game_key_scheduling[j][1] % 24;
aux2 = fn1_game_key_scheduling[j][1] / 24;
fn1_subkeys[aux2] ^= (1 << aux);
}
}
for (j = 0; j < FN2GK; ++j) {
if (BIT(game_key, fn2_game_key_scheduling[j][0]) != 0) {
aux = fn2_game_key_scheduling[j][1] % 24;
aux2 = fn2_game_key_scheduling[j][1] / 24;
fn2_subkeys[aux2] ^= (1 << aux);
}
}
/********************************************************/
/* Sequence-key scheduling; this could be done just once per decryption run */
for (j = 0; j < 20; ++j) {
if (BIT(sequence_key, fn1_sequence_key_scheduling[j][0]) != 0) {
aux = fn1_sequence_key_scheduling[j][1] % 24;
aux2 = fn1_sequence_key_scheduling[j][1] / 24;
fn1_subkeys[aux2] ^= (1 << aux);
}
}
for (j = 0; j < 16; ++j) {
if (BIT(sequence_key, j) != 0) {
aux = fn2_sequence_key_scheduling[j] % 24;
aux2 = fn2_sequence_key_scheduling[j] / 24;
fn2_subkeys[aux2] ^= (1 << aux);
}
}
/**************************************************************/
// First Feistel Network
aux = bitswap16(counter, vec1);
// 1st round
B = aux >> 8;
A = (aux & 0xff) ^ feistel_function(B, fn1_sboxes[0], fn1_subkeys[0]);
// 2nd round
B ^= feistel_function(A, fn1_sboxes[1], fn1_subkeys[1]);
// 3rd round
A ^= feistel_function(B, fn1_sboxes[2], fn1_subkeys[2]);
// 4th round
B ^= feistel_function(A, fn1_sboxes[3], fn1_subkeys[3]);
middle_result = (B << 8) | A;
/* Middle-result-key sheduling */
for (j = 0; j < 16; ++j) {
if (BIT(middle_result, j) != 0) {
aux = fn2_middle_result_scheduling[j] % 24;
aux2 = fn2_middle_result_scheduling[j] / 24;
fn2_subkeys[aux2] ^= (1 << aux);
}
}
/*********************/
// Second Feistel Network
aux = bitswap16(data, vec2);
// 1st round
B = aux >> 8;
A = (aux & 0xff) ^ feistel_function(B, fn2_sboxes[0], fn2_subkeys[0]);
// 2nd round
B ^= feistel_function(A, fn2_sboxes[1], fn2_subkeys[1]);
// 3rd round
A ^= feistel_function(B, fn2_sboxes[2], fn2_subkeys[2]);
// 4th round
B ^= feistel_function(A, fn2_sboxes[3], fn2_subkeys[3]);
aux = (B << 8) | A;
aux = bitswap16(aux, vec3);
return aux;
}
static u16 m_read(uint32_t addr)
{
return ((M2Cartridge *)CurrentCartridge)->ReadCipheredData(addr);
}
static u16 get_decrypted_16()
{
uint16_t enc;
enc = m_read(cryptoAddr);
uint16_t dec = block_decrypt(cryptoKey, cryptoSubKey, cryptoAddr, enc);
uint16_t res = (dec & 3) | (dec_hist & 0xfffc);
dec_hist = dec;
cryptoAddr ++;
return res;
}
static void cryptoStart()
{
block_pos = 0;
done_compression = 0;
buffer_pos = BUFFER_SIZE;
if (bufferBit2 < 14)
{
dec_header = (buffer2a & 0x0003) << 16;
}
else
{
dec_hist = 0; // seems to be needed by astrass at least otherwise any call after the first one will be influenced by the one before it.
dec_header = get_decrypted_16() << 16;
}
dec_header |= get_decrypted_16();
block_numlines = ((dec_header & 0x000000ff) >> 0) + 1;
u32 blocky = ((dec_header & 0x0001ff00) >> 8) + 1;
block_size = block_numlines * blocky;
if(dec_header & FLAG_COMPRESSED)
{
line_buffer_size = blocky;
line_buffer_pos = line_buffer_size;
bufferBit = 7;
bufferBit2 = 15;
}
cryptoReady = 1;
}
static u32 get_compressed_bit()
{
// if(buffer_pos == BUFFER_SIZE)
// enc_fill();
if (bufferBit2 == 15)
{
bufferBit2 = 0;
buffer2a = get_decrypted_16();
buffer2[0] = buffer2a;
buffer2[1] = buffer2a >> 8;
// block_pos+=2;
buffer_pos = 0;
}
else
{
bufferBit2++;
}
// if (bufferBit ==7) printf("using byte %02x\n", buffer2[(buffer_pos&1) ^ 1]);
u32 res = (buffer2[(buffer_pos&1)^1] >> bufferBit) & 1;
bufferBit--;
if(bufferBit == -1) {
bufferBit = 7;
buffer_pos++;
}
return res;
}
static void line_fill()
{
uint8_t *lc = line_buffer;
uint8_t *lp = line_buffer_prev;
memcpy(line_buffer_prev, line_buffer, LINE_SIZE);
line_buffer_pos = 0;
for(int i=0; i != line_buffer_size;) {
// vlc 0: start of line
// vlc 1: interior of line
// vlc 2-9: 7-1 bytes from end of line
int slot = i ? i < line_buffer_size - 7 ? 1 : (i & 7) + 1 : 0;
uint32_t tmp = 0;
while (!(tmp&0x80)) {
if(get_compressed_bit()) {
tmp = trees[slot][1][tmp];
} else {
tmp = trees[slot][0][tmp];
}
}
if(tmp != 0xff) {
int count = (tmp & 7) + 1;
if(tmp&0x40) {
// Copy from previous line
static int offsets[4] = {0, 1, 0, -1};
int offset = offsets[(tmp & 0x18) >> 3];
for(int j=0; j != count; j++) {
lc[i^1] = lp[((i+offset) % line_buffer_size)^1];
i++;
}
} else {
// Get a byte in the stream and write n times
uint8_t byte;
byte = get_compressed_bit() << 1;
byte = (byte | get_compressed_bit()) << 1;
byte = (byte | get_compressed_bit()) << 1;
byte = (byte | get_compressed_bit()) << 1;
byte = (byte | get_compressed_bit()) << 1;
byte = (byte | get_compressed_bit()) << 1;
byte = (byte | get_compressed_bit()) << 1;
byte = byte | get_compressed_bit();
for(int j=0; j != count; j++)
lc[(i++)^1] = byte;
}
}
}
block_pos++;
if (block_numlines == block_pos)
{
done_compression = 1;
}
else
{
}
}
static void enc_fill()
{
for(int i = 0; i != BUFFER_SIZE; i+=2) {
uint16_t val = get_decrypted_16();
buffer[i] = val;
buffer[i+1] = val >> 8;
block_pos+=2;
if (!(dec_header & FLAG_COMPRESSED))
{
if (block_pos == block_size)
{
// if we reach the size specified we need to read a new header
// todo: for compressed blocks this depends on OUTPUT size, not input size, so things get messy
cryptoStart();
}
}
}
buffer_pos = 0;
}
u16 cryptoDecrypt()
{
u8* base;
if(!cryptoReady)
cryptoStart();
if(dec_header & FLAG_COMPRESSED) {
if (line_buffer_pos == line_buffer_size) // if there's no data left to read..
{
if (done_compression == 1)
cryptoStart();
line_fill();
}
base = line_buffer + line_buffer_pos;
line_buffer_pos += 2;
} else {
if(buffer_pos == BUFFER_SIZE)
enc_fill();
base = &buffer[buffer_pos];
buffer_pos += 2;
}
return (base[1] << 8) | base[0]; // Swapping the bytes now
}