summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/igs036crypt.cpp
blob: 352717c45d017d91adea25158ca793c629886d4d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
// 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<<shift;
	uint16_t l = 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,
};