// license:BSD-3-Clause // copyright-holders:Andreas Naive,David Haywood #include "emu.h" #include "igs036crypt.h" /**************************************************************************** IGS036 encryption emulation The encryption used by the IGS036 seems to be another iteration over previous IGS encryption schemes. Basically, it consists on a rotation-based non-trivial obfuscation layered upon a simple address-based XOR encryption (similar to the ones found in previous IGS circuits). The scheme works on 16-bits words and is probably designed to depend on 24 bits of (word-) address; in what follows, we will refer to the 8 lowest ones simply as the lowest bits of the address, and the other 16 as the highest bits the address. The address-based XOR can be thought as 16 one-bit XORs against key bits controlled by certain combinations of up to three address bits. The game key is comprised of 256 8-bits values provided by the internal ARM; every 8-bit value in the key is used on those words whose address match the index modulus 256; in a given key byte, every bit affects two positions of the correponding 16-bits encrypted words. This use of the key is similar to the one found in previous instantiations of IGS circuits. What is new in the IGS036 is the use of an obfuscation working this way: 1) The highest bits of the address are split in 4 groups, every of which controls a rotation by a shift of (plus or minus) 9, 1, 2 and 4 respectively. 2) For every address, the highest bit of the group set in the address controls the activation/deactivation of the rotation for his group, using an associated (and fixed) boolean function depending on the lowest bits of the address. 3) If the group rotation is to be activated according to 2), then another fixed group-level boolean functions (again, depending on the lowest bits of the address) control the direction (left or right) of the rotation. 4) One of the groups (the associated with the shift by 9) interacts with the other three by inverting (when active itself) the activation/deactivation patterns of the other three. 5) The lowest bits of the address control a further rotation(independent on the highest bits of the address). 6) Finally, a global bitswap is applied. All the associated boolean functions are clearly of low complexity, so it should be expected that the hardware is calculating them that way rather than using lookup tables or otherwise. It should be stressed that this obfuscation is done system-wide without dependence on the game keys. On a different note, the unused tail of the ROMs are pattern-filled and, more interestingly, that region appears to be hiding 20-bytes values (SHA-1 hashes?) located at positions which vary per set. See the table below. driver 20-bytes value position in the ROM --------- ---------------------------------- orleg2o $763984-$763997 orleg2 $76C77C-$76C78F kov3 $718040-$718053 kof98umh $E50E60-$E50E73 TO-DO: complete the table with the 20-bytes values *****************************************************************************/ igs036_decryptor::igs036_decryptor(const uint8_t* game_key) : key(game_key) { } void igs036_decryptor::decrypter_rom(uint16_t* rom, int size, int offset) { for (int i = 0; i < size / 2; i++) { rom[i] = decrypt(rom[i], i+offset); } } uint16_t igs036_decryptor::decrypt(uint16_t cipherword, int word_address)const { // key-independent manipulation int aux = deobfuscate(cipherword, word_address); // key-dependent manipulation for (int i=0; i<16; ++i) { if ((word_address&triggers[i][0]) == triggers[i][1]) aux ^= BIT(key[word_address&0xff],i&7) << i; else aux ^= BIT(0x1a3a, i) << i; } return aux; } uint16_t igs036_decryptor::deobfuscate(uint16_t cipherword, int word_address)const { // key-independent manipulation int shift = rotation(word_address); int aux = rol(cipherword, shift); aux = bitswap<16>(aux, 10,9,8,7,0,15,6,5, 14,13,4,3,12,11,2,1); return aux; } int igs036_decryptor::rotation(int address)const { const int group15[] = {15,11, 7, 5}; // 15 is a guess const int group14[] = {14, 9, 3, 2}; const int group13[] = {13,10, 6, 1}; const int group12[] = {12, 8, 4, 0}; // rotation depending on all the address bits int enabled0 = rot_enabled(address, group15); int rot = enabled0 * rot_group(address, group15) * 9; int enabled1 = enabled0 ^ rot_enabled(address, group14); rot += enabled1 * rot_group(address, group14) * 1; int enabled2 = enabled0 ^ rot_enabled(address, group13); rot += enabled2 * rot_group(address, group13) * 2; int enabled3 = enabled0 ^ rot_enabled(address, group12); rot += enabled3 * rot_group(address, group12) * 4; // block-independent rotation (just depending on the lowest 8 bits) int rot2 = 4*BIT(address,0); rot2 += 1*BIT(address,4)*(BIT(address,0)*2-1); rot2 += 4*BIT(address,3)*(BIT(address,0)*2-1); rot2 *= (BIT(address,7)|(BIT(address,0)^BIT(address,1)^1))*2-1; rot2 += 2*((BIT(address,0)^BIT(address,1))&(BIT(address,7)^1)); return (rot+rot2)&0xf; } int igs036_decryptor::rot_enabled(int address, const int* group)const { int enabled = 0; for (int j=0; j<4; ++j) { if (BIT(address,8+group[j])) { int aux = address ^ (0x1b*BIT(address,2)); enabled = rot_enabling[group[j]][aux&3](aux); break; } } return enabled; } int igs036_decryptor::rot_group(int address, const int* group)const { int aux = rot_direction[group[0]&3][address&7](address); return (aux*2)-1; } uint16_t igs036_decryptor::rol(uint16_t num, int shift)const { uint16_t r = num<>(16-shift); return r|l; } // the triggers describe under what conditions are every one of the 16 XORs activated const uint32_t igs036_decryptor::triggers[16][2] = { {0x000101, 0x000001}, {0x000802, 0x000800}, {0x000204, 0x000004}, {0x000408, 0x000408}, {0x010010, 0x000010}, {0x020020, 0x000020}, {0x040040, 0x000040}, {0x080080, 0x080080}, {0x100100, 0x000100}, {0x200200, 0x200000}, {0x400400, 0x400000}, {0x800801, 0x000001}, {0x001004, 0x001000}, {0x002010, 0x002000}, {0x004040, 0x000040}, {0x008100, 0x008100} }; // The rotation depending on the 16 highest address bits depends on a series // of function on the 8 lowest word-address bits. Some comments: // * Bits #5 & #6 are unused so, in fact, they only depend on 6 address bits // * The functions are clearly low-complexity boolean functions on those 6 bits // rather than, say, random lookup tables // * There are quite a number of functionally equivalent ways to implement // those boolean functions, so the given implementation (by multiplexing // over some simple functions) shouldn't be taken too seriously: while it's // functionally correct, it doesn't neccesarily represent the way the hardware // is calculating them. static int unknown(int address) { return 0; } static int cZero (int address) { return 0; } static int cOne (int address) { return 1; } static int bit_3 (int address) { return BIT(address,3); } static int bit_4 (int address) { return BIT(address,4); } static int bit_7 (int address) { return BIT(address,7); } static int not_3 (int address) { return BIT(address,3)^1; } static int not_4 (int address) { return BIT(address,4)^1; } static int not_7 (int address) { return BIT(address,7)^1; } static int xor_37 (int address) { return BIT(address,3)^BIT(address,7); } static int xnor_37(int address) { return BIT(address,3)^BIT(address,7)^1; } static int xor_47 (int address) { return BIT(address,4)^BIT(address,7); } static int xnor_47(int address) { return BIT(address,4)^BIT(address,7)^1; } static int nor_34 (int address) { return (BIT(address,3)|BIT(address,4))^1; } static int impl_43(int address) { return BIT(address,3)||(BIT(address,4)^1); } int (*igs036_decryptor::rot_enabling[16][4])(int) = { {bit_3 , not_3 , bit_3 , not_3 }, {bit_3 , not_3 , bit_3 , not_3 }, {bit_4 , bit_4 , bit_4 , bit_4 }, {bit_4 , not_4 , bit_4 , not_4 }, {bit_3 , bit_3 , bit_3 , bit_3 }, {nor_34 , bit_7 , bit_7 , cZero }, {cZero , cOne , cZero , cOne }, {impl_43, xor_37 , xnor_37, not_3 }, {bit_3 , bit_3 , not_3 , not_3 }, {bit_4 , bit_4 , not_4 , not_4 }, {cZero , cZero , cZero , cZero }, {nor_34 , bit_7 , not_7 , cOne }, {bit_3 , not_3 , bit_3 , not_3 }, {cZero , cOne , cOne , cZero }, {bit_4 , not_4 , bit_4 , not_4 }, {unknown, unknown, unknown, unknown}, }; int (*igs036_decryptor::rot_direction[4][8])(int) = { {bit_3 , xor_37 , xnor_37, not_3 , bit_3 , xor_37 , xnor_37, not_3 }, {cZero , not_7 , not_7 , cZero , cZero , not_7 , not_7 , cZero }, {bit_4 , xor_47 , xnor_47, not_4 , bit_4 , xor_47 , xnor_47, not_4 }, {bit_3 , not_7 , bit_7 , cZero , cOne , not_7 , bit_7 , cZero }, }; // ------------------------GAME KEYS--------------------------- // The keys below have been obtained by an automatic process // exploiting the simple XOR scheme used by the system. Overall, the process, // while simple, seems to be pretty robust, so few errors should be expected, // if any. The exceptions are DDPDOJ & KOF98UMH (see below). const uint8_t m312cn_key[0x100] = { 0x01, 0x09, 0x02, 0xab, 0x23, 0x20, 0xa2, 0x03, 0x10, 0x9b, 0xba, 0x33, 0x04, 0x2e, 0x27, 0x23, 0x92, 0x11, 0x13, 0x93, 0x13, 0x86, 0x83, 0x02, 0x18, 0x8a, 0x8b, 0x9a, 0x10, 0x0f, 0x13, 0x83, 0xa2, 0x98, 0x32, 0xba, 0x06, 0xab, 0x02, 0x0b, 0x1a, 0xa0, 0x13, 0x82, 0x84, 0x80, 0x8a, 0xa7, 0x83, 0xb0, 0xb2, 0xab, 0x31, 0x07, 0xa3, 0x02, 0x10, 0x23, 0x8b, 0xb2, 0x2b, 0x0a, 0xa7, 0xa3, 0x02, 0x7b, 0x12, 0xc3, 0x07, 0x0c, 0x43, 0xa6, 0x91, 0x91, 0x9b, 0xaa, 0x82, 0xca, 0x2e, 0x6a, 0x43, 0x51, 0x02, 0xcb, 0x52, 0x8b, 0x56, 0x57, 0x88, 0xc3, 0x83, 0x1a, 0x8d, 0x51, 0x86, 0x0a, 0xc1, 0x1b, 0x22, 0x5a, 0x07, 0x84, 0xa3, 0xce, 0xba, 0xfa, 0xab, 0x6a, 0xea, 0x2c, 0x2e, 0x67, 0x00, 0x33, 0x53, 0xd3, 0x47, 0x98, 0x93, 0x62, 0x2b, 0x9b, 0x2b, 0x82, 0xed, 0x4b, 0x1a, 0x86, 0xa0, 0xb9, 0x82, 0x0b, 0x27, 0x09, 0xa2, 0xab, 0x20, 0x3a, 0x8b, 0x0a, 0x84, 0x8d, 0x0b, 0x8f, 0x83, 0x8a, 0x92, 0x13, 0x10, 0x18, 0x06, 0x96, 0x83, 0x89, 0x8b, 0x92, 0x1c, 0x92, 0x9b, 0x17, 0x02, 0x2b, 0x02, 0x02, 0x06, 0x25, 0xa2, 0xab, 0xa8, 0x12, 0x13, 0x9a, 0x21, 0x27, 0x03, 0x2a, 0xa3, 0x92, 0x33, 0xb2, 0x94, 0x12, 0x32, 0x9b, 0x90, 0xa0, 0x8a, 0x2a, 0x9a, 0xbb, 0xae, 0x1e, 0x41, 0x2b, 0x92, 0xb2, 0x44, 0xe0, 0x02, 0x6f, 0x61, 0x30, 0x4a, 0x13, 0x61, 0x4f, 0x2e, 0xa6, 0x52, 0x00, 0xc2, 0x8b, 0x53, 0x8f, 0x93, 0x4f, 0x5b, 0x01, 0x1a, 0x9b, 0xc6, 0x01, 0x03, 0x0b, 0x42, 0x09, 0xf2, 0x62, 0x82, 0x41, 0x22, 0xc6, 0x90, 0x2a, 0xfa, 0x0b, 0x6c, 0xa0, 0x4f, 0x03, 0xa0, 0x53, 0xf2, 0xbb, 0x46, 0x96, 0x23, 0x22, 0xd8, 0xfa, 0x12, 0xab, 0x88, 0x1a, 0x7a, 0x8a, }; const uint8_t cjddzsp_key[0x100] = { 0x11, 0x21, 0xa2, 0x1a, 0x84, 0xaf, 0x26, 0x0b, 0x3b, 0xbb, 0x12, 0x9b, 0x89, 0x80, 0x2f, 0x0a, 0x91, 0x80, 0x93, 0x93, 0x80, 0x0b, 0x13, 0x93, 0x0a, 0x82, 0x8a, 0x12, 0x13, 0x05, 0x96, 0x17, 0x81, 0xb1, 0xb3, 0xab, 0x06, 0x2a, 0x87, 0x83, 0x33, 0x93, 0x13, 0x8a, 0x28, 0xa8, 0x07, 0x8b, 0x11, 0xa3, 0xb2, 0xa2, 0x23, 0x17, 0x17, 0xb6, 0x33, 0xa9, 0xa3, 0x23, 0xa0, 0xa3, 0x9b, 0xbb, 0x70, 0xe8, 0x83, 0x72, 0xe6, 0xa2, 0xa2, 0x27, 0xbb, 0xc8, 0xf3, 0x42, 0x6d, 0xc8, 0x66, 0x47, 0x93, 0x18, 0x12, 0x12, 0x13, 0x58, 0xd2, 0xc6, 0x49, 0x09, 0xc3, 0x0a, 0x81, 0x0b, 0xc2, 0xda, 0xd2, 0x33, 0xc2, 0x1a, 0x40, 0x89, 0x26, 0xeb, 0x78, 0x51, 0x5a, 0x62, 0xa3, 0xee, 0x02, 0x8f, 0x42, 0xa1, 0xe3, 0x3a, 0x41, 0x44, 0x93, 0xd3, 0x03, 0xda, 0xe2, 0x83, 0x69, 0xc5, 0xb3, 0xb6, 0x91, 0x00, 0xa2, 0x32, 0x24, 0x88, 0x87, 0xab, 0x02, 0x28, 0x2a, 0x8b, 0x87, 0xab, 0x2b, 0x8b, 0x13, 0x02, 0x03, 0x9a, 0x94, 0x13, 0x87, 0x0b, 0x1a, 0x98, 0x03, 0x1b, 0x10, 0x81, 0x1a, 0x9f, 0x81, 0xa9, 0x03, 0x3a, 0x05, 0x06, 0x27, 0xab, 0x3b, 0xa8, 0x8a, 0xab, 0xaf, 0x0a, 0xaa, 0x2f, 0x31, 0x39, 0x32, 0x3a, 0x81, 0xbf, 0x07, 0x87, 0x89, 0x98, 0xa2, 0x22, 0x13, 0xa4, 0xb6, 0x0e, 0x43, 0xf2, 0x43, 0x33, 0x47, 0x4c, 0x66, 0x26, 0xf2, 0x69, 0x2b, 0x5a, 0xa3, 0x83, 0x4b, 0xe6, 0x41, 0x50, 0x92, 0xcb, 0xd3, 0x1e, 0x57, 0x87, 0x01, 0x19, 0x9a, 0x52, 0x45, 0x5a, 0x9e, 0xde, 0xa3, 0xa1, 0x42, 0x7b, 0xa3, 0x22, 0xa2, 0x87, 0x80, 0xe0, 0xf3, 0x23, 0x2a, 0x8e, 0x2f, 0x6f, 0x92, 0x1a, 0x23, 0xab, 0xb3, 0x09, 0xd6, 0xab, 0x38, 0xe3, 0x2b, 0x3a, 0xdf, 0x7d, 0xea, 0x87, }; const uint8_t cjdh2_key[0x100] = { 0x03, 0x31, 0x92, 0x23, 0x21, 0x2b, 0x23, 0x23, 0x39, 0x01, 0xb2, 0x9b, 0x0d, 0xaa, 0x07, 0x86, 0x03, 0x9b, 0x03, 0x82, 0x82, 0x00, 0x86, 0x0b, 0x80, 0x92, 0x9a, 0x1b, 0x81, 0x9a, 0x92, 0x8f, 0x83, 0x89, 0x82, 0x0a, 0x02, 0x0f, 0x83, 0xa7, 0x80, 0x32, 0xbb, 0x02, 0x8f, 0xa2, 0xaa, 0x0e, 0x80, 0x12, 0x23, 0xbb, 0x86, 0xb9, 0xb3, 0x1b, 0x19, 0xb8, 0x93, 0x22, 0x28, 0x9d, 0xbf, 0xb2, 0xa1, 0xb0, 0x63, 0xaa, 0x81, 0x8a, 0x47, 0x0b, 0xdb, 0x21, 0x5a, 0x03, 0xe9, 0x60, 0x2f, 0xab, 0x00, 0x43, 0xc2, 0x8b, 0x06, 0x54, 0x47, 0x9f, 0x51, 0xc9, 0x4a, 0x4b, 0x1f, 0x40, 0x9f, 0x52, 0x21, 0x00, 0xe3, 0x72, 0x44, 0x43, 0xc2, 0xab, 0x5a, 0x32, 0x1a, 0x62, 0x6d, 0xa2, 0x82, 0xce, 0x73, 0xe0, 0xc3, 0xa3, 0x73, 0x71, 0x16, 0x42, 0x69, 0xc9, 0x02, 0x43, 0x93, 0x23, 0x43, 0xbf, 0x83, 0x19, 0xb2, 0x9a, 0xa0, 0x8a, 0x03, 0x8e, 0x29, 0x03, 0x02, 0x0b, 0xa0, 0xa0, 0x8b, 0x0a, 0x13, 0x0b, 0x12, 0x9a, 0x10, 0x80, 0x87, 0x8f, 0x98, 0x89, 0x13, 0x0b, 0x83, 0x8e, 0x1a, 0x1a, 0x90, 0xab, 0xa2, 0x9b, 0xa5, 0xae, 0x22, 0x0a, 0x8b, 0xab, 0xa3, 0x0a, 0x0e, 0x02, 0x8e, 0x0f, 0x32, 0x3b, 0x13, 0x0b, 0x93, 0x91, 0x22, 0x0b, 0x90, 0xab, 0xb2, 0x33, 0xa1, 0x21, 0xaa, 0xae, 0xa3, 0x93, 0x73, 0xc2, 0x67, 0x81, 0xc7, 0x0a, 0x31, 0xa2, 0x7b, 0x93, 0xa7, 0x60, 0x86, 0xce, 0x53, 0x18, 0x53, 0x52, 0xc6, 0x5b, 0x47, 0x1a, 0x0b, 0x98, 0x5b, 0xda, 0x92, 0x14, 0x07, 0x82, 0x70, 0xc3, 0x02, 0xd2, 0xe1, 0x42, 0x42, 0x47, 0xe3, 0x20, 0x9a, 0xea, 0xe6, 0x02, 0x2a, 0x8f, 0xf3, 0x3a, 0x22, 0x7a, 0xf1, 0x58, 0x97, 0xeb, 0x41, 0x59, 0xe2, 0x73, 0xdd, 0xa7, 0x7e, 0x1f, };