diff options
author | mamehaze <mamehaze@users.noreply.github.com> | 2014-11-02 09:29:08 +0000 |
---|---|---|
committer | mamehaze <mamehaze@users.noreply.github.com> | 2014-11-02 09:29:08 +0000 |
commit | f1d9ba9df8c52c9441f49e9a1f6801c69109c7a2 (patch) | |
tree | 4edaa9a182aaf57293094f54a4c3600ff359eadd | |
parent | 2eb1a3a4f74d40c334e8750fd1cdd89eff74e89c (diff) |
move 315-5881 based encryption to it's own file, allowing me to experiment with the device for the encrypted ST-V games.
-rw-r--r-- | src/mame/drivers/stv.c | 3 | ||||
-rw-r--r-- | src/mame/includes/stv.h | 8 | ||||
-rw-r--r-- | src/mame/machine/315-5881_crypt.c | 815 | ||||
-rw-r--r-- | src/mame/machine/315-5881_crypt.h | 87 | ||||
-rw-r--r-- | src/mame/machine/naomim2.c | 785 | ||||
-rw-r--r-- | src/mame/machine/naomim2.h | 48 | ||||
-rw-r--r-- | src/mame/machine/stvprot.c | 10 | ||||
-rw-r--r-- | src/mame/mame.mak | 1 |
8 files changed, 963 insertions, 794 deletions
diff --git a/src/mame/drivers/stv.c b/src/mame/drivers/stv.c index a3dcc5c7f61..42b67c9eab9 100644 --- a/src/mame/drivers/stv.c +++ b/src/mame/drivers/stv.c @@ -1021,6 +1021,9 @@ static MACHINE_CONFIG_START( stv, stv_state ) MCFG_SOUND_ADD("cdda", CDDA, 0) MCFG_SOUND_ROUTE(0, "lspeaker", 1.0) MCFG_SOUND_ROUTE(1, "rspeaker", 1.0) + + MCFG_DEVICE_ADD("315_5881", SEGA315_5881_CRYPT, 0) + //MCFG_SET_READ_CALLBACK(stv_state, read_callback) MACHINE_CONFIG_END /* diff --git a/src/mame/includes/stv.h b/src/mame/includes/stv.h index 96d6fa09ab7..849db7a5f21 100644 --- a/src/mame/includes/stv.h +++ b/src/mame/includes/stv.h @@ -9,6 +9,8 @@ #include "bus/generic/slot.h" #include "bus/generic/carts.h" +#include "machine/315-5881_crypt.h" + #define MAX_FILTERS (24) #define MAX_BLOCKS (200) #define MAX_DIR_SIZE (256*1024) @@ -33,7 +35,8 @@ public: m_cart3(*this, "stv_slot3"), m_cart4(*this, "stv_slot4"), m_gfxdecode(*this, "gfxdecode"), - m_palette(*this, "palette") + m_palette(*this, "palette"), + m_cryptdevice(*this, "315_5881") { } @@ -167,6 +170,9 @@ public: required_device<gfxdecode_device> m_gfxdecode; required_device<palette_device> m_palette; + optional_device<sega_315_5881_crypt_device> m_cryptdevice; + + bitmap_rgb32 m_tmpbitmap; DECLARE_VIDEO_START(stv_vdp2); UINT32 screen_update_saturn(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); diff --git a/src/mame/machine/315-5881_crypt.c b/src/mame/machine/315-5881_crypt.c new file mode 100644 index 00000000000..7731d43a901 --- /dev/null +++ b/src/mame/machine/315-5881_crypt.c @@ -0,0 +1,815 @@ +/* + re: Tecmo World Cup '98 (ST-V) (from ANY) + + I got one of the card in subject open it up to check the rom version + and made a discovery... + The protection chip has the part number on it "315-5881", it's the same + used on naomi M2 carts as you can see here + http://imagizer.imageshack.us/a/img540/7634/BsqvD8.jpg + + The same chip 315-5881 but with a Lattice IspLSI2032 (Sega part + 315-6050) was used on some Model3 games... + +*/ + +#include "emu.h" +#include "machine/315-5881_crypt.h" + +extern const device_type SEGA315_5881_CRYPT = &device_creator<sega_315_5881_crypt_device>; + + +sega_315_5881_crypt_device::sega_315_5881_crypt_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, SEGA315_5881_CRYPT, "Sega 'SEGA315_5881' Encryption device", tag, owner, clock, "SEGA315_5881", __FILE__) +{ +} + + + +void sega_315_5881_crypt_device::device_start() +{ + buffer = auto_alloc_array(machine(), UINT8, BUFFER_SIZE); + line_buffer = auto_alloc_array(machine(), UINT8, LINE_SIZE); + line_buffer_prev = auto_alloc_array(machine(), UINT8, LINE_SIZE); + + m_read.bind_relative_to(*owner()); + + save_pointer(NAME(buffer), BUFFER_SIZE); + save_pointer(NAME(line_buffer), LINE_SIZE); + save_pointer(NAME(line_buffer_prev), LINE_SIZE); + save_item(NAME(prot_cur_address)); + save_item(NAME(subkey)); + save_item(NAME(enc_ready)); + save_item(NAME(dec_hist)); + save_item(NAME(dec_header)); + save_item(NAME(buffer_pos)); + save_item(NAME(line_buffer_pos)); + save_item(NAME(line_buffer_size)); + +} + +void sega_315_5881_crypt_device::device_reset() +{ + memset(buffer, 0, BUFFER_SIZE); + memset(line_buffer, 0, LINE_SIZE); + memset(line_buffer_prev, 0, LINE_SIZE); + + prot_cur_address = 0; + subkey = 0; + dec_hist = 0; + dec_header = 0; + enc_ready = false; + + buffer_pos = 0; + line_buffer_pos = 0; + line_buffer_size = 0; + buffer_bit = 0; +} + +void sega_315_5881_crypt_device::do_decrypt(UINT8 *&base) +{ + if(!enc_ready) + enc_start(); + if(dec_header & FLAG_COMPRESSED) { + if(line_buffer_pos == line_buffer_size) + line_fill(); + base = line_buffer + line_buffer_pos; + line_buffer_pos += 2; + } else { + if(buffer_pos == BUFFER_SIZE) + enc_fill(); + base = buffer + buffer_pos; + buffer_pos += 2; + } +} + +void sega_315_5881_crypt_device::set_addr_low(UINT16 data) +{ + prot_cur_address = (prot_cur_address & 0xffff0000) | data; + enc_ready = false; +} + +void sega_315_5881_crypt_device::set_addr_high(UINT16 data) +{ + prot_cur_address = (prot_cur_address & 0x0000ffff) | (data << 16); + enc_ready = false; +} + +void sega_315_5881_crypt_device::set_subkey(UINT16 data) +{ + subkey = data; + enc_ready = false; +} + +void sega_315_5881_crypt_device::set_key(UINT32 data) +{ + key = data; + enc_ready = false; +} + +/*************************************************************************** + DECRYPTION EMULATION + +By convention, we label the three known cart protection methods this way (using Deunan Knute's wording): +M1: DMA read of protected ROM area +M2: special read of ROM area which supplies decryption key first +M3: normal read followed by write to cart's decryption buffer (up to 64kB), followed by M2 but from buffer area + +Notes below refer to M2 & M3. + +The encryption is done by a stream cipher operating in counter mode, which use a 16-bits internal block cipher. + +There are 2 "control bits" at the start of the decrypted stream which control the mode of operation: bit #1 set to 1 means +that the decrypted stream needs to be decompressed after being decrypted. More on this later. + +The next 16-bits are part of the header (they don't belong to the plaintext), but his meaning is unclear. It has been +conjectured that it could stablish when to "reset" the process and start processing a new stream (based on some tests +on WWFROYAL, in which the decryption's output doesn't seem to be valid for more than some dozens of words), but some +more testing would be needed for clarifying that. + +After those 18 heading bits, we find the proper plaintext. It must be noted that, due to the initial 2 special bits, +the 16-bits words of the plaintext are shifted 2 bits respect to the word-boundaries of the output stream of the +internal block-cipher. So, at a given step, the internal block cipher will output 16-bits, 14 of which will go to a +given plaintext word, and the remaining 2 to the next plaintext word. + +The underlying block cipher consists of two 4-round Feistel Networks (FN): the first one takes the counter (16 bits), +the game-key (>=26 bits) and the sequence-key (16 bits) and output a middle result (16 bits) which will act as another key +for the second one. The second FN will take the encrypted word (16 bits), the game-key, the sequence-key and the result +from the first FN and will output the decrypted word (16 bits). + +Each round of the Feistel Networks use four substitution sboxes, each having 6 inputs and 2 outputs. The input can be the +XOR of at most two "sources bits", being source bits the bits from the previous round and the bits from the different keys. + +The underlying block cipher has the same structure than the one used by the CPS-2 (Capcom Play System 2) and, +indeed, some of the used sboxes are exactly the same and appear in the same FN/round in both systems (this is not evident, +as you need to apply a bitswapping and some XORs to the input & output of the sboxes to get the same values due). However, +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 +key bit. + +Due to the small key-length, no sophisticated attacks are needed to recover the keys; a brute-force attack knowing just +some (encrypted word-decrypted word) pairs suffice. However, due to the weak key scheduling, it should be noted that some +related keys can produce the same output bytes for some (short) input sequences. + +The only difference in the decryption process between M2 and M3 is the initialization of the counter. In M3, the counter is +always set to 0 at the beginning of the decryption while, in M2, the bits #1-#16 of the ciphertext's address are used +to initialize the counter. + +Note that this implementation considers that the counter initialization for ram decryption is 0 simply because the ram is +mapped to multiples of 128K. + +Due to the nature of the cipher, there are some degrees of freedom when choosing the s-boxes and keys values; by example, +you could apply a fixed bitswapping and XOR to the keys and the decryption would remain the same as long as you change +accordingly the s-boxes' definitions. So the order of the bits in the keys is arbitrary, and the s-boxes values have been +chosen so as to make the key for CAPSNK equal to 0. + +It can be observed that a couple of sboxes have incomplete tables (a 255 value indicate an unknown value). The recovered keys +as of december/2010 show small randomness and big correlations, making possible that some unseen bits could make the +decryption need those incomplete parts. + +****************************************************************************************/ + +const sega_315_5881_crypt_device::sbox sega_315_5881_crypt_device::fn1_sboxes[4][4] = { + { // 1st round + { + { + 0,3,2,2,1,3,1,2,3,2,1,2,1,2,3,1,3,2,2,0,2,1,3,0,0,3,2,3,2,1,2,0, + 2,3,1,1,2,2,1,1,1,0,2,3,3,0,2,1,1,1,1,1,3,0,3,2,1,0,1,2,0,3,1,3, + }, + {3,4,5,7,-1,-1}, + {0,4} + }, + + { + { + 2,2,2,0,3,3,0,1,2,2,3,2,3,0,2,2,1,1,0,3,3,2,0,2,0,1,0,1,2,3,1,1, + 0,1,3,3,1,3,3,1,2,3,2,0,0,0,2,2,0,3,1,3,0,3,2,2,0,3,0,3,1,1,0,2, + }, + {0,1,2,5,6,7}, + {1,6} + }, + + { + { + 0,1,3,0,3,1,1,1,1,2,3,1,3,0,2,3,3,2,0,2,1,1,2,1,1,3,1,0,0,2,0,1, + 1,3,1,0,0,3,2,3,2,0,3,3,0,0,0,0,1,2,3,3,2,0,3,2,1,0,0,0,2,2,3,3, + }, + {0,2,5,6,7,-1}, + {2,3} + }, + + { + { + 3,2,1,2,1,2,3,2,0,3,2,2,3,1,3,3,0,2,3,0,3,3,2,1,1,1,2,0,2,2,0,1, + 1,3,3,0,0,3,0,3,0,2,1,3,2,1,0,0,0,1,1,2,0,1,0,0,0,1,3,3,2,0,3,3, + }, + {1,2,3,4,6,7}, + {5,7} + }, + }, + { // 2nd round + { + { + 3,3,1,2,0,0,2,2,2,1,2,1,3,1,1,3,3,0,0,3,0,3,3,2,1,1,3,2,3,2,1,3, + 2,3,0,1,3,2,0,1,2,1,3,1,2,2,3,3,3,1,2,2,0,3,1,2,2,1,3,0,3,0,1,3, + }, + {0,1,3,4,5,7}, + {0,4} + }, + + { + { + 2,0,1,0,0,3,2,0,3,3,1,2,1,3,0,2,0,2,0,0,0,2,3,1,3,1,1,2,3,0,3,0, + 3,0,2,0,0,2,2,1,0,2,3,3,1,3,1,0,1,3,3,0,0,1,3,1,0,2,0,3,2,1,0,1, + }, + {0,1,3,4,6,-1}, + {1,5} + }, + + { + { + 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, + 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, + }, + {1,2,5,6,7,-1}, + {2,7} + }, + + { + { + 0,1,3,3,3,1,3,3,1,0,2,0,2,0,0,3,1,2,1,3,1,2,3,2,2,0,1,3,0,3,3,3, + 0,0,0,2,1,1,2,3,2,2,3,1,1,2,0,2,0,2,1,3,1,1,3,3,1,1,3,0,2,3,0,0, + }, + {2,3,4,5,6,7}, + {3,6} + }, + }, + { // 3rd round + { + { + 0,0,1,0,1,0,0,3,2,0,0,3,0,1,0,2,0,3,0,0,2,0,3,2,2,1,3,2,2,1,1,2, + 0,0,0,3,0,1,1,0,0,2,1,0,3,1,2,2,2,0,3,1,3,0,1,2,2,1,1,1,0,2,3,1, + }, + {1,2,3,4,5,7}, + {0,5} + }, + + { + { + 1,2,1,0,3,1,1,2,0,0,2,3,2,3,1,3,2,0,3,2,2,3,1,1,1,1,0,3,2,0,0,1, + 1,0,0,1,3,1,2,3,0,0,2,3,3,0,1,0,0,2,3,0,1,2,0,1,3,3,3,1,2,0,2,1, + }, + {0,2,4,5,6,7}, + {1,6} + }, + + { + { + 0,3,0,2,1,2,0,0,1,1,0,0,3,1,1,0,0,3,0,0,2,3,3,2,3,1,2,0,0,2,3,0, + // unused? + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + }, + {0,2,4,6,7,-1}, + {2,3} + }, + + { + { + 0,0,1,0,0,1,0,2,3,3,0,3,3,2,3,0,2,2,2,0,3,2,0,3,1,0,0,3,3,0,0,0, + 2,2,1,0,2,0,3,2,0,0,3,1,3,3,0,0,2,1,1,2,1,0,1,1,0,3,1,2,0,2,0,3, + }, + {0,1,2,3,6,-1}, + {4,7} + }, + }, + { // 4th round + { + { + 0,3,3,3,3,3,2,0,0,1,2,0,2,2,2,2,1,1,0,2,2,1,3,2,3,2,0,1,2,3,2,1, + 3,2,2,3,1,0,1,0,0,2,0,1,2,1,2,3,1,2,1,1,2,2,1,0,1,3,2,3,2,0,3,1, + }, + {0,1,3,4,5,6}, + {0,5} + }, + + { + { + 0,3,0,0,2,0,3,1,1,1,2,2,2,1,3,1,2,2,1,3,2,2,3,3,0,3,1,0,3,2,0,1, + 3,0,2,0,1,0,2,1,3,3,1,2,2,0,2,3,3,2,3,0,1,1,3,3,0,2,1,3,0,2,2,3, + }, + {0,1,2,3,5,7}, + {1,7} + }, + + { + { + 0,1,2,3,3,3,3,1,2,0,2,3,2,1,0,1,2,2,1,2,0,3,2,0,1,1,0,1,3,1,3,1, + 3,1,0,0,1,0,0,0,0,1,2,2,1,1,3,3,1,2,3,3,3,2,3,0,2,2,1,3,3,0,2,0, + }, + {2,3,4,5,6,7}, + {2,3} + }, + + { + { + 0,2,1,1,3,2,0,3,1,0,1,0,3,2,1,1,2,2,0,3,1,0,1,2,2,2,3,3,0,0,0,0, + 1,2,1,0,2,1,2,2,2,3,2,3,0,1,3,0,0,1,3,0,0,1,1,0,1,0,0,0,0,2,0,1, + }, + {0,1,2,4,6,7}, + {4,6} + }, + }, +}; + + +const sega_315_5881_crypt_device::sbox sega_315_5881_crypt_device::fn2_sboxes[4][4] = { + { // 1st round + { + { + 3,3,0,1,0,1,0,0,0,3,0,0,1,3,1,2,0,3,3,3,2,1,0,1,1,1,2,2,2,3,2,2, + 2,1,3,3,1,3,1,1,0,0,1,2,0,2,2,1,1,2,3,1,2,1,3,1,2,2,0,1,3,0,2,2, + }, + {1,3,4,5,6,7}, + {0,7} + }, + + { + { + 0,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, + 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, + }, + {0,3,5,6,-1,-1}, + {1,2} + }, + + { + { + 0,2,2,1,0,1,2,1,2,0,1,2,3,3,0,1,3,1,1,2,1,2,1,3,3,2,3,3,2,1,0,1, + 0,1,0,2,0,1,1,3,2,0,3,2,1,1,1,3,2,3,0,2,3,0,2,2,1,3,0,1,1,2,2,2, + }, + {0,2,3,4,7,-1}, + {3,4} + }, + + { + { + 2,3,1,3,2,0,1,2,0,0,3,3,3,3,3,1,2,0,2,1,2,3,0,2,0,1,0,3,0,2,1,0, + 2,3,0,1,3,0,3,2,3,1,2,0,3,1,1,2,0,3,0,0,2,0,2,1,2,2,3,2,1,2,3,1, + }, + {1,2,5,6,-1,-1}, + {5,6} + }, + }, + { // 2nd round + { + { + 2,3,1,3,1,0,3,3,3,2,3,3,2,0,0,3,2,3,0,3,1,1,2,3,1,1,2,2,0,1,0,0, + 2,1,0,1,2,0,1,2,0,3,1,1,2,3,1,2,0,2,0,1,3,0,1,0,2,2,3,0,3,2,3,0, + }, + {0,1,4,5,6,7}, + {0,7} + }, + + { + { + 0,2,2,0,2,2,0,3,2,3,2,1,3,2,3,3,1,1,0,0,3,0,2,1,1,3,3,2,3,2,0,1, + 1,2,3,0,1,0,3,0,3,1,0,2,1,2,0,3,2,3,1,2,2,0,3,2,3,0,0,1,2,3,3,3, + }, + {0,2,3,6,7,-1}, + {1,5} + }, + + { + { + 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, + 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, + }, + {2,3,4,6,7,-1}, + {2,3} + }, + + { + { + 2,3,1,3,1,1,2,3,3,1,1,0,1,0,2,3,2,1,0,0,2,2,0,1,0,2,2,2,0,2,1,0, + 3,1,2,3,1,3,0,2,1,0,1,0,0,1,2,2,3,2,3,1,3,2,1,1,2,0,2,1,3,3,1,0, + }, + {1,2,3,4,5,6}, + {4,6} + }, + }, + { // 3rd round + { + { + 0,3,0,1,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, + 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, + }, + {1,4,5,6,7,-1}, + {0,5} + }, + + { + { + 0,3,0,1,3,0,3,1,3,2,2,2,3,0,3,2,2,1,2,2,0,3,2,2,0,0,2,1,1,3,2,3, + 2,3,3,1,2,0,1,2,2,1,0,0,0,0,2,3,1,2,0,3,1,3,1,2,3,2,1,0,3,0,0,2, + }, + {0,2,3,4,6,7}, + {1,7} + }, + + { + { + 2,2,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, + // unused? + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + }, + {1,2,4,7,-1,-1}, + {2,4} + }, + + { + { + 0,2,3,1,3,1,1,0,0,1,3,0,2,1,3,3,2,0,2,1,1,2,3,3,0,0,0,2,0,2,3,0, + 3,3,3,3,2,3,3,2,3,0,1,0,2,3,3,2,0,1,3,1,0,1,2,3,3,0,2,0,3,0,3,3, + }, + {0,1,2,3,5,7}, + {3,6} + }, + }, + { // 4th round + { + { + 0,1,1,0,0,1,0,2,3,3,0,1,2,3,0,2,1,0,3,3,2,0,3,0,0,2,1,0,1,0,1,3, + 0,3,3,1,2,0,3,0,1,3,2,0,3,3,1,3,0,2,3,3,2,1,1,2,2,1,2,1,2,0,1,1, + }, + {0,1,2,4,7,-1}, + {0,5} + }, + + { + { + 2,0,0,2,3,0,2,3,3,1,1,1,2,1,1,0,0,2,1,0,0,3,1,0,0,3,3,0,1,0,1,2, + 0,2,0,2,0,1,2,3,2,1,1,0,3,3,3,3,3,3,1,0,3,0,0,2,0,3,2,0,2,2,0,1, + }, + {0,1,3,5,6,-1}, + {1,3} + }, + + { + { + 0,1,1,2,1,3,1,1,0,0,3,1,1,1,2,0,3,2,0,1,1,2,3,3,3,0,3,0,0,2,0,3, + 3,2,0,0,3,2,3,1,2,3,0,3,2,0,1,2,2,2,0,2,0,1,2,2,3,1,2,2,1,1,1,1, + }, + {0,2,3,4,5,7}, + {2,7} + }, + + { + { + 0,1,2,0,3,3,0,3,2,1,3,3,0,3,1,1,3,2,3,2,3,0,0,0,3,0,2,2,3,2,2,3, + 2,2,3,1,2,3,1,2,0,3,0,2,3,1,0,0,3,2,1,2,1,2,1,3,1,0,2,3,3,1,3,2, + }, + {2,3,4,5,6,7}, + {4,6} + }, + }, +}; + +const int sega_315_5881_crypt_device::fn1_game_key_scheduling[38][2] = { + {1,29}, {1,71}, {2,4}, {2,54}, {3,8}, {4,56}, {4,73}, {5,11}, + {6,51}, {7,92}, {8,89}, {9,9}, {9,10}, {9,39}, {9,41}, {9,58}, + {9,59}, {9,86}, {10,90}, {11,6}, {12,64}, {13,49}, {14,44}, {15,40}, + {16,69}, {17,15}, {18,23}, {18,43}, {19,82}, {20,81}, {21,32}, {21,61}, + {22,5}, {23,66}, {24,13}, {24,45}, {25,12}, {25,35} +}; + +const int sega_315_5881_crypt_device::fn2_game_key_scheduling[34][2] = { + {0,0}, {1,3}, {2,11}, {3,20}, {4,22}, {5,23}, {6,29}, {7,38}, + {8,39}, {9,47}, {9,55}, {9,86}, {9,87}, {9,90}, {10,50}, {10,53}, + {11,57}, {12,59}, {13,61}, {13,64}, {14,63}, {15,67}, {16,72}, {17,83}, + {18,88}, {19,94}, {20,35}, {21,17}, {21,92}, {22,6}, {22,11}, {23,85}, + {24,16}, {25,25} +}; + +const int sega_315_5881_crypt_device::fn1_sequence_key_scheduling[20][2] = { + {0,52}, {1,34}, {2,17}, {3,36}, {4,84}, {4,88}, {5,57}, {6,48}, + {6,68}, {7,76}, {8,83}, {9,30}, {10,22}, {10,41}, {11,38}, {12,55}, + {13,74}, {14,19}, {14,80}, {15,26} +}; + +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}; + +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}; + +int sega_315_5881_crypt_device::feistel_function(int input, const struct sbox *sboxes, UINT32 subkeys) +{ + int k,m; + int aux; + int result=0; + + for (m=0; m<4; ++m) { // 4 sboxes + for (k=0, aux=0; k<6; ++k) + if (sboxes[m].inputs[k]!=-1) + aux |= BIT(input, sboxes[m].inputs[k]) << k; + + aux = sboxes[m].table[(aux^subkeys)&0x3f]; + + for (k=0; k<2; ++k) + result |= BIT(aux,k) << sboxes[m].outputs[k]; + + subkeys >>=6; + } + + return result; +} + +/************************** +This implementation is an "educational" version. It must be noted that it can be speed-optimized in a number of ways. +The most evident one is to factor out the parts of the key-scheduling that must only be done once (like the game-key & +sequence-key parts) as noted in the comments inlined in the function. More sophisticated speed-ups can be gained by +noticing that the weak key-scheduling would allow to create some pregenerated look-up tables for doing most of the work +of the function. Even so, it would still be pretty slow, so caching techniques could be a wiser option here. +**************************/ + +UINT16 sega_315_5881_crypt_device::block_decrypt(UINT32 game_key, UINT16 sequence_key, UINT16 counter, UINT16 data) +{ + int j; + int aux,aux2; + int A,B; + int middle_result; + UINT32 fn1_subkeys[4]; + UINT32 fn2_subkeys[4]; + + /* Game-key scheduling; this could be done just once per game at initialization time */ + memset(fn1_subkeys,0,sizeof(UINT32)*4); + memset(fn2_subkeys,0,sizeof(UINT32)*4); + + for (j=0; j<38; ++j) { + if (BIT(game_key, fn1_game_key_scheduling[j][0])!=0) { + aux = fn1_game_key_scheduling[j][1]%24; + aux2 = fn1_game_key_scheduling[j][1]/24; + fn1_subkeys[aux2] ^= (1<<aux); + } + } + + for (j=0; j<34; ++j) { + if (BIT(game_key, fn2_game_key_scheduling[j][0])!=0) { + aux = fn2_game_key_scheduling[j][1]%24; + aux2 = fn2_game_key_scheduling[j][1]/24; + fn2_subkeys[aux2] ^= (1<<aux); + } + } + /********************************************************/ + + /* Sequence-key scheduling; this could be done just once per decryption run */ + for (j=0; j<20; ++j) { + if (BIT(sequence_key,fn1_sequence_key_scheduling[j][0])!=0) { + aux = fn1_sequence_key_scheduling[j][1]%24; + aux2 = fn1_sequence_key_scheduling[j][1]/24; + fn1_subkeys[aux2] ^= (1<<aux); + } + } + + for (j=0; j<16; ++j) { + if (BIT(sequence_key,j)!=0) { + aux = fn2_sequence_key_scheduling[j]%24; + aux2 = fn2_sequence_key_scheduling[j]/24; + fn2_subkeys[aux2] ^= (1<<aux); + } + } + + // subkeys bits 10 & 41 + fn2_subkeys[0] ^= (BIT(sequence_key,2)<<10); + fn2_subkeys[1] ^= (BIT(sequence_key,4)<<17); + /**************************************************************/ + + // First Feistel Network + + aux = BITSWAP16(counter,5,12,14,13,9,3,6,4, 8,1,15,11,0,7,10,2); + + // 1st round + B = aux >> 8; + A = (aux & 0xff) ^ feistel_function(B,fn1_sboxes[0],fn1_subkeys[0]); + + // 2nd round + B = B ^ feistel_function(A,fn1_sboxes[1],fn1_subkeys[1]); + + // 3rd round + A = A ^ feistel_function(B,fn1_sboxes[2],fn1_subkeys[2]); + + // 4th round + B = B ^ feistel_function(A,fn1_sboxes[3],fn1_subkeys[3]); + + middle_result = (B<<8)|A; + + + /* Middle-result-key sheduling */ + for (j=0; j<16; ++j) { + if (BIT(middle_result,j)!=0) { + aux = fn2_middle_result_scheduling[j]%24; + aux2 = fn2_middle_result_scheduling[j]/24; + fn2_subkeys[aux2] ^= (1<<aux); + } + } + /*********************/ + + // Second Feistel Network + + aux = BITSWAP16(data,14,3,8,12,13,7,15,4, 6,2,9,5,11,0,1,10); + + // 1st round + B = aux >> 8; + A = (aux & 0xff) ^ feistel_function(B,fn2_sboxes[0],fn2_subkeys[0]); + + // 2nd round + B = B ^ feistel_function(A,fn2_sboxes[1],fn2_subkeys[1]); + + // 3rd round + A = A ^ feistel_function(B,fn2_sboxes[2],fn2_subkeys[2]); + + // 4th round + B = B ^ feistel_function(A,fn2_sboxes[3],fn2_subkeys[3]); + + aux = (B<<8)|A; + + aux = BITSWAP16(aux,15,7,6,14,13,12,5,4, 3,2,11,10,9,1,0,8); + + return aux; +} + +UINT16 sega_315_5881_crypt_device::get_decrypted_16() +{ + UINT16 enc; + + enc = m_read(prot_cur_address); + + UINT16 dec = block_decrypt(key, subkey, prot_cur_address, enc); + UINT16 res = (dec & 3) | (dec_hist & 0xfffc); + dec_hist = dec; + + prot_cur_address ++; + return res; +} + +void sega_315_5881_crypt_device::enc_start() +{ + buffer_pos = BUFFER_SIZE; + dec_header = get_decrypted_16() << 16; + dec_header |= get_decrypted_16(); + + if(dec_header & FLAG_COMPRESSED) { + line_buffer_size = dec_header & FLAG_LINE_SIZE_512 ? 512 : 256; + line_buffer_pos = line_buffer_size; + buffer_bit = 7; + } + enc_ready = true; +} + +void sega_315_5881_crypt_device::enc_fill() +{ + assert(buffer_pos == BUFFER_SIZE); + for(int i = 0; i != BUFFER_SIZE; i+=2) { + UINT16 val = get_decrypted_16(); + buffer[i] = val; + buffer[i+1] = val >> 8; + } + buffer_pos = 0; +} + +/* node format +0xxxxxxx - next node index +1a0bbccc - end node + a - 0 = repeat + 1 = fetch + b - if a = 1 + 00 - fetch 0 + 01 - fetch 1 + 11 - fetch -1 + if a = 0 + 000 + c - repeat/fetch counter + count = ccc + 1 +11111111 - empty node +*/ +const UINT8 sega_315_5881_crypt_device::trees[9][2][32] = { + { + {0x01,0x10,0x0f,0x05,0xc4,0x13,0x87,0x0a,0xcc,0x81,0xce,0x0c,0x86,0x0e,0x84,0xc2, + 0x11,0xc1,0xc3,0xcf,0x15,0xc8,0xcd,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + {0xc7,0x02,0x03,0x04,0x80,0x06,0x07,0x08,0x09,0xc9,0x0b,0x0d,0x82,0x83,0x85,0xc0, + 0x12,0xc6,0xc5,0x14,0x16,0xca,0xcb,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + }, + { + {0x02,0x80,0x05,0x04,0x81,0x10,0x15,0x82,0x09,0x83,0x0b,0x0c,0x0d,0xdc,0x0f,0xde, + 0x1c,0xcf,0xc5,0xdd,0x86,0x16,0x87,0x18,0x19,0x1a,0xda,0xca,0xc9,0x1e,0xce,0xff,}, + {0x01,0x17,0x03,0x0a,0x08,0x06,0x07,0xc2,0xd9,0xc4,0xd8,0xc8,0x0e,0x84,0xcb,0x85, + 0x11,0x12,0x13,0x14,0xcd,0x1b,0xdb,0xc7,0xc0,0xc1,0x1d,0xdf,0xc3,0xc6,0xcc,0xff,}, + }, + { + {0xc6,0x80,0x03,0x0b,0x05,0x07,0x82,0x08,0x15,0xdc,0xdd,0x0c,0xd9,0xc2,0x14,0x10, + 0x85,0x86,0x18,0x16,0xc5,0xc4,0xc8,0xc9,0xc0,0xcc,0xff,0xff,0xff,0xff,0xff,0xff,}, + {0x01,0x02,0x12,0x04,0x81,0x06,0x83,0xc3,0x09,0x0a,0x84,0x11,0x0d,0x0e,0x0f,0x19, + 0xca,0xc1,0x13,0xd8,0xda,0xdb,0x17,0xde,0xcd,0xcb,0xff,0xff,0xff,0xff,0xff,0xff,}, + }, + { + {0x01,0x80,0x0d,0x04,0x05,0x15,0x83,0x08,0xd9,0x10,0x0b,0x0c,0x84,0x0e,0xc0,0x14, + 0x12,0xcb,0x13,0xca,0xc8,0xc2,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + {0xc5,0x02,0x03,0x07,0x81,0x06,0x82,0xcc,0x09,0x0a,0xc9,0x11,0xc4,0x0f,0x85,0xd8, + 0xda,0xdb,0xc3,0xdc,0xdd,0xc1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + }, + { + {0x01,0x80,0x06,0x0c,0x05,0x81,0xd8,0x84,0x09,0xdc,0x0b,0x0f,0x0d,0x0e,0x10,0xdb, + 0x11,0xca,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + {0xc4,0x02,0x03,0x04,0xcb,0x0a,0x07,0x08,0xd9,0x82,0xc8,0x83,0xc0,0xc1,0xda,0xc2, + 0xc9,0xc3,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + }, + { + {0x01,0x02,0x06,0x0a,0x83,0x0b,0x07,0x08,0x09,0x82,0xd8,0x0c,0xd9,0xda,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + {0xc3,0x80,0x03,0x04,0x05,0x81,0xca,0xc8,0xdb,0xc9,0xc0,0xc1,0x0d,0xc2,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + }, + { + {0x01,0x02,0x03,0x04,0x81,0x07,0x08,0xd8,0xda,0xd9,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + {0xc2,0x80,0x05,0xc9,0xc8,0x06,0x82,0xc0,0x09,0xc1,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + }, + { + {0x01,0x80,0x04,0xc8,0xc0,0xd9,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + {0xc1,0x02,0x03,0x81,0x05,0xd8,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + }, + { + {0x01,0xd8,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + {0xc0,0x80,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, + }, +}; + +int sega_315_5881_crypt_device::get_compressed_bit() +{ + if(buffer_pos == BUFFER_SIZE) + enc_fill(); + int res = (buffer[buffer_pos^1] >> buffer_bit) & 1; + buffer_bit--; + if(buffer_bit == -1) { + buffer_bit = 7; + buffer_pos++; + } + return res; +} + +void sega_315_5881_crypt_device::line_fill() +{ + assert(line_buffer_pos == line_buffer_size); + UINT8 *lp = line_buffer; + UINT8 *lc = line_buffer_prev; + line_buffer = lc; + line_buffer_prev = lp; + line_buffer_pos = 0; + + UINT32 line_buffer_mask = line_buffer_size-1; + + for(int i=0; i != line_buffer_size;) { + // vlc 0: start of line + // vlc 1: interior of line + // vlc 2-9: 7-1 bytes from end of line + + int slot = i ? i < line_buffer_size - 7 ? 1 : (i & 7) + 1 : 0; + + UINT32 tmp = 0; + while (!(tmp&0x80)) + if(get_compressed_bit()) + tmp = trees[slot][1][tmp]; + else + tmp = trees[slot][0][tmp]; + if(tmp != 0xff) { + int count = (tmp & 7) + 1; + + if(tmp&0x40) { + // Copy from previous line + + static int offsets[4] = {0, 1, 0, -1}; + int offset = offsets[(tmp & 0x18) >> 3]; + for(int j=0; j != count; j++) { + lc[i^1] = lp[((i+offset) & line_buffer_mask)^1]; + i++; + } + + } else { + // Get a byte in the stream and write n times + UINT8 byte; + byte = get_compressed_bit() << 1; + byte = (byte | get_compressed_bit()) << 1; + byte = (byte | get_compressed_bit()) << 1; + byte = (byte | get_compressed_bit()) << 1; + byte = (byte | get_compressed_bit()) << 1; + byte = (byte | get_compressed_bit()) << 1; + byte = (byte | get_compressed_bit()) << 1; + byte = byte | get_compressed_bit(); + for(int j=0; j != count; j++) + lc[(i++)^1] = byte; + + } + } + } +}
\ No newline at end of file diff --git a/src/mame/machine/315-5881_crypt.h b/src/mame/machine/315-5881_crypt.h new file mode 100644 index 00000000000..fcea2a21a2d --- /dev/null +++ b/src/mame/machine/315-5881_crypt.h @@ -0,0 +1,87 @@ + +#pragma once + +#ifndef __SEGA315_5881_CRYPT__ +#define __SEGA315_5881_CRYPT__ + +typedef device_delegate<UINT16 (UINT32)> sega_m2_read_delegate; + +extern const device_type SEGA315_5881_CRYPT; + +#define MCFG_SET_READ_CALLBACK( _class, _method) \ + sega_315_5881_crypt_device::set_read_cb(*device, sega_m2_read_delegate(&_class::_method, #_class "::" #_method, NULL, (_class *)0)); + + +class sega_315_5881_crypt_device : public device_t +{ +public: + // construction/destruction + sega_315_5881_crypt_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + + void do_decrypt(UINT8 *&base); + void set_addr_low(UINT16 data); + void set_addr_high(UINT16 data); + void set_subkey(UINT16 data); + void set_key(UINT32 data); + + sega_m2_read_delegate m_read; + + static void set_read_cb(device_t &device,sega_m2_read_delegate readcb) + { + sega_315_5881_crypt_device &dev = downcast<sega_315_5881_crypt_device &>(device); + dev.m_read = readcb; + } + +protected: + virtual void device_start(); + virtual void device_reset(); + +private: + + enum { + BUFFER_SIZE = 32768, LINE_SIZE = 512, + FLAG_COMPRESSED = 0x10000, FLAG_LINE_SIZE_512 = 0x20000 + }; + + UINT32 key; + + UINT8 *buffer, *line_buffer, *line_buffer_prev; + UINT32 prot_cur_address; + UINT16 subkey, dec_hist; + UINT32 dec_header; + + bool enc_ready; + + int buffer_pos, line_buffer_pos, line_buffer_size, buffer_bit; + + struct sbox { + UINT8 table[64]; + int inputs[6]; // positions of the inputs bits, -1 means no input except from key + int outputs[2]; // positions of the output bits + }; + + static const sbox fn1_sboxes[4][4]; + static const sbox fn2_sboxes[4][4]; + + static const int fn1_game_key_scheduling[38][2]; + static const int fn2_game_key_scheduling[34][2]; + static const int fn1_sequence_key_scheduling[20][2]; + static const int fn2_sequence_key_scheduling[16]; + static const int fn2_middle_result_scheduling[16]; + + static const UINT8 trees[9][2][32]; + + int feistel_function(int input, const struct sbox *sboxes, UINT32 subkeys); + UINT16 block_decrypt(UINT32 game_key, UINT16 sequence_key, UINT16 counter, UINT16 data); + + UINT16 get_decrypted_16(); + int get_compressed_bit(); + + void enc_start(); + void enc_fill(); + void line_fill(); + +}; + +#endif diff --git a/src/mame/machine/naomim2.c b/src/mame/machine/naomim2.c index d21c3746a92..ff07cbdfdbf 100644 --- a/src/mame/machine/naomim2.c +++ b/src/mame/machine/naomim2.c @@ -105,7 +105,8 @@ note: if ROM is not mounted its area readed as 0xFF const device_type NAOMI_M2_BOARD = &device_creator<naomi_m2_board>; naomi_m2_board::naomi_m2_board(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) - : naomi_board(mconfig, NAOMI_M2_BOARD, "Sega NAOMI M2 BOARD", tag, owner, clock, "naomi_m2_board", __FILE__) + : naomi_board(mconfig, NAOMI_M2_BOARD, "Sega NAOMI M2 BOARD", tag, owner, clock, "naomi_m2_board", __FILE__), + m_cryptdevice(*this, "segam2crypt") { key_tag = 0; } @@ -120,30 +121,10 @@ void naomi_m2_board::device_start() { naomi_board::device_start(); -#if USE_NAOMICRYPT - key = get_naomi_key(machine()); -#else - const UINT8 *key_data = memregion(key_tag)->base(); - key = (key_data[0] << 24) | (key_data[1] << 16) | (key_data[2] << 8) | key_data[3]; -#endif ram = auto_alloc_array(machine(), UINT8, RAM_SIZE); - buffer = auto_alloc_array(machine(), UINT8, BUFFER_SIZE); - line_buffer = auto_alloc_array(machine(), UINT8, LINE_SIZE); - line_buffer_prev = auto_alloc_array(machine(), UINT8, LINE_SIZE); - save_pointer(NAME(ram), RAM_SIZE); - save_pointer(NAME(buffer), BUFFER_SIZE); - save_pointer(NAME(line_buffer), LINE_SIZE); - save_pointer(NAME(line_buffer_prev), LINE_SIZE); save_item(NAME(rom_cur_address)); - save_item(NAME(prot_cur_address)); - save_item(NAME(subkey)); - save_item(NAME(enc_ready)); - save_item(NAME(dec_hist)); - save_item(NAME(dec_header)); - save_item(NAME(buffer_pos)); - save_item(NAME(line_buffer_pos)); - save_item(NAME(line_buffer_size)); + save_pointer(NAME(ram), RAM_SIZE); } void naomi_m2_board::device_reset() @@ -151,21 +132,15 @@ void naomi_m2_board::device_reset() naomi_board::device_reset(); memset(ram, 0, RAM_SIZE); - memset(buffer, 0, BUFFER_SIZE); - memset(line_buffer, 0, LINE_SIZE); - memset(line_buffer_prev, 0, LINE_SIZE); rom_cur_address = 0; - prot_cur_address = 0; - subkey = 0; - dec_hist = 0; - dec_header = 0; - enc_ready = false; - - buffer_pos = 0; - line_buffer_pos = 0; - line_buffer_size = 0; - buffer_bit = 0; + +#if USE_NAOMICRYPT + m_cryptdevice->set_key(get_naomi_key(machine())); +#else + const UINT8 *key_data = memregion(key_tag)->base(); + m_cryptdevice->set_key((key_data[0] << 24) | (key_data[1] << 16) | (key_data[2] << 8) | key_data[3]); +#endif } void naomi_m2_board::board_setup_address(UINT32 address, bool is_dma) @@ -175,21 +150,10 @@ void naomi_m2_board::board_setup_address(UINT32 address, bool is_dma) void naomi_m2_board::board_get_buffer(UINT8 *&base, UINT32 &limit) { + if(rom_cur_address & 0x40000000) { if(rom_cur_address == 0x4001fffe) { - if(!enc_ready) - enc_start(); - if(dec_header & FLAG_COMPRESSED) { - if(line_buffer_pos == line_buffer_size) - line_fill(); - base = line_buffer + line_buffer_pos; - line_buffer_pos += 2; - } else { - if(buffer_pos == BUFFER_SIZE) - enc_fill(); - base = buffer + buffer_pos; - buffer_pos += 2; - } + m_cryptdevice->do_decrypt(base); limit = 2; } else @@ -214,6 +178,8 @@ void naomi_m2_board::board_advance(UINT32 size) void naomi_m2_board::board_write(offs_t offset, UINT16 data) { + + if(offset & 0x40000000) { if((offset & 0x0f000000) == 0x02000000) { offset &= RAM_SIZE-1; @@ -222,724 +188,35 @@ void naomi_m2_board::board_write(offs_t offset, UINT16 data) return; } switch(offset & 0x1fffffff) { - case 0x1fff8: prot_cur_address = (prot_cur_address & 0xffff0000) | data; enc_ready = false; return; - case 0x1fffa: prot_cur_address = (prot_cur_address & 0x0000ffff) | (data << 16); enc_ready = false; return; - case 0x1fffc: subkey = data; enc_ready = false; return; - } - } - throw emu_fatalerror("NAOMIM2: unhandled board write %08x, %04x\n", offset, data); -} - -/*************************************************************************** - DECRYPTION EMULATION - -By convention, we label the three known cart protection methods this way (using Deunan Knute's wording): -M1: DMA read of protected ROM area -M2: special read of ROM area which supplies decryption key first -M3: normal read followed by write to cart's decryption buffer (up to 64kB), followed by M2 but from buffer area - -Notes below refer to M2 & M3. - -The encryption is done by a stream cipher operating in counter mode, which use a 16-bits internal block cipher. - -There are 2 "control bits" at the start of the decrypted stream which control the mode of operation: bit #1 set to 1 means -that the decrypted stream needs to be decompressed after being decrypted. More on this later. - -The next 16-bits are part of the header (they don't belong to the plaintext), but his meaning is unclear. It has been -conjectured that it could stablish when to "reset" the process and start processing a new stream (based on some tests -on WWFROYAL, in which the decryption's output doesn't seem to be valid for more than some dozens of words), but some -more testing would be needed for clarifying that. - -After those 18 heading bits, we find the proper plaintext. It must be noted that, due to the initial 2 special bits, -the 16-bits words of the plaintext are shifted 2 bits respect to the word-boundaries of the output stream of the -internal block-cipher. So, at a given step, the internal block cipher will output 16-bits, 14 of which will go to a -given plaintext word, and the remaining 2 to the next plaintext word. - -The underlying block cipher consists of two 4-round Feistel Networks (FN): the first one takes the counter (16 bits), -the game-key (>=26 bits) and the sequence-key (16 bits) and output a middle result (16 bits) which will act as another key -for the second one. The second FN will take the encrypted word (16 bits), the game-key, the sequence-key and the result -from the first FN and will output the decrypted word (16 bits). - -Each round of the Feistel Networks use four substitution sboxes, each having 6 inputs and 2 outputs. The input can be the -XOR of at most two "sources bits", being source bits the bits from the previous round and the bits from the different keys. - -The underlying block cipher has the same structure than the one used by the CPS-2 (Capcom Play System 2) and, -indeed, some of the used sboxes are exactly the same and appear in the same FN/round in both systems (this is not evident, -as you need to apply a bitswapping and some XORs to the input & output of the sboxes to get the same values due). However, -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 -key bit. - -Due to the small key-length, no sophisticated attacks are needed to recover the keys; a brute-force attack knowing just -some (encrypted word-decrypted word) pairs suffice. However, due to the weak key scheduling, it should be noted that some -related keys can produce the same output bytes for some (short) input sequences. - -The only difference in the decryption process between M2 and M3 is the initialization of the counter. In M3, the counter is -always set to 0 at the beginning of the decryption while, in M2, the bits #1-#16 of the ciphertext's address are used -to initialize the counter. - -Note that this implementation considers that the counter initialization for ram decryption is 0 simply because the ram is -mapped to multiples of 128K. - -Due to the nature of the cipher, there are some degrees of freedom when choosing the s-boxes and keys values; by example, -you could apply a fixed bitswapping and XOR to the keys and the decryption would remain the same as long as you change -accordingly the s-boxes' definitions. So the order of the bits in the keys is arbitrary, and the s-boxes values have been -chosen so as to make the key for CAPSNK equal to 0. - -It can be observed that a couple of sboxes have incomplete tables (a 255 value indicate an unknown value). The recovered keys -as of december/2010 show small randomness and big correlations, making possible that some unseen bits could make the -decryption need those incomplete parts. - -****************************************************************************************/ - -const naomi_m2_board::sbox naomi_m2_board::fn1_sboxes[4][4] = { - { // 1st round - { - { - 0,3,2,2,1,3,1,2,3,2,1,2,1,2,3,1,3,2,2,0,2,1,3,0,0,3,2,3,2,1,2,0, - 2,3,1,1,2,2,1,1,1,0,2,3,3,0,2,1,1,1,1,1,3,0,3,2,1,0,1,2,0,3,1,3, - }, - {3,4,5,7,-1,-1}, - {0,4} - }, - - { - { - 2,2,2,0,3,3,0,1,2,2,3,2,3,0,2,2,1,1,0,3,3,2,0,2,0,1,0,1,2,3,1,1, - 0,1,3,3,1,3,3,1,2,3,2,0,0,0,2,2,0,3,1,3,0,3,2,2,0,3,0,3,1,1,0,2, - }, - {0,1,2,5,6,7}, - {1,6} - }, - - { - { - 0,1,3,0,3,1,1,1,1,2,3,1,3,0,2,3,3,2,0,2,1,1,2,1,1,3,1,0,0,2,0,1, - 1,3,1,0,0,3,2,3,2,0,3,3,0,0,0,0,1,2,3,3,2,0,3,2,1,0,0,0,2,2,3,3, - }, - {0,2,5,6,7,-1}, - {2,3} - }, - - { - { - 3,2,1,2,1,2,3,2,0,3,2,2,3,1,3,3,0,2,3,0,3,3,2,1,1,1,2,0,2,2,0,1, - 1,3,3,0,0,3,0,3,0,2,1,3,2,1,0,0,0,1,1,2,0,1,0,0,0,1,3,3,2,0,3,3, - }, - {1,2,3,4,6,7}, - {5,7} - }, - }, - { // 2nd round - { - { - 3,3,1,2,0,0,2,2,2,1,2,1,3,1,1,3,3,0,0,3,0,3,3,2,1,1,3,2,3,2,1,3, - 2,3,0,1,3,2,0,1,2,1,3,1,2,2,3,3,3,1,2,2,0,3,1,2,2,1,3,0,3,0,1,3, - }, - {0,1,3,4,5,7}, - {0,4} - }, - - { - { - 2,0,1,0,0,3,2,0,3,3,1,2,1,3,0,2,0,2,0,0,0,2,3,1,3,1,1,2,3,0,3,0, - 3,0,2,0,0,2,2,1,0,2,3,3,1,3,1,0,1,3,3,0,0,1,3,1,0,2,0,3,2,1,0,1, - }, - {0,1,3,4,6,-1}, - {1,5} - }, - - { - { - 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, - 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, - }, - {1,2,5,6,7,-1}, - {2,7} - }, - - { - { - 0,1,3,3,3,1,3,3,1,0,2,0,2,0,0,3,1,2,1,3,1,2,3,2,2,0,1,3,0,3,3,3, - 0,0,0,2,1,1,2,3,2,2,3,1,1,2,0,2,0,2,1,3,1,1,3,3,1,1,3,0,2,3,0,0, - }, - {2,3,4,5,6,7}, - {3,6} - }, - }, - { // 3rd round - { - { - 0,0,1,0,1,0,0,3,2,0,0,3,0,1,0,2,0,3,0,0,2,0,3,2,2,1,3,2,2,1,1,2, - 0,0,0,3,0,1,1,0,0,2,1,0,3,1,2,2,2,0,3,1,3,0,1,2,2,1,1,1,0,2,3,1, - }, - {1,2,3,4,5,7}, - {0,5} - }, - - { - { - 1,2,1,0,3,1,1,2,0,0,2,3,2,3,1,3,2,0,3,2,2,3,1,1,1,1,0,3,2,0,0,1, - 1,0,0,1,3,1,2,3,0,0,2,3,3,0,1,0,0,2,3,0,1,2,0,1,3,3,3,1,2,0,2,1, - }, - {0,2,4,5,6,7}, - {1,6} - }, - - { - { - 0,3,0,2,1,2,0,0,1,1,0,0,3,1,1,0,0,3,0,0,2,3,3,2,3,1,2,0,0,2,3,0, - // unused? - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - }, - {0,2,4,6,7,-1}, - {2,3} - }, - - { - { - 0,0,1,0,0,1,0,2,3,3,0,3,3,2,3,0,2,2,2,0,3,2,0,3,1,0,0,3,3,0,0,0, - 2,2,1,0,2,0,3,2,0,0,3,1,3,3,0,0,2,1,1,2,1,0,1,1,0,3,1,2,0,2,0,3, - }, - {0,1,2,3,6,-1}, - {4,7} - }, - }, - { // 4th round - { - { - 0,3,3,3,3,3,2,0,0,1,2,0,2,2,2,2,1,1,0,2,2,1,3,2,3,2,0,1,2,3,2,1, - 3,2,2,3,1,0,1,0,0,2,0,1,2,1,2,3,1,2,1,1,2,2,1,0,1,3,2,3,2,0,3,1, - }, - {0,1,3,4,5,6}, - {0,5} - }, - - { - { - 0,3,0,0,2,0,3,1,1,1,2,2,2,1,3,1,2,2,1,3,2,2,3,3,0,3,1,0,3,2,0,1, - 3,0,2,0,1,0,2,1,3,3,1,2,2,0,2,3,3,2,3,0,1,1,3,3,0,2,1,3,0,2,2,3, - }, - {0,1,2,3,5,7}, - {1,7} - }, - - { - { - 0,1,2,3,3,3,3,1,2,0,2,3,2,1,0,1,2,2,1,2,0,3,2,0,1,1,0,1,3,1,3,1, - 3,1,0,0,1,0,0,0,0,1,2,2,1,1,3,3,1,2,3,3,3,2,3,0,2,2,1,3,3,0,2,0, - }, - {2,3,4,5,6,7}, - {2,3} - }, - - { - { - 0,2,1,1,3,2,0,3,1,0,1,0,3,2,1,1,2,2,0,3,1,0,1,2,2,2,3,3,0,0,0,0, - 1,2,1,0,2,1,2,2,2,3,2,3,0,1,3,0,0,1,3,0,0,1,1,0,1,0,0,0,0,2,0,1, - }, - {0,1,2,4,6,7}, - {4,6} - }, - }, -}; - - -const naomi_m2_board::sbox naomi_m2_board::fn2_sboxes[4][4] = { - { // 1st round - { - { - 3,3,0,1,0,1,0,0,0,3,0,0,1,3,1,2,0,3,3,3,2,1,0,1,1,1,2,2,2,3,2,2, - 2,1,3,3,1,3,1,1,0,0,1,2,0,2,2,1,1,2,3,1,2,1,3,1,2,2,0,1,3,0,2,2, - }, - {1,3,4,5,6,7}, - {0,7} - }, - - { - { - 0,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, - 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, - }, - {0,3,5,6,-1,-1}, - {1,2} - }, - - { - { - 0,2,2,1,0,1,2,1,2,0,1,2,3,3,0,1,3,1,1,2,1,2,1,3,3,2,3,3,2,1,0,1, - 0,1,0,2,0,1,1,3,2,0,3,2,1,1,1,3,2,3,0,2,3,0,2,2,1,3,0,1,1,2,2,2, - }, - {0,2,3,4,7,-1}, - {3,4} - }, - - { - { - 2,3,1,3,2,0,1,2,0,0,3,3,3,3,3,1,2,0,2,1,2,3,0,2,0,1,0,3,0,2,1,0, - 2,3,0,1,3,0,3,2,3,1,2,0,3,1,1,2,0,3,0,0,2,0,2,1,2,2,3,2,1,2,3,1, - }, - {1,2,5,6,-1,-1}, - {5,6} - }, - }, - { // 2nd round - { - { - 2,3,1,3,1,0,3,3,3,2,3,3,2,0,0,3,2,3,0,3,1,1,2,3,1,1,2,2,0,1,0,0, - 2,1,0,1,2,0,1,2,0,3,1,1,2,3,1,2,0,2,0,1,3,0,1,0,2,2,3,0,3,2,3,0, - }, - {0,1,4,5,6,7}, - {0,7} - }, - - { - { - 0,2,2,0,2,2,0,3,2,3,2,1,3,2,3,3,1,1,0,0,3,0,2,1,1,3,3,2,3,2,0,1, - 1,2,3,0,1,0,3,0,3,1,0,2,1,2,0,3,2,3,1,2,2,0,3,2,3,0,0,1,2,3,3,3, - }, - {0,2,3,6,7,-1}, - {1,5} - }, - - { - { - 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, - 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, - }, - {2,3,4,6,7,-1}, - {2,3} - }, - - { - { - 2,3,1,3,1,1,2,3,3,1,1,0,1,0,2,3,2,1,0,0,2,2,0,1,0,2,2,2,0,2,1,0, - 3,1,2,3,1,3,0,2,1,0,1,0,0,1,2,2,3,2,3,1,3,2,1,1,2,0,2,1,3,3,1,0, - }, - {1,2,3,4,5,6}, - {4,6} - }, - }, - { // 3rd round - { - { - 0,3,0,1,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, - 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, - }, - {1,4,5,6,7,-1}, - {0,5} - }, - - { - { - 0,3,0,1,3,0,3,1,3,2,2,2,3,0,3,2,2,1,2,2,0,3,2,2,0,0,2,1,1,3,2,3, - 2,3,3,1,2,0,1,2,2,1,0,0,0,0,2,3,1,2,0,3,1,3,1,2,3,2,1,0,3,0,0,2, - }, - {0,2,3,4,6,7}, - {1,7} - }, - - { - { - 2,2,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, - // unused? - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, - }, - {1,2,4,7,-1,-1}, - {2,4} - }, - - { - { - 0,2,3,1,3,1,1,0,0,1,3,0,2,1,3,3,2,0,2,1,1,2,3,3,0,0,0,2,0,2,3,0, - 3,3,3,3,2,3,3,2,3,0,1,0,2,3,3,2,0,1,3,1,0,1,2,3,3,0,2,0,3,0,3,3, - }, - {0,1,2,3,5,7}, - {3,6} - }, - }, - { // 4th round - { - { - 0,1,1,0,0,1,0,2,3,3,0,1,2,3,0,2,1,0,3,3,2,0,3,0,0,2,1,0,1,0,1,3, - 0,3,3,1,2,0,3,0,1,3,2,0,3,3,1,3,0,2,3,3,2,1,1,2,2,1,2,1,2,0,1,1, - }, - {0,1,2,4,7,-1}, - {0,5} - }, - - { - { - 2,0,0,2,3,0,2,3,3,1,1,1,2,1,1,0,0,2,1,0,0,3,1,0,0,3,3,0,1,0,1,2, - 0,2,0,2,0,1,2,3,2,1,1,0,3,3,3,3,3,3,1,0,3,0,0,2,0,3,2,0,2,2,0,1, - }, - {0,1,3,5,6,-1}, - {1,3} - }, - - { - { - 0,1,1,2,1,3,1,1,0,0,3,1,1,1,2,0,3,2,0,1,1,2,3,3,3,0,3,0,0,2,0,3, - 3,2,0,0,3,2,3,1,2,3,0,3,2,0,1,2,2,2,0,2,0,1,2,2,3,1,2,2,1,1,1,1, - }, - {0,2,3,4,5,7}, - {2,7} - }, - - { - { - 0,1,2,0,3,3,0,3,2,1,3,3,0,3,1,1,3,2,3,2,3,0,0,0,3,0,2,2,3,2,2,3, - 2,2,3,1,2,3,1,2,0,3,0,2,3,1,0,0,3,2,1,2,1,2,1,3,1,0,2,3,3,1,3,2, - }, - {2,3,4,5,6,7}, - {4,6} - }, - }, -}; - -const int naomi_m2_board::fn1_game_key_scheduling[38][2] = { - {1,29}, {1,71}, {2,4}, {2,54}, {3,8}, {4,56}, {4,73}, {5,11}, - {6,51}, {7,92}, {8,89}, {9,9}, {9,10}, {9,39}, {9,41}, {9,58}, - {9,59}, {9,86}, {10,90}, {11,6}, {12,64}, {13,49}, {14,44}, {15,40}, - {16,69}, {17,15}, {18,23}, {18,43}, {19,82}, {20,81}, {21,32}, {21,61}, - {22,5}, {23,66}, {24,13}, {24,45}, {25,12}, {25,35} -}; - -const int naomi_m2_board::fn2_game_key_scheduling[34][2] = { - {0,0}, {1,3}, {2,11}, {3,20}, {4,22}, {5,23}, {6,29}, {7,38}, - {8,39}, {9,47}, {9,55}, {9,86}, {9,87}, {9,90}, {10,50}, {10,53}, - {11,57}, {12,59}, {13,61}, {13,64}, {14,63}, {15,67}, {16,72}, {17,83}, - {18,88}, {19,94}, {20,35}, {21,17}, {21,92}, {22,6}, {22,11}, {23,85}, - {24,16}, {25,25} -}; - -const int naomi_m2_board::fn1_sequence_key_scheduling[20][2] = { - {0,52}, {1,34}, {2,17}, {3,36}, {4,84}, {4,88}, {5,57}, {6,48}, - {6,68}, {7,76}, {8,83}, {9,30}, {10,22}, {10,41}, {11,38}, {12,55}, - {13,74}, {14,19}, {14,80}, {15,26} -}; - -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}; - -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}; - -int naomi_m2_board::feistel_function(int input, const struct sbox *sboxes, UINT32 subkeys) -{ - int k,m; - int aux; - int result=0; - - for (m=0; m<4; ++m) { // 4 sboxes - for (k=0, aux=0; k<6; ++k) - if (sboxes[m].inputs[k]!=-1) - aux |= BIT(input, sboxes[m].inputs[k]) << k; - - aux = sboxes[m].table[(aux^subkeys)&0x3f]; - - for (k=0; k<2; ++k) - result |= BIT(aux,k) << sboxes[m].outputs[k]; - - subkeys >>=6; - } - - return result; -} - -/************************** -This implementation is an "educational" version. It must be noted that it can be speed-optimized in a number of ways. -The most evident one is to factor out the parts of the key-scheduling that must only be done once (like the game-key & -sequence-key parts) as noted in the comments inlined in the function. More sophisticated speed-ups can be gained by -noticing that the weak key-scheduling would allow to create some pregenerated look-up tables for doing most of the work -of the function. Even so, it would still be pretty slow, so caching techniques could be a wiser option here. -**************************/ - -UINT16 naomi_m2_board::block_decrypt(UINT32 game_key, UINT16 sequence_key, UINT16 counter, UINT16 data) -{ - int j; - int aux,aux2; - int A,B; - int middle_result; - UINT32 fn1_subkeys[4]; - UINT32 fn2_subkeys[4]; - - /* Game-key scheduling; this could be done just once per game at initialization time */ - memset(fn1_subkeys,0,sizeof(UINT32)*4); - memset(fn2_subkeys,0,sizeof(UINT32)*4); - - for (j=0; j<38; ++j) { - if (BIT(game_key, fn1_game_key_scheduling[j][0])!=0) { - aux = fn1_game_key_scheduling[j][1]%24; - aux2 = fn1_game_key_scheduling[j][1]/24; - fn1_subkeys[aux2] ^= (1<<aux); - } - } - - for (j=0; j<34; ++j) { - if (BIT(game_key, fn2_game_key_scheduling[j][0])!=0) { - aux = fn2_game_key_scheduling[j][1]%24; - aux2 = fn2_game_key_scheduling[j][1]/24; - fn2_subkeys[aux2] ^= (1<<aux); - } - } - /********************************************************/ - - /* Sequence-key scheduling; this could be done just once per decryption run */ - for (j=0; j<20; ++j) { - if (BIT(sequence_key,fn1_sequence_key_scheduling[j][0])!=0) { - aux = fn1_sequence_key_scheduling[j][1]%24; - aux2 = fn1_sequence_key_scheduling[j][1]/24; - fn1_subkeys[aux2] ^= (1<<aux); - } - } - for (j=0; j<16; ++j) { - if (BIT(sequence_key,j)!=0) { - aux = fn2_sequence_key_scheduling[j]%24; - aux2 = fn2_sequence_key_scheduling[j]/24; - fn2_subkeys[aux2] ^= (1<<aux); + case 0x1fff8: m_cryptdevice->set_addr_low(data); return; + case 0x1fffa: m_cryptdevice->set_addr_high(data); return; + case 0x1fffc: m_cryptdevice->set_subkey(data); return; } } - - // subkeys bits 10 & 41 - fn2_subkeys[0] ^= (BIT(sequence_key,2)<<10); - fn2_subkeys[1] ^= (BIT(sequence_key,4)<<17); - /**************************************************************/ - - // First Feistel Network - - aux = BITSWAP16(counter,5,12,14,13,9,3,6,4, 8,1,15,11,0,7,10,2); - - // 1st round - B = aux >> 8; - A = (aux & 0xff) ^ feistel_function(B,fn1_sboxes[0],fn1_subkeys[0]); - - // 2nd round - B = B ^ feistel_function(A,fn1_sboxes[1],fn1_subkeys[1]); - - // 3rd round - A = A ^ feistel_function(B,fn1_sboxes[2],fn1_subkeys[2]); - - // 4th round - B = B ^ feistel_function(A,fn1_sboxes[3],fn1_subkeys[3]); - - middle_result = (B<<8)|A; - - - /* Middle-result-key sheduling */ - for (j=0; j<16; ++j) { - if (BIT(middle_result,j)!=0) { - aux = fn2_middle_result_scheduling[j]%24; - aux2 = fn2_middle_result_scheduling[j]/24; - fn2_subkeys[aux2] ^= (1<<aux); - } - } - /*********************/ - - // Second Feistel Network - - aux = BITSWAP16(data,14,3,8,12,13,7,15,4, 6,2,9,5,11,0,1,10); - - // 1st round - B = aux >> 8; - A = (aux & 0xff) ^ feistel_function(B,fn2_sboxes[0],fn2_subkeys[0]); - - // 2nd round - B = B ^ feistel_function(A,fn2_sboxes[1],fn2_subkeys[1]); - - // 3rd round - A = A ^ feistel_function(B,fn2_sboxes[2],fn2_subkeys[2]); - - // 4th round - B = B ^ feistel_function(A,fn2_sboxes[3],fn2_subkeys[3]); - - aux = (B<<8)|A; - - aux = BITSWAP16(aux,15,7,6,14,13,12,5,4, 3,2,11,10,9,1,0,8); - - return aux; + throw emu_fatalerror("NAOMIM2: unhandled board write %08x, %04x\n", offset, data); } -UINT16 naomi_m2_board::get_decrypted_16() +UINT16 naomi_m2_board::read_callback(UINT32 addr) { - UINT16 enc; + if ((addr & 0xffff0000) == 0x01000000) { + int base = 2*(addr & 0x7fff); + return ram[base+1] | (ram[base] << 8); - if((prot_cur_address & 0xffff0000) == 0x01000000) { - int base = 2*(prot_cur_address & 0x7fff); - enc = ram[base+1] | (ram[base] << 8); - } else { - const UINT8 *base = m_region->base() + 2*prot_cur_address; - enc = base[1] | (base[0] << 8); } - - UINT16 dec = block_decrypt(key, subkey, prot_cur_address, enc); - UINT16 res = (dec & 3) | (dec_hist & 0xfffc); - dec_hist = dec; - - prot_cur_address ++; - return res; -} - -void naomi_m2_board::enc_start() -{ - buffer_pos = BUFFER_SIZE; - dec_header = get_decrypted_16() << 16; - dec_header |= get_decrypted_16(); - - if(dec_header & FLAG_COMPRESSED) { - line_buffer_size = dec_header & FLAG_LINE_SIZE_512 ? 512 : 256; - line_buffer_pos = line_buffer_size; - buffer_bit = 7; + else { + const UINT8 *base = m_region->base() + 2*addr; + return base[1] | (base[0] << 8); } - enc_ready = true; } -void naomi_m2_board::enc_fill() -{ - assert(buffer_pos == BUFFER_SIZE); - for(int i = 0; i != BUFFER_SIZE; i+=2) { - UINT16 val = get_decrypted_16(); - buffer[i] = val; - buffer[i+1] = val >> 8; - } - buffer_pos = 0; -} +static MACHINE_CONFIG_FRAGMENT( naomim2 ) + MCFG_DEVICE_ADD("segam2crypt", SEGA315_5881_CRYPT, 0) + MCFG_SET_READ_CALLBACK(naomi_m2_board, read_callback) +MACHINE_CONFIG_END -/* node format -0xxxxxxx - next node index -1a0bbccc - end node - a - 0 = repeat - 1 = fetch - b - if a = 1 - 00 - fetch 0 - 01 - fetch 1 - 11 - fetch -1 - if a = 0 - 000 - c - repeat/fetch counter - count = ccc + 1 -11111111 - empty node -*/ -const UINT8 naomi_m2_board::trees[9][2][32] = { - { - {0x01,0x10,0x0f,0x05,0xc4,0x13,0x87,0x0a,0xcc,0x81,0xce,0x0c,0x86,0x0e,0x84,0xc2, - 0x11,0xc1,0xc3,0xcf,0x15,0xc8,0xcd,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, - {0xc7,0x02,0x03,0x04,0x80,0x06,0x07,0x08,0x09,0xc9,0x0b,0x0d,0x82,0x83,0x85,0xc0, - 0x12,0xc6,0xc5,0x14,0x16,0xca,0xcb,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, - }, - { - {0x02,0x80,0x05,0x04,0x81,0x10,0x15,0x82,0x09,0x83,0x0b,0x0c,0x0d,0xdc,0x0f,0xde, - 0x1c,0xcf,0xc5,0xdd,0x86,0x16,0x87,0x18,0x19,0x1a,0xda,0xca,0xc9,0x1e,0xce,0xff,}, - {0x01,0x17,0x03,0x0a,0x08,0x06,0x07,0xc2,0xd9,0xc4,0xd8,0xc8,0x0e,0x84,0xcb,0x85, - 0x11,0x12,0x13,0x14,0xcd,0x1b,0xdb,0xc7,0xc0,0xc1,0x1d,0xdf,0xc3,0xc6,0xcc,0xff,}, - }, - { - {0xc6,0x80,0x03,0x0b,0x05,0x07,0x82,0x08,0x15,0xdc,0xdd,0x0c,0xd9,0xc2,0x14,0x10, - 0x85,0x86,0x18,0x16,0xc5,0xc4,0xc8,0xc9,0xc0,0xcc,0xff,0xff,0xff,0xff,0xff,0xff,}, - {0x01,0x02,0x12,0x04,0x81,0x06,0x83,0xc3,0x09,0x0a,0x84,0x11,0x0d,0x0e,0x0f,0x19, - 0xca,0xc1,0x13,0xd8,0xda,0xdb,0x17,0xde,0xcd,0xcb,0xff,0xff,0xff,0xff,0xff,0xff,}, - }, - { - {0x01,0x80,0x0d,0x04,0x05,0x15,0x83,0x08,0xd9,0x10,0x0b,0x0c,0x84,0x0e,0xc0,0x14, - 0x12,0xcb,0x13,0xca,0xc8,0xc2,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, - {0xc5,0x02,0x03,0x07,0x81,0x06,0x82,0xcc,0x09,0x0a,0xc9,0x11,0xc4,0x0f,0x85,0xd8, - 0xda,0xdb,0xc3,0xdc,0xdd,0xc1,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, - }, - { - {0x01,0x80,0x06,0x0c,0x05,0x81,0xd8,0x84,0x09,0xdc,0x0b,0x0f,0x0d,0x0e,0x10,0xdb, - 0x11,0xca,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, - {0xc4,0x02,0x03,0x04,0xcb,0x0a,0x07,0x08,0xd9,0x82,0xc8,0x83,0xc0,0xc1,0xda,0xc2, - 0xc9,0xc3,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, - }, - { - {0x01,0x02,0x06,0x0a,0x83,0x0b,0x07,0x08,0x09,0x82,0xd8,0x0c,0xd9,0xda,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, - {0xc3,0x80,0x03,0x04,0x05,0x81,0xca,0xc8,0xdb,0xc9,0xc0,0xc1,0x0d,0xc2,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, - }, - { - {0x01,0x02,0x03,0x04,0x81,0x07,0x08,0xd8,0xda,0xd9,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, - {0xc2,0x80,0x05,0xc9,0xc8,0x06,0x82,0xc0,0x09,0xc1,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, - }, - { - {0x01,0x80,0x04,0xc8,0xc0,0xd9,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, - {0xc1,0x02,0x03,0x81,0x05,0xd8,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, - }, - { - {0x01,0xd8,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, - {0xc0,0x80,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,}, - }, -}; - -int naomi_m2_board::get_compressed_bit() +machine_config_constructor naomi_m2_board::device_mconfig_additions() const { - if(buffer_pos == BUFFER_SIZE) - enc_fill(); - int res = (buffer[buffer_pos^1] >> buffer_bit) & 1; - buffer_bit--; - if(buffer_bit == -1) { - buffer_bit = 7; - buffer_pos++; - } - return res; + return MACHINE_CONFIG_NAME( naomim2 ); } -void naomi_m2_board::line_fill() -{ - assert(line_buffer_pos == line_buffer_size); - UINT8 *lp = line_buffer; - UINT8 *lc = line_buffer_prev; - line_buffer = lc; - line_buffer_prev = lp; - line_buffer_pos = 0; - - UINT32 line_buffer_mask = line_buffer_size-1; - - for(int i=0; i != line_buffer_size;) { - // vlc 0: start of line - // vlc 1: interior of line - // vlc 2-9: 7-1 bytes from end of line - - int slot = i ? i < line_buffer_size - 7 ? 1 : (i & 7) + 1 : 0; - - UINT32 tmp = 0; - while (!(tmp&0x80)) - if(get_compressed_bit()) - tmp = trees[slot][1][tmp]; - else - tmp = trees[slot][0][tmp]; - if(tmp != 0xff) { - int count = (tmp & 7) + 1; - - if(tmp&0x40) { - // Copy from previous line - - static int offsets[4] = {0, 1, 0, -1}; - int offset = offsets[(tmp & 0x18) >> 3]; - for(int j=0; j != count; j++) { - lc[i^1] = lp[((i+offset) & line_buffer_mask)^1]; - i++; - } - - } else { - // Get a byte in the stream and write n times - UINT8 byte; - byte = get_compressed_bit() << 1; - byte = (byte | get_compressed_bit()) << 1; - byte = (byte | get_compressed_bit()) << 1; - byte = (byte | get_compressed_bit()) << 1; - byte = (byte | get_compressed_bit()) << 1; - byte = (byte | get_compressed_bit()) << 1; - byte = (byte | get_compressed_bit()) << 1; - byte = byte | get_compressed_bit(); - for(int j=0; j != count; j++) - lc[(i++)^1] = byte; - - } - } - } -} diff --git a/src/mame/machine/naomim2.h b/src/mame/machine/naomim2.h index e5406763d48..4efce0fcbe8 100644 --- a/src/mame/machine/naomim2.h +++ b/src/mame/machine/naomim2.h @@ -2,6 +2,7 @@ #define _NAOMIM2_H_ #include "naomibd.h" +#include "315-5881_crypt.h" #define MCFG_NAOMI_M2_BOARD_ADD(_tag, _key_tag, _eeprom_tag, _actel_tag, _irq_cb) \ MCFG_NAOMI_BOARD_ADD(_tag, NAOMI_M2_BOARD, _eeprom_tag, _actel_tag, _irq_cb) \ @@ -11,12 +12,17 @@ class naomi_m2_board : public naomi_board { public: naomi_m2_board(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + UINT32 rom_cur_address; + static const int RAM_SIZE = 65536; + UINT8* ram; static void static_set_tags(device_t &device, const char *_key_tag); + UINT16 read_callback(UINT32 addr); protected: virtual void device_start(); virtual void device_reset(); + virtual machine_config_constructor device_mconfig_additions() const; virtual void board_setup_address(UINT32 address, bool is_dma); virtual void board_get_buffer(UINT8 *&base, UINT32 &limit); @@ -24,48 +30,12 @@ protected: virtual void board_write(offs_t offset, UINT16 data); private: - enum { - RAM_SIZE = 65536, BUFFER_SIZE = 32768, LINE_SIZE = 512, - FLAG_COMPRESSED = 0x10000, FLAG_LINE_SIZE_512 = 0x20000 - }; - const char *key_tag; - UINT32 key; - - UINT8 *ram, *buffer, *line_buffer, *line_buffer_prev; - UINT32 rom_cur_address, prot_cur_address; - UINT16 subkey, dec_hist; - UINT32 dec_header; - bool enc_ready; - - int buffer_pos, line_buffer_pos, line_buffer_size, buffer_bit; - - struct sbox { - UINT8 table[64]; - int inputs[6]; // positions of the inputs bits, -1 means no input except from key - int outputs[2]; // positions of the output bits - }; - - static const sbox fn1_sboxes[4][4]; - static const sbox fn2_sboxes[4][4]; - static const int fn1_game_key_scheduling[38][2]; - static const int fn2_game_key_scheduling[34][2]; - static const int fn1_sequence_key_scheduling[20][2]; - static const int fn2_sequence_key_scheduling[16]; - static const int fn2_middle_result_scheduling[16]; - - static const UINT8 trees[9][2][32]; - - int feistel_function(int input, const struct sbox *sboxes, UINT32 subkeys); - UINT16 block_decrypt(UINT32 game_key, UINT16 sequence_key, UINT16 counter, UINT16 data); - - UINT16 get_decrypted_16(); - int get_compressed_bit(); + const char *key_tag; - void enc_start(); - void enc_fill(); - void line_fill(); + + required_device<sega_315_5881_crypt_device> m_cryptdevice; }; extern const device_type NAOMI_M2_BOARD; diff --git a/src/mame/machine/stvprot.c b/src/mame/machine/stvprot.c index ede1c0264bc..901cf83e003 100644 --- a/src/mame/machine/stvprot.c +++ b/src/mame/machine/stvprot.c @@ -572,6 +572,8 @@ UINT32 ffreveng_prot_read_callback( address_space &space, int protaddr, UINT32 k * *************************************/ +// the naomi hookup of 315-5881 reads 16-bits at a time, here we seem to read 32? + READ32_MEMBER( stv_state::common_prot_r ) { UINT32 *ROM = (UINT32 *)space.machine().root_device().memregion("abus")->base(); @@ -616,10 +618,18 @@ WRITE32_MEMBER ( stv_state::common_prot_w ) else if(offset == 2) { COMBINE_DATA(&m_abus_prot_addr); + + m_cryptdevice->set_addr_low(m_abus_prot_addr >> 16); + m_cryptdevice->set_addr_high(m_abus_prot_addr&0xffff); + } else if(offset == 3) { COMBINE_DATA(&m_abus_protkey); + + m_cryptdevice->set_subkey(m_abus_protkey); + + int a_bus_vector; a_bus_vector = m_abus_prot_addr >> 16; a_bus_vector|= (m_abus_prot_addr & 0xffff) << 16; diff --git a/src/mame/mame.mak b/src/mame/mame.mak index 3629b6b25e1..a378753cc90 100644 --- a/src/mame/mame.mak +++ b/src/mame/mame.mak @@ -1707,6 +1707,7 @@ $(MAMEOBJ)/sega.a: \ $(DRIVERS)/naomi.o $(MACHINE)/dc.o $(VIDEO)/powervr2.o $(MACHINE)/naomi.o \ $(MACHINE)/naomig1.o $(MACHINE)/naomibd.o $(MACHINE)/naomirom.o $(MACHINE)/naomigd.o \ $(MACHINE)/naomicrypt.o $(MACHINE)/naomim1.o $(MACHINE)/naomim2.o $(MACHINE)/naomim4.o \ + $(MACHINE)/315-5881_crypt.o \ $(MACHINE)/awboard.o \ $(MACHINE)/mie.o $(MACHINE)/maple-dc.o $(MACHINE)/mapledev.o $(MACHINE)/dc-ctrl.o $(MACHINE)/jvs13551.o \ $(DRIVERS)/triforce.o \ |