trunk/src/mame/machine/315-5881_crypt.c
r0 | r241644 | |
| 1 | /* |
| 2 | re: Tecmo World Cup '98 (ST-V) (from ANY) |
| 3 | |
| 4 | I got one of the card in subject open it up to check the rom version |
| 5 | and made a discovery... |
| 6 | The protection chip has the part number on it "315-5881", it's the same |
| 7 | used on naomi M2 carts as you can see here |
| 8 | http://imagizer.imageshack.us/a/img540/7634/BsqvD8.jpg |
| 9 | |
| 10 | The same chip 315-5881 but with a Lattice IspLSI2032 (Sega part |
| 11 | 315-6050) was used on some Model3 games... |
| 12 | |
| 13 | */ |
| 14 | |
| 15 | #include "emu.h" |
| 16 | #include "machine/315-5881_crypt.h" |
| 17 | |
| 18 | extern const device_type SEGA315_5881_CRYPT = &device_creator<sega_315_5881_crypt_device>; |
| 19 | |
| 20 | |
| 21 | sega_315_5881_crypt_device::sega_315_5881_crypt_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
| 22 | : device_t(mconfig, SEGA315_5881_CRYPT, "Sega 'SEGA315_5881' Encryption device", tag, owner, clock, "SEGA315_5881", __FILE__) |
| 23 | { |
| 24 | } |
| 25 | |
| 26 | |
| 27 | |
| 28 | void sega_315_5881_crypt_device::device_start() |
| 29 | { |
| 30 | buffer = auto_alloc_array(machine(), UINT8, BUFFER_SIZE); |
| 31 | line_buffer = auto_alloc_array(machine(), UINT8, LINE_SIZE); |
| 32 | line_buffer_prev = auto_alloc_array(machine(), UINT8, LINE_SIZE); |
| 33 | |
| 34 | m_read.bind_relative_to(*owner()); |
| 35 | |
| 36 | save_pointer(NAME(buffer), BUFFER_SIZE); |
| 37 | save_pointer(NAME(line_buffer), LINE_SIZE); |
| 38 | save_pointer(NAME(line_buffer_prev), LINE_SIZE); |
| 39 | save_item(NAME(prot_cur_address)); |
| 40 | save_item(NAME(subkey)); |
| 41 | save_item(NAME(enc_ready)); |
| 42 | save_item(NAME(dec_hist)); |
| 43 | save_item(NAME(dec_header)); |
| 44 | save_item(NAME(buffer_pos)); |
| 45 | save_item(NAME(line_buffer_pos)); |
| 46 | save_item(NAME(line_buffer_size)); |
| 47 | |
| 48 | } |
| 49 | |
| 50 | void sega_315_5881_crypt_device::device_reset() |
| 51 | { |
| 52 | memset(buffer, 0, BUFFER_SIZE); |
| 53 | memset(line_buffer, 0, LINE_SIZE); |
| 54 | memset(line_buffer_prev, 0, LINE_SIZE); |
| 55 | |
| 56 | prot_cur_address = 0; |
| 57 | subkey = 0; |
| 58 | dec_hist = 0; |
| 59 | dec_header = 0; |
| 60 | enc_ready = false; |
| 61 | |
| 62 | buffer_pos = 0; |
| 63 | line_buffer_pos = 0; |
| 64 | line_buffer_size = 0; |
| 65 | buffer_bit = 0; |
| 66 | } |
| 67 | |
| 68 | void sega_315_5881_crypt_device::do_decrypt(UINT8 *&base) |
| 69 | { |
| 70 | if(!enc_ready) |
| 71 | enc_start(); |
| 72 | if(dec_header & FLAG_COMPRESSED) { |
| 73 | if(line_buffer_pos == line_buffer_size) |
| 74 | line_fill(); |
| 75 | base = line_buffer + line_buffer_pos; |
| 76 | line_buffer_pos += 2; |
| 77 | } else { |
| 78 | if(buffer_pos == BUFFER_SIZE) |
| 79 | enc_fill(); |
| 80 | base = buffer + buffer_pos; |
| 81 | buffer_pos += 2; |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | void sega_315_5881_crypt_device::set_addr_low(UINT16 data) |
| 86 | { |
| 87 | prot_cur_address = (prot_cur_address & 0xffff0000) | data; |
| 88 | enc_ready = false; |
| 89 | } |
| 90 | |
| 91 | void sega_315_5881_crypt_device::set_addr_high(UINT16 data) |
| 92 | { |
| 93 | prot_cur_address = (prot_cur_address & 0x0000ffff) | (data << 16); |
| 94 | enc_ready = false; |
| 95 | } |
| 96 | |
| 97 | void sega_315_5881_crypt_device::set_subkey(UINT16 data) |
| 98 | { |
| 99 | subkey = data; |
| 100 | enc_ready = false; |
| 101 | } |
| 102 | |
| 103 | void sega_315_5881_crypt_device::set_key(UINT32 data) |
| 104 | { |
| 105 | key = data; |
| 106 | enc_ready = false; |
| 107 | } |
| 108 | |
| 109 | /*************************************************************************** |
| 110 | DECRYPTION EMULATION |
| 111 | |
| 112 | By convention, we label the three known cart protection methods this way (using Deunan Knute's wording): |
| 113 | M1: DMA read of protected ROM area |
| 114 | M2: special read of ROM area which supplies decryption key first |
| 115 | M3: normal read followed by write to cart's decryption buffer (up to 64kB), followed by M2 but from buffer area |
| 116 | |
| 117 | Notes below refer to M2 & M3. |
| 118 | |
| 119 | The encryption is done by a stream cipher operating in counter mode, which use a 16-bits internal block cipher. |
| 120 | |
| 121 | There are 2 "control bits" at the start of the decrypted stream which control the mode of operation: bit #1 set to 1 means |
| 122 | that the decrypted stream needs to be decompressed after being decrypted. More on this later. |
| 123 | |
| 124 | The next 16-bits are part of the header (they don't belong to the plaintext), but his meaning is unclear. It has been |
| 125 | conjectured that it could stablish when to "reset" the process and start processing a new stream (based on some tests |
| 126 | on WWFROYAL, in which the decryption's output doesn't seem to be valid for more than some dozens of words), but some |
| 127 | more testing would be needed for clarifying that. |
| 128 | |
| 129 | After those 18 heading bits, we find the proper plaintext. It must be noted that, due to the initial 2 special bits, |
| 130 | the 16-bits words of the plaintext are shifted 2 bits respect to the word-boundaries of the output stream of the |
| 131 | internal block-cipher. So, at a given step, the internal block cipher will output 16-bits, 14 of which will go to a |
| 132 | given plaintext word, and the remaining 2 to the next plaintext word. |
| 133 | |
| 134 | The underlying block cipher consists of two 4-round Feistel Networks (FN): the first one takes the counter (16 bits), |
| 135 | the game-key (>=26 bits) and the sequence-key (16 bits) and output a middle result (16 bits) which will act as another key |
| 136 | for the second one. The second FN will take the encrypted word (16 bits), the game-key, the sequence-key and the result |
| 137 | from the first FN and will output the decrypted word (16 bits). |
| 138 | |
| 139 | Each round of the Feistel Networks use four substitution sboxes, each having 6 inputs and 2 outputs. The input can be the |
| 140 | XOR of at most two "sources bits", being source bits the bits from the previous round and the bits from the different keys. |
| 141 | |
| 142 | The underlying block cipher has the same structure than the one used by the CPS-2 (Capcom Play System 2) and, |
| 143 | indeed, some of the used sboxes are exactly the same and appear in the same FN/round in both systems (this is not evident, |
| 144 | as you need to apply a bitswapping and some XORs to the input & output of the sboxes to get the same values due). However, |
| 145 | the key scheduling used by this implementation is much weaker than the CPS-2's one. Many s-boxes inputs aren't XORed with any |
| 146 | key bit. |
| 147 | |
| 148 | Due to the small key-length, no sophisticated attacks are needed to recover the keys; a brute-force attack knowing just |
| 149 | some (encrypted word-decrypted word) pairs suffice. However, due to the weak key scheduling, it should be noted that some |
| 150 | related keys can produce the same output bytes for some (short) input sequences. |
| 151 | |
| 152 | The only difference in the decryption process between M2 and M3 is the initialization of the counter. In M3, the counter is |
| 153 | always set to 0 at the beginning of the decryption while, in M2, the bits #1-#16 of the ciphertext's address are used |
| 154 | to initialize the counter. |
| 155 | |
| 156 | Note that this implementation considers that the counter initialization for ram decryption is 0 simply because the ram is |
| 157 | mapped to multiples of 128K. |
| 158 | |
| 159 | Due to the nature of the cipher, there are some degrees of freedom when choosing the s-boxes and keys values; by example, |
| 160 | you could apply a fixed bitswapping and XOR to the keys and the decryption would remain the same as long as you change |
| 161 | accordingly the s-boxes' definitions. So the order of the bits in the keys is arbitrary, and the s-boxes values have been |
| 162 | chosen so as to make the key for CAPSNK equal to 0. |
| 163 | |
| 164 | It can be observed that a couple of sboxes have incomplete tables (a 255 value indicate an unknown value). The recovered keys |
| 165 | as of december/2010 show small randomness and big correlations, making possible that some unseen bits could make the |
| 166 | decryption need those incomplete parts. |
| 167 | |
| 168 | ****************************************************************************************/ |
| 169 | |
| 170 | const sega_315_5881_crypt_device::sbox sega_315_5881_crypt_device::fn1_sboxes[4][4] = { |
| 171 | { // 1st round |
| 172 | { |
| 173 | { |
| 174 | 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, |
| 175 | 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, |
| 176 | }, |
| 177 | {3,4,5,7,-1,-1}, |
| 178 | {0,4} |
| 179 | }, |
| 180 | |
| 181 | { |
| 182 | { |
| 183 | 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, |
| 184 | 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, |
| 185 | }, |
| 186 | {0,1,2,5,6,7}, |
| 187 | {1,6} |
| 188 | }, |
| 189 | |
| 190 | { |
| 191 | { |
| 192 | 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, |
| 193 | 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, |
| 194 | }, |
| 195 | {0,2,5,6,7,-1}, |
| 196 | {2,3} |
| 197 | }, |
| 198 | |
| 199 | { |
| 200 | { |
| 201 | 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, |
| 202 | 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, |
| 203 | }, |
| 204 | {1,2,3,4,6,7}, |
| 205 | {5,7} |
| 206 | }, |
| 207 | }, |
| 208 | { // 2nd round |
| 209 | { |
| 210 | { |
| 211 | 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, |
| 212 | 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, |
| 213 | }, |
| 214 | {0,1,3,4,5,7}, |
| 215 | {0,4} |
| 216 | }, |
| 217 | |
| 218 | { |
| 219 | { |
| 220 | 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, |
| 221 | 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, |
| 222 | }, |
| 223 | {0,1,3,4,6,-1}, |
| 224 | {1,5} |
| 225 | }, |
| 226 | |
| 227 | { |
| 228 | { |
| 229 | 2,2,2,3,1,1,0,1,0,1,2,2,3,3,0,2,0,3,2,3,3,0,2,1,0,3,1,0,0,2,3,2, |
| 230 | 3,2,0,3,2,0,1,0,3,3,1,1,2,2,2,0,2,1,3,1,1,1,1,2,2,2,3,0,1,3,0,0, |
| 231 | }, |
| 232 | {1,2,5,6,7,-1}, |
| 233 | {2,7} |
| 234 | }, |
| 235 | |
| 236 | { |
| 237 | { |
| 238 | 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, |
| 239 | 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, |
| 240 | }, |
| 241 | {2,3,4,5,6,7}, |
| 242 | {3,6} |
| 243 | }, |
| 244 | }, |
| 245 | { // 3rd round |
| 246 | { |
| 247 | { |
| 248 | 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, |
| 249 | 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, |
| 250 | }, |
| 251 | {1,2,3,4,5,7}, |
| 252 | {0,5} |
| 253 | }, |
| 254 | |
| 255 | { |
| 256 | { |
| 257 | 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, |
| 258 | 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, |
| 259 | }, |
| 260 | {0,2,4,5,6,7}, |
| 261 | {1,6} |
| 262 | }, |
| 263 | |
| 264 | { |
| 265 | { |
| 266 | 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, |
| 267 | // unused? |
| 268 | 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, |
| 269 | }, |
| 270 | {0,2,4,6,7,-1}, |
| 271 | {2,3} |
| 272 | }, |
| 273 | |
| 274 | { |
| 275 | { |
| 276 | 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, |
| 277 | 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, |
| 278 | }, |
| 279 | {0,1,2,3,6,-1}, |
| 280 | {4,7} |
| 281 | }, |
| 282 | }, |
| 283 | { // 4th round |
| 284 | { |
| 285 | { |
| 286 | 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, |
| 287 | 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, |
| 288 | }, |
| 289 | {0,1,3,4,5,6}, |
| 290 | {0,5} |
| 291 | }, |
| 292 | |
| 293 | { |
| 294 | { |
| 295 | 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, |
| 296 | 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, |
| 297 | }, |
| 298 | {0,1,2,3,5,7}, |
| 299 | {1,7} |
| 300 | }, |
| 301 | |
| 302 | { |
| 303 | { |
| 304 | 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, |
| 305 | 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, |
| 306 | }, |
| 307 | {2,3,4,5,6,7}, |
| 308 | {2,3} |
| 309 | }, |
| 310 | |
| 311 | { |
| 312 | { |
| 313 | 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, |
| 314 | 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, |
| 315 | }, |
| 316 | {0,1,2,4,6,7}, |
| 317 | {4,6} |
| 318 | }, |
| 319 | }, |
| 320 | }; |
| 321 | |
| 322 | |
| 323 | const sega_315_5881_crypt_device::sbox sega_315_5881_crypt_device::fn2_sboxes[4][4] = { |
| 324 | { // 1st round |
| 325 | { |
| 326 | { |
| 327 | 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, |
| 328 | 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, |
| 329 | }, |
| 330 | {1,3,4,5,6,7}, |
| 331 | {0,7} |
| 332 | }, |
| 333 | |
| 334 | { |
| 335 | { |
| 336 | 0,2,3,2,1,1,0,0,2,1,0,3,3,0,0,0,3,2,0,2,1,1,2,1,0,0,3,1,2,2,3,1, |
| 337 | 3,1,3,0,0,0,1,3,1,0,0,3,2,2,3,1,1,3,0,0,2,1,3,3,1,3,1,2,3,1,2,1, |
| 338 | }, |
| 339 | {0,3,5,6,-1,-1}, |
| 340 | {1,2} |
| 341 | }, |
| 342 | |
| 343 | { |
| 344 | { |
| 345 | 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, |
| 346 | 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, |
| 347 | }, |
| 348 | {0,2,3,4,7,-1}, |
| 349 | {3,4} |
| 350 | }, |
| 351 | |
| 352 | { |
| 353 | { |
| 354 | 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, |
| 355 | 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, |
| 356 | }, |
| 357 | {1,2,5,6,-1,-1}, |
| 358 | {5,6} |
| 359 | }, |
| 360 | }, |
| 361 | { // 2nd round |
| 362 | { |
| 363 | { |
| 364 | 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, |
| 365 | 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, |
| 366 | }, |
| 367 | {0,1,4,5,6,7}, |
| 368 | {0,7} |
| 369 | }, |
| 370 | |
| 371 | { |
| 372 | { |
| 373 | 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, |
| 374 | 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, |
| 375 | }, |
| 376 | {0,2,3,6,7,-1}, |
| 377 | {1,5} |
| 378 | }, |
| 379 | |
| 380 | { |
| 381 | { |
| 382 | 1,2,3,2,0,3,2,3,0,1,1,0,0,2,2,3,2,0,0,3,0,2,3,3,2,2,1,0,2,1,0,3, |
| 383 | 1,0,2,0,1,1,0,1,0,0,1,0,3,0,3,3,2,2,0,2,1,1,1,0,3,0,1,3,2,3,2,1, |
| 384 | }, |
| 385 | {2,3,4,6,7,-1}, |
| 386 | {2,3} |
| 387 | }, |
| 388 | |
| 389 | { |
| 390 | { |
| 391 | 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, |
| 392 | 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, |
| 393 | }, |
| 394 | {1,2,3,4,5,6}, |
| 395 | {4,6} |
| 396 | }, |
| 397 | }, |
| 398 | { // 3rd round |
| 399 | { |
| 400 | { |
| 401 | 0,3,0,1,0,2,3,3,1,0,1,3,2,2,1,1,3,3,3,0,2,0,2,0,0,0,2,3,1,1,0,0, |
| 402 | 3,3,0,3,3,0,0,2,1,1,1,0,2,2,2,0,3,0,3,1,2,2,0,3,0,0,3,2,0,3,2,1, |
| 403 | }, |
| 404 | {1,4,5,6,7,-1}, |
| 405 | {0,5} |
| 406 | }, |
| 407 | |
| 408 | { |
| 409 | { |
| 410 | 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, |
| 411 | 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, |
| 412 | }, |
| 413 | {0,2,3,4,6,7}, |
| 414 | {1,7} |
| 415 | }, |
| 416 | |
| 417 | { |
| 418 | { |
| 419 | 2,2,3,2,0,3,2,3,1,1,2,0,2,3,1,3,0,0,0,3,2,0,1,0,1,3,2,3,3,3,1,0, |
| 420 | // unused? |
| 421 | 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, |
| 422 | }, |
| 423 | {1,2,4,7,-1,-1}, |
| 424 | {2,4} |
| 425 | }, |
| 426 | |
| 427 | { |
| 428 | { |
| 429 | 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, |
| 430 | 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, |
| 431 | }, |
| 432 | {0,1,2,3,5,7}, |
| 433 | {3,6} |
| 434 | }, |
| 435 | }, |
| 436 | { // 4th round |
| 437 | { |
| 438 | { |
| 439 | 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, |
| 440 | 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, |
| 441 | }, |
| 442 | {0,1,2,4,7,-1}, |
| 443 | {0,5} |
| 444 | }, |
| 445 | |
| 446 | { |
| 447 | { |
| 448 | 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, |
| 449 | 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, |
| 450 | }, |
| 451 | {0,1,3,5,6,-1}, |
| 452 | {1,3} |
| 453 | }, |
| 454 | |
| 455 | { |
| 456 | { |
| 457 | 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, |
| 458 | 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, |
| 459 | }, |
| 460 | {0,2,3,4,5,7}, |
| 461 | {2,7} |
| 462 | }, |
| 463 | |
| 464 | { |
| 465 | { |
| 466 | 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, |
| 467 | 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, |
| 468 | }, |
| 469 | {2,3,4,5,6,7}, |
| 470 | {4,6} |
| 471 | }, |
| 472 | }, |
| 473 | }; |
| 474 | |
| 475 | const int sega_315_5881_crypt_device::fn1_game_key_scheduling[38][2] = { |
| 476 | {1,29}, {1,71}, {2,4}, {2,54}, {3,8}, {4,56}, {4,73}, {5,11}, |
| 477 | {6,51}, {7,92}, {8,89}, {9,9}, {9,10}, {9,39}, {9,41}, {9,58}, |
| 478 | {9,59}, {9,86}, {10,90}, {11,6}, {12,64}, {13,49}, {14,44}, {15,40}, |
| 479 | {16,69}, {17,15}, {18,23}, {18,43}, {19,82}, {20,81}, {21,32}, {21,61}, |
| 480 | {22,5}, {23,66}, {24,13}, {24,45}, {25,12}, {25,35} |
| 481 | }; |
| 482 | |
| 483 | const int sega_315_5881_crypt_device::fn2_game_key_scheduling[34][2] = { |
| 484 | {0,0}, {1,3}, {2,11}, {3,20}, {4,22}, {5,23}, {6,29}, {7,38}, |
| 485 | {8,39}, {9,47}, {9,55}, {9,86}, {9,87}, {9,90}, {10,50}, {10,53}, |
| 486 | {11,57}, {12,59}, {13,61}, {13,64}, {14,63}, {15,67}, {16,72}, {17,83}, |
| 487 | {18,88}, {19,94}, {20,35}, {21,17}, {21,92}, {22,6}, {22,11}, {23,85}, |
| 488 | {24,16}, {25,25} |
| 489 | }; |
| 490 | |
| 491 | const int sega_315_5881_crypt_device::fn1_sequence_key_scheduling[20][2] = { |
| 492 | {0,52}, {1,34}, {2,17}, {3,36}, {4,84}, {4,88}, {5,57}, {6,48}, |
| 493 | {6,68}, {7,76}, {8,83}, {9,30}, {10,22}, {10,41}, {11,38}, {12,55}, |
| 494 | {13,74}, {14,19}, {14,80}, {15,26} |
| 495 | }; |
| 496 | |
| 497 | const int sega_315_5881_crypt_device::fn2_sequence_key_scheduling[16] = {77,34,8,42,36,27,69,66,13,9,79,31,49,7,24,64}; |
| 498 | |
| 499 | const int sega_315_5881_crypt_device::fn2_middle_result_scheduling[16] = {1,10,44,68,74,78,81,95,2,4,30,40,41,51,53,58}; |
| 500 | |
| 501 | int sega_315_5881_crypt_device::feistel_function(int input, const struct sbox *sboxes, UINT32 subkeys) |
| 502 | { |
| 503 | int k,m; |
| 504 | int aux; |
| 505 | int result=0; |
| 506 | |
| 507 | for (m=0; m<4; ++m) { // 4 sboxes |
| 508 | for (k=0, aux=0; k<6; ++k) |
| 509 | if (sboxes[m].inputs[k]!=-1) |
| 510 | aux |= BIT(input, sboxes[m].inputs[k]) << k; |
| 511 | |
| 512 | aux = sboxes[m].table[(aux^subkeys)&0x3f]; |
| 513 | |
| 514 | for (k=0; k<2; ++k) |
| 515 | result |= BIT(aux,k) << sboxes[m].outputs[k]; |
| 516 | |
| 517 | subkeys >>=6; |
| 518 | } |
| 519 | |
| 520 | return result; |
| 521 | } |
| 522 | |
| 523 | /************************** |
| 524 | This implementation is an "educational" version. It must be noted that it can be speed-optimized in a number of ways. |
| 525 | The most evident one is to factor out the parts of the key-scheduling that must only be done once (like the game-key & |
| 526 | sequence-key parts) as noted in the comments inlined in the function. More sophisticated speed-ups can be gained by |
| 527 | noticing that the weak key-scheduling would allow to create some pregenerated look-up tables for doing most of the work |
| 528 | of the function. Even so, it would still be pretty slow, so caching techniques could be a wiser option here. |
| 529 | **************************/ |
| 530 | |
| 531 | UINT16 sega_315_5881_crypt_device::block_decrypt(UINT32 game_key, UINT16 sequence_key, UINT16 counter, UINT16 data) |
| 532 | { |
| 533 | int j; |
| 534 | int aux,aux2; |
| 535 | int A,B; |
| 536 | int middle_result; |
| 537 | UINT32 fn1_subkeys[4]; |
| 538 | UINT32 fn2_subkeys[4]; |
| 539 | |
| 540 | /* Game-key scheduling; this could be done just once per game at initialization time */ |
| 541 | memset(fn1_subkeys,0,sizeof(UINT32)*4); |
| 542 | memset(fn2_subkeys,0,sizeof(UINT32)*4); |
| 543 | |
| 544 | for (j=0; j<38; ++j) { |
| 545 | if (BIT(game_key, fn1_game_key_scheduling[j][0])!=0) { |
| 546 | aux = fn1_game_key_scheduling[j][1]%24; |
| 547 | aux2 = fn1_game_key_scheduling[j][1]/24; |
| 548 | fn1_subkeys[aux2] ^= (1<<aux); |
| 549 | } |
| 550 | } |
| 551 | |
| 552 | for (j=0; j<34; ++j) { |
| 553 | if (BIT(game_key, fn2_game_key_scheduling[j][0])!=0) { |
| 554 | aux = fn2_game_key_scheduling[j][1]%24; |
| 555 | aux2 = fn2_game_key_scheduling[j][1]/24; |
| 556 | fn2_subkeys[aux2] ^= (1<<aux); |
| 557 | } |
| 558 | } |
| 559 | /********************************************************/ |
| 560 | |
| 561 | /* Sequence-key scheduling; this could be done just once per decryption run */ |
| 562 | for (j=0; j<20; ++j) { |
| 563 | if (BIT(sequence_key,fn1_sequence_key_scheduling[j][0])!=0) { |
| 564 | aux = fn1_sequence_key_scheduling[j][1]%24; |
| 565 | aux2 = fn1_sequence_key_scheduling[j][1]/24; |
| 566 | fn1_subkeys[aux2] ^= (1<<aux); |
| 567 | } |
| 568 | } |
| 569 | |
| 570 | for (j=0; j<16; ++j) { |
| 571 | if (BIT(sequence_key,j)!=0) { |
| 572 | aux = fn2_sequence_key_scheduling[j]%24; |
| 573 | aux2 = fn2_sequence_key_scheduling[j]/24; |
| 574 | fn2_subkeys[aux2] ^= (1<<aux); |
| 575 | } |
| 576 | } |
| 577 | |
| 578 | // subkeys bits 10 & 41 |
| 579 | fn2_subkeys[0] ^= (BIT(sequence_key,2)<<10); |
| 580 | fn2_subkeys[1] ^= (BIT(sequence_key,4)<<17); |
| 581 | /**************************************************************/ |
| 582 | |
| 583 | // First Feistel Network |
| 584 | |
| 585 | aux = BITSWAP16(counter,5,12,14,13,9,3,6,4, 8,1,15,11,0,7,10,2); |
| 586 | |
| 587 | // 1st round |
| 588 | B = aux >> 8; |
| 589 | A = (aux & 0xff) ^ feistel_function(B,fn1_sboxes[0],fn1_subkeys[0]); |
| 590 | |
| 591 | // 2nd round |
| 592 | B = B ^ feistel_function(A,fn1_sboxes[1],fn1_subkeys[1]); |
| 593 | |
| 594 | // 3rd round |
| 595 | A = A ^ feistel_function(B,fn1_sboxes[2],fn1_subkeys[2]); |
| 596 | |
| 597 | // 4th round |
| 598 | B = B ^ feistel_function(A,fn1_sboxes[3],fn1_subkeys[3]); |
| 599 | |
| 600 | middle_result = (B<<8)|A; |
| 601 | |
| 602 | |
| 603 | /* Middle-result-key sheduling */ |
| 604 | for (j=0; j<16; ++j) { |
| 605 | if (BIT(middle_result,j)!=0) { |
| 606 | aux = fn2_middle_result_scheduling[j]%24; |
| 607 | aux2 = fn2_middle_result_scheduling[j]/24; |
| 608 | fn2_subkeys[aux2] ^= (1<<aux); |
| 609 | } |
| 610 | } |
| 611 | /*********************/ |
| 612 | |
| 613 | // Second Feistel Network |
| 614 | |
| 615 | aux = BITSWAP16(data,14,3,8,12,13,7,15,4, 6,2,9,5,11,0,1,10); |
| 616 | |
| 617 | // 1st round |
| 618 | B = aux >> 8; |
| 619 | A = (aux & 0xff) ^ feistel_function(B,fn2_sboxes[0],fn2_subkeys[0]); |
| 620 | |
| 621 | // 2nd round |
| 622 | B = B ^ feistel_function(A,fn2_sboxes[1],fn2_subkeys[1]); |
| 623 | |
| 624 | // 3rd round |
| 625 | A = A ^ feistel_function(B,fn2_sboxes[2],fn2_subkeys[2]); |
| 626 | |
| 627 | // 4th round |
| 628 | B = B ^ feistel_function(A,fn2_sboxes[3],fn2_subkeys[3]); |
| 629 | |
| 630 | aux = (B<<8)|A; |
| 631 | |
| 632 | aux = BITSWAP16(aux,15,7,6,14,13,12,5,4, 3,2,11,10,9,1,0,8); |
| 633 | |
| 634 | return aux; |
| 635 | } |
| 636 | |
| 637 | UINT16 sega_315_5881_crypt_device::get_decrypted_16() |
| 638 | { |
| 639 | UINT16 enc; |
| 640 | |
| 641 | enc = m_read(prot_cur_address); |
| 642 | |
| 643 | UINT16 dec = block_decrypt(key, subkey, prot_cur_address, enc); |
| 644 | UINT16 res = (dec & 3) | (dec_hist & 0xfffc); |
| 645 | dec_hist = dec; |
| 646 | |
| 647 | prot_cur_address ++; |
| 648 | return res; |
| 649 | } |
| 650 | |
| 651 | void sega_315_5881_crypt_device::enc_start() |
| 652 | { |
| 653 | buffer_pos = BUFFER_SIZE; |
| 654 | dec_header = get_decrypted_16() << 16; |
| 655 | dec_header |= get_decrypted_16(); |
| 656 | |
| 657 | if(dec_header & FLAG_COMPRESSED) { |
| 658 | line_buffer_size = dec_header & FLAG_LINE_SIZE_512 ? 512 : 256; |
| 659 | line_buffer_pos = line_buffer_size; |
| 660 | buffer_bit = 7; |
| 661 | } |
| 662 | enc_ready = true; |
| 663 | } |
| 664 | |
| 665 | void sega_315_5881_crypt_device::enc_fill() |
| 666 | { |
| 667 | assert(buffer_pos == BUFFER_SIZE); |
| 668 | for(int i = 0; i != BUFFER_SIZE; i+=2) { |
| 669 | UINT16 val = get_decrypted_16(); |
| 670 | buffer[i] = val; |
| 671 | buffer[i+1] = val >> 8; |
| 672 | } |
| 673 | buffer_pos = 0; |
| 674 | } |
| 675 | |
| 676 | /* node format |
| 677 | 0xxxxxxx - next node index |
| 678 | 1a0bbccc - end node |
| 679 | a - 0 = repeat |
| 680 | 1 = fetch |
| 681 | b - if a = 1 |
| 682 | 00 - fetch 0 |
| 683 | 01 - fetch 1 |
| 684 | 11 - fetch -1 |
| 685 | if a = 0 |
| 686 | 000 |
| 687 | c - repeat/fetch counter |
| 688 | count = ccc + 1 |
| 689 | 11111111 - empty node |
| 690 | */ |
| 691 | const UINT8 sega_315_5881_crypt_device::trees[9][2][32] = { |
| 692 | { |
| 693 | {0x01,0x10,0x0f,0x05,0xc4,0x13,0x87,0x0a,0xcc,0x81,0xce,0x0c,0x86,0x0e,0x84,0xc2, |
| 694 | 0x11,0xc1,0xc3,0xcf,0x15,0xc8,0xcd,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
| 695 | {0xc7,0x02,0x03,0x04,0x80,0x06,0x07,0x08,0x09,0xc9,0x0b,0x0d,0x82,0x83,0x85,0xc0, |
| 696 | 0x12,0xc6,0xc5,0x14,0x16,0xca,0xcb,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
| 697 | }, |
| 698 | { |
| 699 | {0x02,0x80,0x05,0x04,0x81,0x10,0x15,0x82,0x09,0x83,0x0b,0x0c,0x0d,0xdc,0x0f,0xde, |
| 700 | 0x1c,0xcf,0xc5,0xdd,0x86,0x16,0x87,0x18,0x19,0x1a,0xda,0xca,0xc9,0x1e,0xce,0xff,}, |
| 701 | {0x01,0x17,0x03,0x0a,0x08,0x06,0x07,0xc2,0xd9,0xc4,0xd8,0xc8,0x0e,0x84,0xcb,0x85, |
| 702 | 0x11,0x12,0x13,0x14,0xcd,0x1b,0xdb,0xc7,0xc0,0xc1,0x1d,0xdf,0xc3,0xc6,0xcc,0xff,}, |
| 703 | }, |
| 704 | { |
| 705 | {0xc6,0x80,0x03,0x0b,0x05,0x07,0x82,0x08,0x15,0xdc,0xdd,0x0c,0xd9,0xc2,0x14,0x10, |
| 706 | 0x85,0x86,0x18,0x16,0xc5,0xc4,0xc8,0xc9,0xc0,0xcc,0xff,0xff,0xff,0xff,0xff,0xff,}, |
| 707 | {0x01,0x02,0x12,0x04,0x81,0x06,0x83,0xc3,0x09,0x0a,0x84,0x11,0x0d,0x0e,0x0f,0x19, |
| 708 | 0xca,0xc1,0x13,0xd8,0xda,0xdb,0x17,0xde,0xcd,0xcb,0xff,0xff,0xff,0xff,0xff,0xff,}, |
| 709 | }, |
| 710 | { |
| 711 | {0x01,0x80,0x0d,0x04,0x05,0x15,0x83,0x08,0xd9,0x10,0x0b,0x0c,0x84,0x0e,0xc0,0x14, |
| 712 | 0x12,0xcb,0x13,0xca,0xc8,0xc2,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
| 713 | {0xc5,0x02,0x03,0x07,0x81,0x06,0x82,0xcc,0x09,0x0a,0xc9,0x11,0xc4,0x0f,0x85,0xd8, |
| 714 | 0xda,0xdb,0xc3,0xdc,0xdd,0xc1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
| 715 | }, |
| 716 | { |
| 717 | {0x01,0x80,0x06,0x0c,0x05,0x81,0xd8,0x84,0x09,0xdc,0x0b,0x0f,0x0d,0x0e,0x10,0xdb, |
| 718 | 0x11,0xca,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
| 719 | {0xc4,0x02,0x03,0x04,0xcb,0x0a,0x07,0x08,0xd9,0x82,0xc8,0x83,0xc0,0xc1,0xda,0xc2, |
| 720 | 0xc9,0xc3,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
| 721 | }, |
| 722 | { |
| 723 | {0x01,0x02,0x06,0x0a,0x83,0x0b,0x07,0x08,0x09,0x82,0xd8,0x0c,0xd9,0xda,0xff,0xff, |
| 724 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
| 725 | {0xc3,0x80,0x03,0x04,0x05,0x81,0xca,0xc8,0xdb,0xc9,0xc0,0xc1,0x0d,0xc2,0xff,0xff, |
| 726 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
| 727 | }, |
| 728 | { |
| 729 | {0x01,0x02,0x03,0x04,0x81,0x07,0x08,0xd8,0xda,0xd9,0xff,0xff,0xff,0xff,0xff,0xff, |
| 730 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
| 731 | {0xc2,0x80,0x05,0xc9,0xc8,0x06,0x82,0xc0,0x09,0xc1,0xff,0xff,0xff,0xff,0xff,0xff, |
| 732 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
| 733 | }, |
| 734 | { |
| 735 | {0x01,0x80,0x04,0xc8,0xc0,0xd9,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
| 736 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
| 737 | {0xc1,0x02,0x03,0x81,0x05,0xd8,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
| 738 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
| 739 | }, |
| 740 | { |
| 741 | {0x01,0xd8,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
| 742 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
| 743 | {0xc0,0x80,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
| 744 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
| 745 | }, |
| 746 | }; |
| 747 | |
| 748 | int sega_315_5881_crypt_device::get_compressed_bit() |
| 749 | { |
| 750 | if(buffer_pos == BUFFER_SIZE) |
| 751 | enc_fill(); |
| 752 | int res = (buffer[buffer_pos^1] >> buffer_bit) & 1; |
| 753 | buffer_bit--; |
| 754 | if(buffer_bit == -1) { |
| 755 | buffer_bit = 7; |
| 756 | buffer_pos++; |
| 757 | } |
| 758 | return res; |
| 759 | } |
| 760 | |
| 761 | void sega_315_5881_crypt_device::line_fill() |
| 762 | { |
| 763 | assert(line_buffer_pos == line_buffer_size); |
| 764 | UINT8 *lp = line_buffer; |
| 765 | UINT8 *lc = line_buffer_prev; |
| 766 | line_buffer = lc; |
| 767 | line_buffer_prev = lp; |
| 768 | line_buffer_pos = 0; |
| 769 | |
| 770 | UINT32 line_buffer_mask = line_buffer_size-1; |
| 771 | |
| 772 | for(int i=0; i != line_buffer_size;) { |
| 773 | // vlc 0: start of line |
| 774 | // vlc 1: interior of line |
| 775 | // vlc 2-9: 7-1 bytes from end of line |
| 776 | |
| 777 | int slot = i ? i < line_buffer_size - 7 ? 1 : (i & 7) + 1 : 0; |
| 778 | |
| 779 | UINT32 tmp = 0; |
| 780 | while (!(tmp&0x80)) |
| 781 | if(get_compressed_bit()) |
| 782 | tmp = trees[slot][1][tmp]; |
| 783 | else |
| 784 | tmp = trees[slot][0][tmp]; |
| 785 | if(tmp != 0xff) { |
| 786 | int count = (tmp & 7) + 1; |
| 787 | |
| 788 | if(tmp&0x40) { |
| 789 | // Copy from previous line |
| 790 | |
| 791 | static int offsets[4] = {0, 1, 0, -1}; |
| 792 | int offset = offsets[(tmp & 0x18) >> 3]; |
| 793 | for(int j=0; j != count; j++) { |
| 794 | lc[i^1] = lp[((i+offset) & line_buffer_mask)^1]; |
| 795 | i++; |
| 796 | } |
| 797 | |
| 798 | } else { |
| 799 | // Get a byte in the stream and write n times |
| 800 | UINT8 byte; |
| 801 | byte = get_compressed_bit() << 1; |
| 802 | byte = (byte | get_compressed_bit()) << 1; |
| 803 | byte = (byte | get_compressed_bit()) << 1; |
| 804 | byte = (byte | get_compressed_bit()) << 1; |
| 805 | byte = (byte | get_compressed_bit()) << 1; |
| 806 | byte = (byte | get_compressed_bit()) << 1; |
| 807 | byte = (byte | get_compressed_bit()) << 1; |
| 808 | byte = byte | get_compressed_bit(); |
| 809 | for(int j=0; j != count; j++) |
| 810 | lc[(i++)^1] = byte; |
| 811 | |
| 812 | } |
| 813 | } |
| 814 | } |
| 815 | } |
| | No newline at end of file |
trunk/src/mame/machine/naomim2.c
r241643 | r241644 | |
105 | 105 | const device_type NAOMI_M2_BOARD = &device_creator<naomi_m2_board>; |
106 | 106 | |
107 | 107 | naomi_m2_board::naomi_m2_board(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) |
108 | | : naomi_board(mconfig, NAOMI_M2_BOARD, "Sega NAOMI M2 BOARD", tag, owner, clock, "naomi_m2_board", __FILE__) |
| 108 | : naomi_board(mconfig, NAOMI_M2_BOARD, "Sega NAOMI M2 BOARD", tag, owner, clock, "naomi_m2_board", __FILE__), |
| 109 | m_cryptdevice(*this, "segam2crypt") |
109 | 110 | { |
110 | 111 | key_tag = 0; |
111 | 112 | } |
r241643 | r241644 | |
120 | 121 | { |
121 | 122 | naomi_board::device_start(); |
122 | 123 | |
123 | | #if USE_NAOMICRYPT |
124 | | key = get_naomi_key(machine()); |
125 | | #else |
126 | | const UINT8 *key_data = memregion(key_tag)->base(); |
127 | | key = (key_data[0] << 24) | (key_data[1] << 16) | (key_data[2] << 8) | key_data[3]; |
128 | | #endif |
129 | 124 | ram = auto_alloc_array(machine(), UINT8, RAM_SIZE); |
130 | | buffer = auto_alloc_array(machine(), UINT8, BUFFER_SIZE); |
131 | | line_buffer = auto_alloc_array(machine(), UINT8, LINE_SIZE); |
132 | | line_buffer_prev = auto_alloc_array(machine(), UINT8, LINE_SIZE); |
133 | 125 | |
134 | | save_pointer(NAME(ram), RAM_SIZE); |
135 | | save_pointer(NAME(buffer), BUFFER_SIZE); |
136 | | save_pointer(NAME(line_buffer), LINE_SIZE); |
137 | | save_pointer(NAME(line_buffer_prev), LINE_SIZE); |
138 | 126 | save_item(NAME(rom_cur_address)); |
139 | | save_item(NAME(prot_cur_address)); |
140 | | save_item(NAME(subkey)); |
141 | | save_item(NAME(enc_ready)); |
142 | | save_item(NAME(dec_hist)); |
143 | | save_item(NAME(dec_header)); |
144 | | save_item(NAME(buffer_pos)); |
145 | | save_item(NAME(line_buffer_pos)); |
146 | | save_item(NAME(line_buffer_size)); |
| 127 | save_pointer(NAME(ram), RAM_SIZE); |
147 | 128 | } |
148 | 129 | |
149 | 130 | void naomi_m2_board::device_reset() |
r241643 | r241644 | |
151 | 132 | naomi_board::device_reset(); |
152 | 133 | |
153 | 134 | memset(ram, 0, RAM_SIZE); |
154 | | memset(buffer, 0, BUFFER_SIZE); |
155 | | memset(line_buffer, 0, LINE_SIZE); |
156 | | memset(line_buffer_prev, 0, LINE_SIZE); |
157 | 135 | |
158 | 136 | rom_cur_address = 0; |
159 | | prot_cur_address = 0; |
160 | | subkey = 0; |
161 | | dec_hist = 0; |
162 | | dec_header = 0; |
163 | | enc_ready = false; |
164 | 137 | |
165 | | buffer_pos = 0; |
166 | | line_buffer_pos = 0; |
167 | | line_buffer_size = 0; |
168 | | buffer_bit = 0; |
| 138 | #if USE_NAOMICRYPT |
| 139 | m_cryptdevice->set_key(get_naomi_key(machine())); |
| 140 | #else |
| 141 | const UINT8 *key_data = memregion(key_tag)->base(); |
| 142 | m_cryptdevice->set_key((key_data[0] << 24) | (key_data[1] << 16) | (key_data[2] << 8) | key_data[3]); |
| 143 | #endif |
169 | 144 | } |
170 | 145 | |
171 | 146 | void naomi_m2_board::board_setup_address(UINT32 address, bool is_dma) |
r241643 | r241644 | |
175 | 150 | |
176 | 151 | void naomi_m2_board::board_get_buffer(UINT8 *&base, UINT32 &limit) |
177 | 152 | { |
| 153 | |
178 | 154 | if(rom_cur_address & 0x40000000) { |
179 | 155 | if(rom_cur_address == 0x4001fffe) { |
180 | | if(!enc_ready) |
181 | | enc_start(); |
182 | | if(dec_header & FLAG_COMPRESSED) { |
183 | | if(line_buffer_pos == line_buffer_size) |
184 | | line_fill(); |
185 | | base = line_buffer + line_buffer_pos; |
186 | | line_buffer_pos += 2; |
187 | | } else { |
188 | | if(buffer_pos == BUFFER_SIZE) |
189 | | enc_fill(); |
190 | | base = buffer + buffer_pos; |
191 | | buffer_pos += 2; |
192 | | } |
| 156 | m_cryptdevice->do_decrypt(base); |
193 | 157 | limit = 2; |
194 | 158 | |
195 | 159 | } else |
r241643 | r241644 | |
214 | 178 | |
215 | 179 | void naomi_m2_board::board_write(offs_t offset, UINT16 data) |
216 | 180 | { |
| 181 | |
| 182 | |
217 | 183 | if(offset & 0x40000000) { |
218 | 184 | if((offset & 0x0f000000) == 0x02000000) { |
219 | 185 | offset &= RAM_SIZE-1; |
r241643 | r241644 | |
222 | 188 | return; |
223 | 189 | } |
224 | 190 | switch(offset & 0x1fffffff) { |
225 | | case 0x1fff8: prot_cur_address = (prot_cur_address & 0xffff0000) | data; enc_ready = false; return; |
226 | | case 0x1fffa: prot_cur_address = (prot_cur_address & 0x0000ffff) | (data << 16); enc_ready = false; return; |
227 | | case 0x1fffc: subkey = data; enc_ready = false; return; |
| 191 | |
| 192 | case 0x1fff8: m_cryptdevice->set_addr_low(data); return; |
| 193 | case 0x1fffa: m_cryptdevice->set_addr_high(data); return; |
| 194 | case 0x1fffc: m_cryptdevice->set_subkey(data); return; |
228 | 195 | } |
229 | 196 | } |
230 | 197 | throw emu_fatalerror("NAOMIM2: unhandled board write %08x, %04x\n", offset, data); |
231 | 198 | } |
232 | 199 | |
233 | | /*************************************************************************** |
234 | | DECRYPTION EMULATION |
235 | | |
236 | | By convention, we label the three known cart protection methods this way (using Deunan Knute's wording): |
237 | | M1: DMA read of protected ROM area |
238 | | M2: special read of ROM area which supplies decryption key first |
239 | | M3: normal read followed by write to cart's decryption buffer (up to 64kB), followed by M2 but from buffer area |
240 | | |
241 | | Notes below refer to M2 & M3. |
242 | | |
243 | | The encryption is done by a stream cipher operating in counter mode, which use a 16-bits internal block cipher. |
244 | | |
245 | | There are 2 "control bits" at the start of the decrypted stream which control the mode of operation: bit #1 set to 1 means |
246 | | that the decrypted stream needs to be decompressed after being decrypted. More on this later. |
247 | | |
248 | | The next 16-bits are part of the header (they don't belong to the plaintext), but his meaning is unclear. It has been |
249 | | conjectured that it could stablish when to "reset" the process and start processing a new stream (based on some tests |
250 | | on WWFROYAL, in which the decryption's output doesn't seem to be valid for more than some dozens of words), but some |
251 | | more testing would be needed for clarifying that. |
252 | | |
253 | | After those 18 heading bits, we find the proper plaintext. It must be noted that, due to the initial 2 special bits, |
254 | | the 16-bits words of the plaintext are shifted 2 bits respect to the word-boundaries of the output stream of the |
255 | | internal block-cipher. So, at a given step, the internal block cipher will output 16-bits, 14 of which will go to a |
256 | | given plaintext word, and the remaining 2 to the next plaintext word. |
257 | | |
258 | | The underlying block cipher consists of two 4-round Feistel Networks (FN): the first one takes the counter (16 bits), |
259 | | the game-key (>=26 bits) and the sequence-key (16 bits) and output a middle result (16 bits) which will act as another key |
260 | | for the second one. The second FN will take the encrypted word (16 bits), the game-key, the sequence-key and the result |
261 | | from the first FN and will output the decrypted word (16 bits). |
262 | | |
263 | | Each round of the Feistel Networks use four substitution sboxes, each having 6 inputs and 2 outputs. The input can be the |
264 | | XOR of at most two "sources bits", being source bits the bits from the previous round and the bits from the different keys. |
265 | | |
266 | | The underlying block cipher has the same structure than the one used by the CPS-2 (Capcom Play System 2) and, |
267 | | indeed, some of the used sboxes are exactly the same and appear in the same FN/round in both systems (this is not evident, |
268 | | as you need to apply a bitswapping and some XORs to the input & output of the sboxes to get the same values due). However, |
269 | | the key scheduling used by this implementation is much weaker than the CPS-2's one. Many s-boxes inputs aren't XORed with any |
270 | | key bit. |
271 | | |
272 | | Due to the small key-length, no sophisticated attacks are needed to recover the keys; a brute-force attack knowing just |
273 | | some (encrypted word-decrypted word) pairs suffice. However, due to the weak key scheduling, it should be noted that some |
274 | | related keys can produce the same output bytes for some (short) input sequences. |
275 | | |
276 | | The only difference in the decryption process between M2 and M3 is the initialization of the counter. In M3, the counter is |
277 | | always set to 0 at the beginning of the decryption while, in M2, the bits #1-#16 of the ciphertext's address are used |
278 | | to initialize the counter. |
279 | | |
280 | | Note that this implementation considers that the counter initialization for ram decryption is 0 simply because the ram is |
281 | | mapped to multiples of 128K. |
282 | | |
283 | | Due to the nature of the cipher, there are some degrees of freedom when choosing the s-boxes and keys values; by example, |
284 | | you could apply a fixed bitswapping and XOR to the keys and the decryption would remain the same as long as you change |
285 | | accordingly the s-boxes' definitions. So the order of the bits in the keys is arbitrary, and the s-boxes values have been |
286 | | chosen so as to make the key for CAPSNK equal to 0. |
287 | | |
288 | | It can be observed that a couple of sboxes have incomplete tables (a 255 value indicate an unknown value). The recovered keys |
289 | | as of december/2010 show small randomness and big correlations, making possible that some unseen bits could make the |
290 | | decryption need those incomplete parts. |
291 | | |
292 | | ****************************************************************************************/ |
293 | | |
294 | | const naomi_m2_board::sbox naomi_m2_board::fn1_sboxes[4][4] = { |
295 | | { // 1st round |
296 | | { |
297 | | { |
298 | | 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, |
299 | | 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, |
300 | | }, |
301 | | {3,4,5,7,-1,-1}, |
302 | | {0,4} |
303 | | }, |
304 | | |
305 | | { |
306 | | { |
307 | | 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, |
308 | | 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, |
309 | | }, |
310 | | {0,1,2,5,6,7}, |
311 | | {1,6} |
312 | | }, |
313 | | |
314 | | { |
315 | | { |
316 | | 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, |
317 | | 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, |
318 | | }, |
319 | | {0,2,5,6,7,-1}, |
320 | | {2,3} |
321 | | }, |
322 | | |
323 | | { |
324 | | { |
325 | | 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, |
326 | | 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, |
327 | | }, |
328 | | {1,2,3,4,6,7}, |
329 | | {5,7} |
330 | | }, |
331 | | }, |
332 | | { // 2nd round |
333 | | { |
334 | | { |
335 | | 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, |
336 | | 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, |
337 | | }, |
338 | | {0,1,3,4,5,7}, |
339 | | {0,4} |
340 | | }, |
341 | | |
342 | | { |
343 | | { |
344 | | 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, |
345 | | 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, |
346 | | }, |
347 | | {0,1,3,4,6,-1}, |
348 | | {1,5} |
349 | | }, |
350 | | |
351 | | { |
352 | | { |
353 | | 2,2,2,3,1,1,0,1,0,1,2,2,3,3,0,2,0,3,2,3,3,0,2,1,0,3,1,0,0,2,3,2, |
354 | | 3,2,0,3,2,0,1,0,3,3,1,1,2,2,2,0,2,1,3,1,1,1,1,2,2,2,3,0,1,3,0,0, |
355 | | }, |
356 | | {1,2,5,6,7,-1}, |
357 | | {2,7} |
358 | | }, |
359 | | |
360 | | { |
361 | | { |
362 | | 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, |
363 | | 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, |
364 | | }, |
365 | | {2,3,4,5,6,7}, |
366 | | {3,6} |
367 | | }, |
368 | | }, |
369 | | { // 3rd round |
370 | | { |
371 | | { |
372 | | 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, |
373 | | 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, |
374 | | }, |
375 | | {1,2,3,4,5,7}, |
376 | | {0,5} |
377 | | }, |
378 | | |
379 | | { |
380 | | { |
381 | | 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, |
382 | | 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, |
383 | | }, |
384 | | {0,2,4,5,6,7}, |
385 | | {1,6} |
386 | | }, |
387 | | |
388 | | { |
389 | | { |
390 | | 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, |
391 | | // unused? |
392 | | 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, |
393 | | }, |
394 | | {0,2,4,6,7,-1}, |
395 | | {2,3} |
396 | | }, |
397 | | |
398 | | { |
399 | | { |
400 | | 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, |
401 | | 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, |
402 | | }, |
403 | | {0,1,2,3,6,-1}, |
404 | | {4,7} |
405 | | }, |
406 | | }, |
407 | | { // 4th round |
408 | | { |
409 | | { |
410 | | 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, |
411 | | 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, |
412 | | }, |
413 | | {0,1,3,4,5,6}, |
414 | | {0,5} |
415 | | }, |
416 | | |
417 | | { |
418 | | { |
419 | | 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, |
420 | | 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, |
421 | | }, |
422 | | {0,1,2,3,5,7}, |
423 | | {1,7} |
424 | | }, |
425 | | |
426 | | { |
427 | | { |
428 | | 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, |
429 | | 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, |
430 | | }, |
431 | | {2,3,4,5,6,7}, |
432 | | {2,3} |
433 | | }, |
434 | | |
435 | | { |
436 | | { |
437 | | 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, |
438 | | 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, |
439 | | }, |
440 | | {0,1,2,4,6,7}, |
441 | | {4,6} |
442 | | }, |
443 | | }, |
444 | | }; |
445 | | |
446 | | |
447 | | const naomi_m2_board::sbox naomi_m2_board::fn2_sboxes[4][4] = { |
448 | | { // 1st round |
449 | | { |
450 | | { |
451 | | 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, |
452 | | 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, |
453 | | }, |
454 | | {1,3,4,5,6,7}, |
455 | | {0,7} |
456 | | }, |
457 | | |
458 | | { |
459 | | { |
460 | | 0,2,3,2,1,1,0,0,2,1,0,3,3,0,0,0,3,2,0,2,1,1,2,1,0,0,3,1,2,2,3,1, |
461 | | 3,1,3,0,0,0,1,3,1,0,0,3,2,2,3,1,1,3,0,0,2,1,3,3,1,3,1,2,3,1,2,1, |
462 | | }, |
463 | | {0,3,5,6,-1,-1}, |
464 | | {1,2} |
465 | | }, |
466 | | |
467 | | { |
468 | | { |
469 | | 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, |
470 | | 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, |
471 | | }, |
472 | | {0,2,3,4,7,-1}, |
473 | | {3,4} |
474 | | }, |
475 | | |
476 | | { |
477 | | { |
478 | | 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, |
479 | | 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, |
480 | | }, |
481 | | {1,2,5,6,-1,-1}, |
482 | | {5,6} |
483 | | }, |
484 | | }, |
485 | | { // 2nd round |
486 | | { |
487 | | { |
488 | | 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, |
489 | | 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, |
490 | | }, |
491 | | {0,1,4,5,6,7}, |
492 | | {0,7} |
493 | | }, |
494 | | |
495 | | { |
496 | | { |
497 | | 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, |
498 | | 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, |
499 | | }, |
500 | | {0,2,3,6,7,-1}, |
501 | | {1,5} |
502 | | }, |
503 | | |
504 | | { |
505 | | { |
506 | | 1,2,3,2,0,3,2,3,0,1,1,0,0,2,2,3,2,0,0,3,0,2,3,3,2,2,1,0,2,1,0,3, |
507 | | 1,0,2,0,1,1,0,1,0,0,1,0,3,0,3,3,2,2,0,2,1,1,1,0,3,0,1,3,2,3,2,1, |
508 | | }, |
509 | | {2,3,4,6,7,-1}, |
510 | | {2,3} |
511 | | }, |
512 | | |
513 | | { |
514 | | { |
515 | | 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, |
516 | | 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, |
517 | | }, |
518 | | {1,2,3,4,5,6}, |
519 | | {4,6} |
520 | | }, |
521 | | }, |
522 | | { // 3rd round |
523 | | { |
524 | | { |
525 | | 0,3,0,1,0,2,3,3,1,0,1,3,2,2,1,1,3,3,3,0,2,0,2,0,0,0,2,3,1,1,0,0, |
526 | | 3,3,0,3,3,0,0,2,1,1,1,0,2,2,2,0,3,0,3,1,2,2,0,3,0,0,3,2,0,3,2,1, |
527 | | }, |
528 | | {1,4,5,6,7,-1}, |
529 | | {0,5} |
530 | | }, |
531 | | |
532 | | { |
533 | | { |
534 | | 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, |
535 | | 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, |
536 | | }, |
537 | | {0,2,3,4,6,7}, |
538 | | {1,7} |
539 | | }, |
540 | | |
541 | | { |
542 | | { |
543 | | 2,2,3,2,0,3,2,3,1,1,2,0,2,3,1,3,0,0,0,3,2,0,1,0,1,3,2,3,3,3,1,0, |
544 | | // unused? |
545 | | 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, |
546 | | }, |
547 | | {1,2,4,7,-1,-1}, |
548 | | {2,4} |
549 | | }, |
550 | | |
551 | | { |
552 | | { |
553 | | 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, |
554 | | 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, |
555 | | }, |
556 | | {0,1,2,3,5,7}, |
557 | | {3,6} |
558 | | }, |
559 | | }, |
560 | | { // 4th round |
561 | | { |
562 | | { |
563 | | 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, |
564 | | 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, |
565 | | }, |
566 | | {0,1,2,4,7,-1}, |
567 | | {0,5} |
568 | | }, |
569 | | |
570 | | { |
571 | | { |
572 | | 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, |
573 | | 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, |
574 | | }, |
575 | | {0,1,3,5,6,-1}, |
576 | | {1,3} |
577 | | }, |
578 | | |
579 | | { |
580 | | { |
581 | | 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, |
582 | | 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, |
583 | | }, |
584 | | {0,2,3,4,5,7}, |
585 | | {2,7} |
586 | | }, |
587 | | |
588 | | { |
589 | | { |
590 | | 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, |
591 | | 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, |
592 | | }, |
593 | | {2,3,4,5,6,7}, |
594 | | {4,6} |
595 | | }, |
596 | | }, |
597 | | }; |
598 | | |
599 | | const int naomi_m2_board::fn1_game_key_scheduling[38][2] = { |
600 | | {1,29}, {1,71}, {2,4}, {2,54}, {3,8}, {4,56}, {4,73}, {5,11}, |
601 | | {6,51}, {7,92}, {8,89}, {9,9}, {9,10}, {9,39}, {9,41}, {9,58}, |
602 | | {9,59}, {9,86}, {10,90}, {11,6}, {12,64}, {13,49}, {14,44}, {15,40}, |
603 | | {16,69}, {17,15}, {18,23}, {18,43}, {19,82}, {20,81}, {21,32}, {21,61}, |
604 | | {22,5}, {23,66}, {24,13}, {24,45}, {25,12}, {25,35} |
605 | | }; |
606 | | |
607 | | const int naomi_m2_board::fn2_game_key_scheduling[34][2] = { |
608 | | {0,0}, {1,3}, {2,11}, {3,20}, {4,22}, {5,23}, {6,29}, {7,38}, |
609 | | {8,39}, {9,47}, {9,55}, {9,86}, {9,87}, {9,90}, {10,50}, {10,53}, |
610 | | {11,57}, {12,59}, {13,61}, {13,64}, {14,63}, {15,67}, {16,72}, {17,83}, |
611 | | {18,88}, {19,94}, {20,35}, {21,17}, {21,92}, {22,6}, {22,11}, {23,85}, |
612 | | {24,16}, {25,25} |
613 | | }; |
614 | | |
615 | | const int naomi_m2_board::fn1_sequence_key_scheduling[20][2] = { |
616 | | {0,52}, {1,34}, {2,17}, {3,36}, {4,84}, {4,88}, {5,57}, {6,48}, |
617 | | {6,68}, {7,76}, {8,83}, {9,30}, {10,22}, {10,41}, {11,38}, {12,55}, |
618 | | {13,74}, {14,19}, {14,80}, {15,26} |
619 | | }; |
620 | | |
621 | | const int naomi_m2_board::fn2_sequence_key_scheduling[16] = {77,34,8,42,36,27,69,66,13,9,79,31,49,7,24,64}; |
622 | | |
623 | | const int naomi_m2_board::fn2_middle_result_scheduling[16] = {1,10,44,68,74,78,81,95,2,4,30,40,41,51,53,58}; |
624 | | |
625 | | int naomi_m2_board::feistel_function(int input, const struct sbox *sboxes, UINT32 subkeys) |
| 200 | UINT16 naomi_m2_board::read_callback(UINT32 addr) |
626 | 201 | { |
627 | | int k,m; |
628 | | int aux; |
629 | | int result=0; |
| 202 | if ((addr & 0xffff0000) == 0x01000000) { |
| 203 | int base = 2*(addr & 0x7fff); |
| 204 | return ram[base+1] | (ram[base] << 8); |
630 | 205 | |
631 | | for (m=0; m<4; ++m) { // 4 sboxes |
632 | | for (k=0, aux=0; k<6; ++k) |
633 | | if (sboxes[m].inputs[k]!=-1) |
634 | | aux |= BIT(input, sboxes[m].inputs[k]) << k; |
635 | | |
636 | | aux = sboxes[m].table[(aux^subkeys)&0x3f]; |
637 | | |
638 | | for (k=0; k<2; ++k) |
639 | | result |= BIT(aux,k) << sboxes[m].outputs[k]; |
640 | | |
641 | | subkeys >>=6; |
642 | 206 | } |
643 | | |
644 | | return result; |
645 | | } |
646 | | |
647 | | /************************** |
648 | | This implementation is an "educational" version. It must be noted that it can be speed-optimized in a number of ways. |
649 | | The most evident one is to factor out the parts of the key-scheduling that must only be done once (like the game-key & |
650 | | sequence-key parts) as noted in the comments inlined in the function. More sophisticated speed-ups can be gained by |
651 | | noticing that the weak key-scheduling would allow to create some pregenerated look-up tables for doing most of the work |
652 | | of the function. Even so, it would still be pretty slow, so caching techniques could be a wiser option here. |
653 | | **************************/ |
654 | | |
655 | | UINT16 naomi_m2_board::block_decrypt(UINT32 game_key, UINT16 sequence_key, UINT16 counter, UINT16 data) |
656 | | { |
657 | | int j; |
658 | | int aux,aux2; |
659 | | int A,B; |
660 | | int middle_result; |
661 | | UINT32 fn1_subkeys[4]; |
662 | | UINT32 fn2_subkeys[4]; |
663 | | |
664 | | /* Game-key scheduling; this could be done just once per game at initialization time */ |
665 | | memset(fn1_subkeys,0,sizeof(UINT32)*4); |
666 | | memset(fn2_subkeys,0,sizeof(UINT32)*4); |
667 | | |
668 | | for (j=0; j<38; ++j) { |
669 | | if (BIT(game_key, fn1_game_key_scheduling[j][0])!=0) { |
670 | | aux = fn1_game_key_scheduling[j][1]%24; |
671 | | aux2 = fn1_game_key_scheduling[j][1]/24; |
672 | | fn1_subkeys[aux2] ^= (1<<aux); |
673 | | } |
| 207 | else { |
| 208 | const UINT8 *base = m_region->base() + 2*addr; |
| 209 | return base[1] | (base[0] << 8); |
674 | 210 | } |
675 | | |
676 | | for (j=0; j<34; ++j) { |
677 | | if (BIT(game_key, fn2_game_key_scheduling[j][0])!=0) { |
678 | | aux = fn2_game_key_scheduling[j][1]%24; |
679 | | aux2 = fn2_game_key_scheduling[j][1]/24; |
680 | | fn2_subkeys[aux2] ^= (1<<aux); |
681 | | } |
682 | | } |
683 | | /********************************************************/ |
684 | | |
685 | | /* Sequence-key scheduling; this could be done just once per decryption run */ |
686 | | for (j=0; j<20; ++j) { |
687 | | if (BIT(sequence_key,fn1_sequence_key_scheduling[j][0])!=0) { |
688 | | aux = fn1_sequence_key_scheduling[j][1]%24; |
689 | | aux2 = fn1_sequence_key_scheduling[j][1]/24; |
690 | | fn1_subkeys[aux2] ^= (1<<aux); |
691 | | } |
692 | | } |
693 | | |
694 | | for (j=0; j<16; ++j) { |
695 | | if (BIT(sequence_key,j)!=0) { |
696 | | aux = fn2_sequence_key_scheduling[j]%24; |
697 | | aux2 = fn2_sequence_key_scheduling[j]/24; |
698 | | fn2_subkeys[aux2] ^= (1<<aux); |
699 | | } |
700 | | } |
701 | | |
702 | | // subkeys bits 10 & 41 |
703 | | fn2_subkeys[0] ^= (BIT(sequence_key,2)<<10); |
704 | | fn2_subkeys[1] ^= (BIT(sequence_key,4)<<17); |
705 | | /**************************************************************/ |
706 | | |
707 | | // First Feistel Network |
708 | | |
709 | | aux = BITSWAP16(counter,5,12,14,13,9,3,6,4, 8,1,15,11,0,7,10,2); |
710 | | |
711 | | // 1st round |
712 | | B = aux >> 8; |
713 | | A = (aux & 0xff) ^ feistel_function(B,fn1_sboxes[0],fn1_subkeys[0]); |
714 | | |
715 | | // 2nd round |
716 | | B = B ^ feistel_function(A,fn1_sboxes[1],fn1_subkeys[1]); |
717 | | |
718 | | // 3rd round |
719 | | A = A ^ feistel_function(B,fn1_sboxes[2],fn1_subkeys[2]); |
720 | | |
721 | | // 4th round |
722 | | B = B ^ feistel_function(A,fn1_sboxes[3],fn1_subkeys[3]); |
723 | | |
724 | | middle_result = (B<<8)|A; |
725 | | |
726 | | |
727 | | /* Middle-result-key sheduling */ |
728 | | for (j=0; j<16; ++j) { |
729 | | if (BIT(middle_result,j)!=0) { |
730 | | aux = fn2_middle_result_scheduling[j]%24; |
731 | | aux2 = fn2_middle_result_scheduling[j]/24; |
732 | | fn2_subkeys[aux2] ^= (1<<aux); |
733 | | } |
734 | | } |
735 | | /*********************/ |
736 | | |
737 | | // Second Feistel Network |
738 | | |
739 | | aux = BITSWAP16(data,14,3,8,12,13,7,15,4, 6,2,9,5,11,0,1,10); |
740 | | |
741 | | // 1st round |
742 | | B = aux >> 8; |
743 | | A = (aux & 0xff) ^ feistel_function(B,fn2_sboxes[0],fn2_subkeys[0]); |
744 | | |
745 | | // 2nd round |
746 | | B = B ^ feistel_function(A,fn2_sboxes[1],fn2_subkeys[1]); |
747 | | |
748 | | // 3rd round |
749 | | A = A ^ feistel_function(B,fn2_sboxes[2],fn2_subkeys[2]); |
750 | | |
751 | | // 4th round |
752 | | B = B ^ feistel_function(A,fn2_sboxes[3],fn2_subkeys[3]); |
753 | | |
754 | | aux = (B<<8)|A; |
755 | | |
756 | | aux = BITSWAP16(aux,15,7,6,14,13,12,5,4, 3,2,11,10,9,1,0,8); |
757 | | |
758 | | return aux; |
759 | 211 | } |
760 | 212 | |
761 | | UINT16 naomi_m2_board::get_decrypted_16() |
762 | | { |
763 | | UINT16 enc; |
| 213 | static MACHINE_CONFIG_FRAGMENT( naomim2 ) |
| 214 | MCFG_DEVICE_ADD("segam2crypt", SEGA315_5881_CRYPT, 0) |
| 215 | MCFG_SET_READ_CALLBACK(naomi_m2_board, read_callback) |
| 216 | MACHINE_CONFIG_END |
764 | 217 | |
765 | | if((prot_cur_address & 0xffff0000) == 0x01000000) { |
766 | | int base = 2*(prot_cur_address & 0x7fff); |
767 | | enc = ram[base+1] | (ram[base] << 8); |
768 | | } else { |
769 | | const UINT8 *base = m_region->base() + 2*prot_cur_address; |
770 | | enc = base[1] | (base[0] << 8); |
771 | | } |
772 | | |
773 | | UINT16 dec = block_decrypt(key, subkey, prot_cur_address, enc); |
774 | | UINT16 res = (dec & 3) | (dec_hist & 0xfffc); |
775 | | dec_hist = dec; |
776 | | |
777 | | prot_cur_address ++; |
778 | | return res; |
779 | | } |
780 | | |
781 | | void naomi_m2_board::enc_start() |
| 218 | machine_config_constructor naomi_m2_board::device_mconfig_additions() const |
782 | 219 | { |
783 | | buffer_pos = BUFFER_SIZE; |
784 | | dec_header = get_decrypted_16() << 16; |
785 | | dec_header |= get_decrypted_16(); |
786 | | |
787 | | if(dec_header & FLAG_COMPRESSED) { |
788 | | line_buffer_size = dec_header & FLAG_LINE_SIZE_512 ? 512 : 256; |
789 | | line_buffer_pos = line_buffer_size; |
790 | | buffer_bit = 7; |
791 | | } |
792 | | enc_ready = true; |
| 220 | return MACHINE_CONFIG_NAME( naomim2 ); |
793 | 221 | } |
794 | 222 | |
795 | | void naomi_m2_board::enc_fill() |
796 | | { |
797 | | assert(buffer_pos == BUFFER_SIZE); |
798 | | for(int i = 0; i != BUFFER_SIZE; i+=2) { |
799 | | UINT16 val = get_decrypted_16(); |
800 | | buffer[i] = val; |
801 | | buffer[i+1] = val >> 8; |
802 | | } |
803 | | buffer_pos = 0; |
804 | | } |
805 | | |
806 | | /* node format |
807 | | 0xxxxxxx - next node index |
808 | | 1a0bbccc - end node |
809 | | a - 0 = repeat |
810 | | 1 = fetch |
811 | | b - if a = 1 |
812 | | 00 - fetch 0 |
813 | | 01 - fetch 1 |
814 | | 11 - fetch -1 |
815 | | if a = 0 |
816 | | 000 |
817 | | c - repeat/fetch counter |
818 | | count = ccc + 1 |
819 | | 11111111 - empty node |
820 | | */ |
821 | | const UINT8 naomi_m2_board::trees[9][2][32] = { |
822 | | { |
823 | | {0x01,0x10,0x0f,0x05,0xc4,0x13,0x87,0x0a,0xcc,0x81,0xce,0x0c,0x86,0x0e,0x84,0xc2, |
824 | | 0x11,0xc1,0xc3,0xcf,0x15,0xc8,0xcd,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
825 | | {0xc7,0x02,0x03,0x04,0x80,0x06,0x07,0x08,0x09,0xc9,0x0b,0x0d,0x82,0x83,0x85,0xc0, |
826 | | 0x12,0xc6,0xc5,0x14,0x16,0xca,0xcb,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
827 | | }, |
828 | | { |
829 | | {0x02,0x80,0x05,0x04,0x81,0x10,0x15,0x82,0x09,0x83,0x0b,0x0c,0x0d,0xdc,0x0f,0xde, |
830 | | 0x1c,0xcf,0xc5,0xdd,0x86,0x16,0x87,0x18,0x19,0x1a,0xda,0xca,0xc9,0x1e,0xce,0xff,}, |
831 | | {0x01,0x17,0x03,0x0a,0x08,0x06,0x07,0xc2,0xd9,0xc4,0xd8,0xc8,0x0e,0x84,0xcb,0x85, |
832 | | 0x11,0x12,0x13,0x14,0xcd,0x1b,0xdb,0xc7,0xc0,0xc1,0x1d,0xdf,0xc3,0xc6,0xcc,0xff,}, |
833 | | }, |
834 | | { |
835 | | {0xc6,0x80,0x03,0x0b,0x05,0x07,0x82,0x08,0x15,0xdc,0xdd,0x0c,0xd9,0xc2,0x14,0x10, |
836 | | 0x85,0x86,0x18,0x16,0xc5,0xc4,0xc8,0xc9,0xc0,0xcc,0xff,0xff,0xff,0xff,0xff,0xff,}, |
837 | | {0x01,0x02,0x12,0x04,0x81,0x06,0x83,0xc3,0x09,0x0a,0x84,0x11,0x0d,0x0e,0x0f,0x19, |
838 | | 0xca,0xc1,0x13,0xd8,0xda,0xdb,0x17,0xde,0xcd,0xcb,0xff,0xff,0xff,0xff,0xff,0xff,}, |
839 | | }, |
840 | | { |
841 | | {0x01,0x80,0x0d,0x04,0x05,0x15,0x83,0x08,0xd9,0x10,0x0b,0x0c,0x84,0x0e,0xc0,0x14, |
842 | | 0x12,0xcb,0x13,0xca,0xc8,0xc2,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
843 | | {0xc5,0x02,0x03,0x07,0x81,0x06,0x82,0xcc,0x09,0x0a,0xc9,0x11,0xc4,0x0f,0x85,0xd8, |
844 | | 0xda,0xdb,0xc3,0xdc,0xdd,0xc1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
845 | | }, |
846 | | { |
847 | | {0x01,0x80,0x06,0x0c,0x05,0x81,0xd8,0x84,0x09,0xdc,0x0b,0x0f,0x0d,0x0e,0x10,0xdb, |
848 | | 0x11,0xca,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
849 | | {0xc4,0x02,0x03,0x04,0xcb,0x0a,0x07,0x08,0xd9,0x82,0xc8,0x83,0xc0,0xc1,0xda,0xc2, |
850 | | 0xc9,0xc3,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
851 | | }, |
852 | | { |
853 | | {0x01,0x02,0x06,0x0a,0x83,0x0b,0x07,0x08,0x09,0x82,0xd8,0x0c,0xd9,0xda,0xff,0xff, |
854 | | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
855 | | {0xc3,0x80,0x03,0x04,0x05,0x81,0xca,0xc8,0xdb,0xc9,0xc0,0xc1,0x0d,0xc2,0xff,0xff, |
856 | | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
857 | | }, |
858 | | { |
859 | | {0x01,0x02,0x03,0x04,0x81,0x07,0x08,0xd8,0xda,0xd9,0xff,0xff,0xff,0xff,0xff,0xff, |
860 | | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
861 | | {0xc2,0x80,0x05,0xc9,0xc8,0x06,0x82,0xc0,0x09,0xc1,0xff,0xff,0xff,0xff,0xff,0xff, |
862 | | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
863 | | }, |
864 | | { |
865 | | {0x01,0x80,0x04,0xc8,0xc0,0xd9,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
866 | | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
867 | | {0xc1,0x02,0x03,0x81,0x05,0xd8,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
868 | | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
869 | | }, |
870 | | { |
871 | | {0x01,0xd8,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
872 | | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
873 | | {0xc0,0x80,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, |
874 | | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, |
875 | | }, |
876 | | }; |
877 | | |
878 | | int naomi_m2_board::get_compressed_bit() |
879 | | { |
880 | | if(buffer_pos == BUFFER_SIZE) |
881 | | enc_fill(); |
882 | | int res = (buffer[buffer_pos^1] >> buffer_bit) & 1; |
883 | | buffer_bit--; |
884 | | if(buffer_bit == -1) { |
885 | | buffer_bit = 7; |
886 | | buffer_pos++; |
887 | | } |
888 | | return res; |
889 | | } |
890 | | |
891 | | void naomi_m2_board::line_fill() |
892 | | { |
893 | | assert(line_buffer_pos == line_buffer_size); |
894 | | UINT8 *lp = line_buffer; |
895 | | UINT8 *lc = line_buffer_prev; |
896 | | line_buffer = lc; |
897 | | line_buffer_prev = lp; |
898 | | line_buffer_pos = 0; |
899 | | |
900 | | UINT32 line_buffer_mask = line_buffer_size-1; |
901 | | |
902 | | for(int i=0; i != line_buffer_size;) { |
903 | | // vlc 0: start of line |
904 | | // vlc 1: interior of line |
905 | | // vlc 2-9: 7-1 bytes from end of line |
906 | | |
907 | | int slot = i ? i < line_buffer_size - 7 ? 1 : (i & 7) + 1 : 0; |
908 | | |
909 | | UINT32 tmp = 0; |
910 | | while (!(tmp&0x80)) |
911 | | if(get_compressed_bit()) |
912 | | tmp = trees[slot][1][tmp]; |
913 | | else |
914 | | tmp = trees[slot][0][tmp]; |
915 | | if(tmp != 0xff) { |
916 | | int count = (tmp & 7) + 1; |
917 | | |
918 | | if(tmp&0x40) { |
919 | | // Copy from previous line |
920 | | |
921 | | static int offsets[4] = {0, 1, 0, -1}; |
922 | | int offset = offsets[(tmp & 0x18) >> 3]; |
923 | | for(int j=0; j != count; j++) { |
924 | | lc[i^1] = lp[((i+offset) & line_buffer_mask)^1]; |
925 | | i++; |
926 | | } |
927 | | |
928 | | } else { |
929 | | // Get a byte in the stream and write n times |
930 | | UINT8 byte; |
931 | | byte = get_compressed_bit() << 1; |
932 | | byte = (byte | get_compressed_bit()) << 1; |
933 | | byte = (byte | get_compressed_bit()) << 1; |
934 | | byte = (byte | get_compressed_bit()) << 1; |
935 | | byte = (byte | get_compressed_bit()) << 1; |
936 | | byte = (byte | get_compressed_bit()) << 1; |
937 | | byte = (byte | get_compressed_bit()) << 1; |
938 | | byte = byte | get_compressed_bit(); |
939 | | for(int j=0; j != count; j++) |
940 | | lc[(i++)^1] = byte; |
941 | | |
942 | | } |
943 | | } |
944 | | } |
945 | | } |