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