/* 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 "naomi_cart.h" #include 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 (u32 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 }