1 // license:BSD-3-Clause
2 // copyright-holders:Olivier Galibert,Andreas Naive
3
4 #include "emu.h"
5 #include "naomim4.h"
6
7 // Decoder for M4-type NAOMI cart encryption
8
9 // In hardware, the decryption is managed by the XC3S50 Xilinx Spartan FPGA (IC2)
10 // and the annexed PIC16C621A PIC MCU (IC3).
11 // - The FPGA control the clock line of the security PIC.
12 // - The protocol between the FPGA and the MCU is nibble-based, though it hasn't been RE for now.
13 // - The decryption algorithm is clearly nibble-based too.
14
15 // The decryption algorithm itself implements a stream cipher built on top of a 16-bits block cipher.
16 // The underlying block-cipher is a SP-network of 2 rounds (both identical in structure). In every
17 // round, the substitution phase is done using 4 fixed 4-to-4 sboxes acting on every nibble. The permutation
18 // phase is indeed a nibble-based linear combination.
19 // With that block cipher, a stream cipher is constructed by feeding the output result of the 1st round
20 // of a certain 16-bits block as a whitening value for the next block. The cart dependent data used by
21 // the algorithm is a 32-bits key stored in the PIC16C621A. The hardware auto-reset the feed value
22 // to the cart-based IV every 16 blocks (32 bytes); that reset is not address-based, but index-based.
23
24 DEFINE_DEVICE_TYPE(NAOMI_M4_BOARD, naomi_m4_board, "naomi_m4_board", "Sega NAOMI M4 Board")
25
26 const uint8_t naomi_m4_board::k_sboxes[4][16] = {
27 {9,8,2,11,1,14,5,15,12,6,0,3,7,13,10,4},
28 {2,10,0,15,14,1,11,3,7,12,13,8,4,9,5,6},
29 {4,11,3,8,7,2,15,13,1,5,14,9,6,12,0,10},
30 {1,13,8,2,0,5,6,14,4,11,15,10,12,3,7,9}
31 };
32
33 // from S29GL512N datasheet
34 static uint8_t cfidata[] = {
35 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
36 0x51,0x00,0x52,0x00,0x59,0x00,0x02,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x27,0x00,0x36,0x00,0x00,0x00,0x00,0x00,0x07,0x00,
37 0x07,0x00,0x0a,0x00,0x00,0x00,0x01,0x00,0x05,0x00,0x04,0x00,0x00,0x00,0x1a,0x00,0x02,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x01,0x00,0xff,0x00,0x01,0x00,0x00,0x00,
38 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
39 0x50,0x00,0x52,0x00,0x49,0x00,0x31,0x00,0x33,0x00,0x10,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0xb5,0x00,0xc5,0x00,0x04,0x00,
40 0x01,0x00
41 };
42
submap(address_map & map)43 void naomi_m4_board::submap(address_map &map)
44 {
45 naomi_board::submap(map);
46 map(0x1a, 0x1b).r(FUNC(naomi_m4_board::m4_id_r)); // Read: bits 8-15 - 0x55, bit 7 - 1 if IC7 EPR rom enabled; Write: bit 0 - master/slave board selection.
47 }
48
naomi_m4_board(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)49 naomi_m4_board::naomi_m4_board(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
50 : naomi_board(mconfig, NAOMI_M4_BOARD, tag, owner, clock)
51 , m_region(*this, DEVICE_SELF)
52 , m_key_data(*this, finder_base::DUMMY_TAG)
53 {
54 }
55
device_start()56 void naomi_m4_board::device_start()
57 {
58 naomi_board::device_start();
59
60 std::string sid = parameter("id");
61 if (!sid.empty())
62 m4id = strtoll(sid.c_str(), nullptr, 16);
63 else
64 {
65 logerror("%s: Warning: M4 ID not provided\n", tag());
66 m4id = 0x5504;
67 }
68
69 subkey1 = (m_key_data[0x5e2] << 8) | m_key_data[0x5e0];
70 subkey2 = (m_key_data[0x5e6] << 8) | m_key_data[0x5e4];
71
72 buffer = std::make_unique<uint8_t[]>(BUFFER_SIZE);
73 enc_init();
74
75 save_pointer(NAME(buffer), BUFFER_SIZE);
76 save_item(NAME(rom_cur_address));
77 save_item(NAME(buffer_actual_size));
78 save_item(NAME(encryption));
79 save_item(NAME(cfi_mode));
80 save_item(NAME(counter));
81 }
82
enc_init()83 void naomi_m4_board::enc_init()
84 {
85 one_round = std::make_unique<uint16_t[]>(0x10000);
86
87 for(int round_input = 0; round_input < 0x10000; round_input++) {
88 uint8_t input_nibble[4];
89 uint8_t output_nibble[4];
90
91 for (int nibble_idx = 0; nibble_idx < 4; ++nibble_idx) {
92 input_nibble[nibble_idx] = (round_input >> (nibble_idx*4)) & 0xf;
93 output_nibble[nibble_idx] = 0;
94 }
95
96 uint8_t aux_nibble = input_nibble[3];
97 for (int nibble_idx = 0; nibble_idx < 4; ++nibble_idx) { // 4 s-boxes per round
98 aux_nibble ^= k_sboxes[nibble_idx][input_nibble[nibble_idx]];
99 for (int i = 0; i < 4; ++i) // diffusion of the bits
100 output_nibble[(nibble_idx - i) & 3] |= aux_nibble & (1 << i);
101 }
102
103 uint16_t result = 0;
104 for (int nibble_idx = 0; nibble_idx < 4; ++nibble_idx)
105 result |= (output_nibble[nibble_idx] << (4 * nibble_idx));
106
107 one_round[round_input] = result;
108 }
109 }
110
device_reset()111 void naomi_m4_board::device_reset()
112 {
113 naomi_board::device_reset();
114 rom_cur_address = 0;
115 buffer_actual_size = 0;
116 encryption = false;
117 cfi_mode = false;
118 counter = 0;
119 iv = 0;
120 }
121
board_setup_address(uint32_t address,bool is_dma)122 void naomi_m4_board::board_setup_address(uint32_t address, bool is_dma)
123 {
124 rom_cur_address = address & 0x1ffffffe;
125 encryption = rom_offset & 0x40000000;
126
127 if(encryption) {
128 enc_reset();
129 enc_fill();
130 }
131 }
132
board_get_buffer(uint8_t * & base,uint32_t & limit)133 void naomi_m4_board::board_get_buffer(uint8_t *&base, uint32_t &limit)
134 {
135 static uint8_t retzero[2] = { 0, 0 };
136
137 if (cfi_mode) {
138 int fpr_num = m4id & 0x7f;
139
140 if (((rom_cur_address >> 26) & 0x07) < fpr_num) {
141 base = &cfidata[rom_cur_address & 0xffff];
142 limit = 2;
143 return;
144 }
145 }
146
147 if(encryption) {
148 base = buffer.get();
149 limit = BUFFER_SIZE;
150
151 } else {
152 uint32_t size = m_region->bytes();
153 if (rom_cur_address < size)
154 {
155 base = m_region->base() + rom_cur_address;
156 limit = size - rom_cur_address;
157 } else {
158 base = retzero;
159 limit = 2;
160 }
161 }
162 }
163
board_advance(uint32_t size)164 void naomi_m4_board::board_advance(uint32_t size)
165 {
166 if(encryption) {
167 if(size < buffer_actual_size) {
168 memmove(buffer.get(), buffer.get() + size, buffer_actual_size - size);
169 buffer_actual_size -= size;
170 } else
171 buffer_actual_size = 0;
172 enc_fill();
173
174 } else
175 rom_cur_address += size;
176 }
177
enc_reset()178 void naomi_m4_board::enc_reset()
179 {
180 buffer_actual_size = 0;
181 iv = 0;
182 counter = 0;
183 }
184
decrypt_one_round(uint16_t word,uint16_t subkey)185 uint16_t naomi_m4_board::decrypt_one_round(uint16_t word, uint16_t subkey)
186 {
187 return one_round[word ^ subkey] ^ subkey ;
188 }
189
enc_fill()190 void naomi_m4_board::enc_fill()
191 {
192 const uint8_t *base = m_region->base() + rom_cur_address;
193 while(buffer_actual_size < BUFFER_SIZE) {
194 uint16_t enc = base[0] | (base[1] << 8);
195 uint16_t dec = iv;
196 iv = decrypt_one_round(enc ^ iv, subkey1);
197 dec ^= decrypt_one_round(iv, subkey2);
198
199 buffer[buffer_actual_size++] = dec;
200 buffer[buffer_actual_size++] = dec >> 8;
201
202 base += 2;
203 rom_cur_address += 2;
204
205 counter++;
206 if(counter == 16) {
207 counter = 0;
208 iv = 0;
209 }
210 }
211 }
212
m4_id_r()213 uint16_t naomi_m4_board::m4_id_r()
214 {
215 return m4id & 0xff80;
216 }
217
board_write(offs_t offset,uint16_t data)218 void naomi_m4_board::board_write(offs_t offset, uint16_t data)
219 {
220 if (((offset&0xffff) == 0x00aa) && (data == 0x0098))
221 cfi_mode = true;
222 if (((offset&0xffff) == 0x0000) && (data == 0x00f0))
223 cfi_mode = false;
224 }
225