816 lines
20 KiB
C++
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
|
|
}
|