2 ## This file is part of the libsigrokdecode project.
4 ## Copyright (C) 2018 Michalis Pappas <mpappas@fastmail.fm>
6 ## This program is free software; you can redistribute it and/or modify
7 ## it under the terms of the GNU General Public License as published by
8 ## the Free Software Foundation; either version 2 of the License, or
9 ## (at your option) any later version.
11 ## This program is distributed in the hope that it will be useful,
12 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ## GNU General Public License for more details.
16 ## You should have received a copy of the GNU General Public License
17 ## along with this program; if not, see <http://www.gnu.org/licenses/>.
20 import sigrokdecode as srd
22 WORD_ADDR_RESET = 0x00
23 WORD_ADDR_SLEEP = 0x01
25 WORD_ADDR_COMMAND = 0x03
27 WORD_ADDR = {0x00: 'RESET', 0x01: 'SLEEP', 0x02: 'IDLE', 0x03: 'COMMAND'}
30 OPCODE_DERIVE_KEY = 0x1c
36 OPCODE_CHECK_MAC = 0x28
41 OPCODE_PRIVWRITE = 0x46
46 OPCODE_UPDATE_EXTRA = 0x20
77 ZONES = {0x00: 'CONFIG', 0x01: 'OTP', 0x02: 'DATA'}
80 STATUS_CHECKMAC_FAIL = 0x01
81 STATUS_PARSE_ERROR = 0x03
82 STATUS_EXECUTION_ERROR = 0x0f
84 STATUS_CRC_COMM_ERROR = 0xff
87 0x00: 'Command success',
88 0x01: 'Checkmac failure',
90 0x0f: 'Execution error',
92 0xff: 'CRC / communications error',
95 class Decoder(srd.Decoder):
99 longname = 'Microchip ATSHA204A'
100 desc = 'Microchip ATSHA204A family crypto authentication protocol.'
104 tags = ['Security/crypto', 'IC', 'Memory']
106 ('waddr', 'Word address'),
108 ('opcode', 'Opcode'),
109 ('param1', 'Param1'),
110 ('param2', 'Param2'),
113 ('status', 'Status'),
114 ('warning', 'Warning'),
117 ('frame', 'Frames', (0, 1, 2, 3, 4, 5, 6)),
118 ('status-vals', 'Status', (7,)),
119 ('warnings', 'Warnings', (8,)),
127 self.waddr = self.opcode = -1
128 self.ss_block = self.es_block = 0
132 self.out_ann = self.register(srd.OUTPUT_ANN)
134 def output_tx_bytes(self):
136 if len(b) < 1: # Ignore wakeup.
140 if self.waddr == WORD_ADDR_COMMAND:
143 if len(b) - 1 != count:
144 self.put_warning(b[0][0], b[-1][1],
145 'Invalid frame length: Got {}, expecting {} '.format(
148 self.opcode = b[2][2]
149 self.put_opcode(b[2])
150 self.put_param1(b[3])
151 self.put_param2([b[4], b[5]])
152 self.put_data(b[6:-2])
153 self.put_crc([b[-2], b[-1]])
155 def output_rx_bytes(self):
159 if self.waddr == WORD_ADDR_RESET:
160 self.put_data([b[1]])
161 self.put_crc([b[2], b[3]])
162 self.put_status(b[0][0], b[-1][1], b[1][2])
163 elif self.waddr == WORD_ADDR_COMMAND:
164 if count == 4: # Status / Error.
165 self.put_data([b[1]])
166 self.put_crc([b[2], b[3]])
167 self.put_status(b[0][0], b[-1][1], b[1][2])
169 self.put_data(b[1:-2])
170 self.put_crc([b[-2], b[-1]])
172 def putx(self, s, data):
173 self.put(s[0], s[1], self.out_ann, data)
175 def puty(self, s, data):
176 self.put(s[0][0], s[1][1], self.out_ann, data)
178 def putz(self, ss, es, data):
179 self.put(ss, es, self.out_ann, data)
181 def put_waddr(self, s):
182 self.putx(s, [0, ['Word addr: %s' % WORD_ADDR[s[2]]]])
184 def put_count(self, s):
185 self.putx(s, [1, ['Count: %s' % s[2]]])
187 def put_opcode(self, s):
188 self.putx(s, [2, ['Opcode: %s' % OPCODES[s[2]]]])
190 def put_param1(self, s):
192 if op in (OPCODE_CHECK_MAC, OPCODE_COUNTER, OPCODE_DEV_REV, \
193 OPCODE_ECDH, OPCODE_GEN_KEY, OPCODE_HMAC, OPCODE_MAC, \
194 OPCODE_NONCE, OPCODE_RANDOM, OPCODE_SHA, OPCODE_SIGN, \
196 self.putx(s, [3, ['Mode: %02X' % s[2]]])
197 elif op == OPCODE_DERIVE_KEY:
198 self.putx(s, [3, ['Random: %s' % s[2]]])
199 elif op == OPCODE_PRIVWRITE:
200 self.putx(s, [3, ['Encrypted: {}'.format('Yes' if s[2] & 0x40 else 'No')]])
201 elif op == OPCODE_GEN_DIG:
202 self.putx(s, [3, ['Zone: %s' % ZONES[s[2]]]])
203 elif op == OPCODE_LOCK:
204 self.putx(s, [3, ['Zone: {}, Summary: {}'.format(
205 'DATA/OTP' if s[2] else 'CONFIG',
206 'Ignored' if s[2] & 0x80 else 'Used')]])
207 elif op == OPCODE_PAUSE:
208 self.putx(s, [3, ['Selector: %02X' % s[2]]])
209 elif op == OPCODE_READ:
210 self.putx(s, [3, ['Zone: {}, Length: {}'.format(ZONES[s[2] & 0x03],
211 '32 bytes' if s[2] & 0x90 else '4 bytes')]])
212 elif op == OPCODE_WRITE:
213 self.putx(s, [3, ['Zone: {}, Encrypted: {}, Length: {}'.format(ZONES[s[2] & 0x03],
214 'Yes' if s[2] & 0x40 else 'No', '32 bytes' if s[2] & 0x90 else '4 bytes')]])
216 self.putx(s, [3, ['Param1: %02X' % s[2]]])
218 def put_param2(self, s):
220 if op == OPCODE_DERIVE_KEY:
221 self.puty(s, [4, ['TargetKey: {:02x} {:02x}'.format(s[1][2], s[0][2])]])
222 elif op in (OPCODE_COUNTER, OPCODE_ECDH, OPCODE_GEN_KEY, OPCODE_PRIVWRITE, \
223 OPCODE_SIGN, OPCODE_VERIFY):
224 self.puty(s, [4, ['KeyID: {:02x} {:02x}'.format(s[1][2], s[0][2])]])
225 elif op in (OPCODE_NONCE, OPCODE_PAUSE, OPCODE_RANDOM):
226 self.puty(s, [4, ['Zero: {:02x} {:02x}'.format(s[1][2], s[0][2])]])
227 elif op in (OPCODE_HMAC, OPCODE_MAC, OPCODE_CHECK_MAC, OPCODE_GEN_DIG):
228 self.puty(s, [4, ['SlotID: {:02x} {:02x}'.format(s[1][2], s[0][2])]])
229 elif op == OPCODE_LOCK:
230 self.puty(s, [4, ['Summary: {:02x} {:02x}'.format(s[1][2], s[0][2])]])
231 elif op in (OPCODE_READ, OPCODE_WRITE):
232 self.puty(s, [4, ['Address: {:02x} {:02x}'.format(s[1][2], s[0][2])]])
233 elif op == OPCODE_UPDATE_EXTRA:
234 self.puty(s, [4, ['NewValue: {:02x}'.format(s[0][2])]])
236 self.puty(s, [4, ['-']])
238 def put_data(self, s):
242 if op == OPCODE_CHECK_MAC:
243 self.putz(s[0][0], s[31][1], [5, ['ClientChal: %s' % ' '.join(format(i[2], '02x') for i in s[0:32])]])
244 self.putz(s[32][0], s[63][1], [5, ['ClientResp: %s' % ' '.join(format(i[2], '02x') for i in s[32:64])]])
245 self.putz(s[64][0], s[76][1], [5, ['OtherData: %s' % ' '.join(format(i[2], '02x') for i in s[64:77])]])
246 elif op == OPCODE_DERIVE_KEY:
247 self.putz(s[0][0], s[31][1], [5, ['MAC: %s' % ' '.join(format(i[2], '02x') for i in s)]])
248 elif op == OPCODE_ECDH:
249 self.putz(s[0][0], s[31][1], [5, ['Pub X: %s' % ' '.join(format(i[2], '02x') for i in s[0:32])]])
250 self.putz(s[32][0], s[63][1], [5, ['Pub Y: %s' % ' '.join(format(i[2], '02x') for i in s[32:64])]])
251 elif op in (OPCODE_GEN_DIG, OPCODE_GEN_KEY):
252 self.putz(s[0][0], s[3][1], [5, ['OtherData: %s' % ' '.join(format(i[2], '02x') for i in s)]])
253 elif op == OPCODE_MAC:
254 self.putz(s[0][0], s[31][1], [5, ['Challenge: %s' % ' '.join(format(i[2], '02x') for i in s)]])
255 elif op == OPCODE_PRIVWRITE:
256 if len(s) > 36: # Key + MAC.
257 self.putz(s[0][0], s[-35][1], [5, ['Value: %s' % ' '.join(format(i[2], '02x') for i in s)]])
258 self.putz(s[-32][0], s[-1][1], [5, ['MAC: %s' % ' '.join(format(i[2], '02x') for i in s)]])
260 self.putz(s[0][0], s[-1][1], [5, ['Value: %s' % ' '.join(format(i[2], '02x') for i in s)]])
261 elif op == OPCODE_VERIFY:
262 if len(s) >= 64: # ECDSA components (always present)
263 self.putz(s[0][0], s[31][1], [5, ['ECDSA R: %s' % ' '.join(format(i[2], '02x') for i in s[0:32])]])
264 self.putz(s[32][0], s[63][1], [5, ['ECDSA S: %s' % ' '.join(format(i[2], '02x') for i in s[32:64])]])
265 if len(s) == 83: # OtherData (follow ECDSA components in validate / invalidate mode)
266 self.putz(s[64][0], s[82][1], [5, ['OtherData: %s' % ' '.join(format(i[2], '02x') for i in s[64:83])]])
267 if len(s) == 128: # Public key components (follow ECDSA components in external mode)
268 self.putz(s[64][0], s[95][1], [5, ['Pub X: %s' % ' '.join(format(i[2], '02x') for i in s[64:96])]])
269 self.putz(s[96][0], s[127][1], [5, ['Pub Y: %s' % ' '.join(format(i[2], '02x') for i in s[96:128])]])
270 elif op == OPCODE_WRITE:
271 if len(s) > 32: # Value + MAC.
272 self.putz(s[0][0], s[-31][1], [5, ['Value: %s' % ' '.join(format(i[2], '02x') for i in s)]])
273 self.putz(s[-32][0], s[-1][1], [5, ['MAC: %s' % ' '.join(format(i[2], '02x') for i in s)]])
275 self.putz(s[0][0], s[-1][1], [5, ['Value: %s' % ' '.join(format(i[2], '02x') for i in s)]])
277 self.putz(s[0][0], s[-1][1], [5, ['Data: %s' % ' '.join(format(i[2], '02x') for i in s)]])
279 def put_crc(self, s):
280 self.puty(s, [6, ['CRC: {:02X} {:02X}'.format(s[0][2], s[1][2])]])
282 def put_status(self, ss, es, status):
283 self.putz(ss, es, [7, ['Status: %s' % STATUS[status]]])
285 def put_warning(self, ss, es, msg):
286 self.putz(ss, es, [8, ['Warning: %s' % msg]])
288 def decode(self, ss, es, data):
291 if self.state == 'IDLE':
292 # Wait for an I²C START condition.
295 self.state = 'GET SLAVE ADDR'
297 elif self.state == 'GET SLAVE ADDR':
298 # Wait for an address read/write operation.
299 if cmd == 'ADDRESS READ':
300 self.state = 'READ REGS'
301 elif cmd == 'ADDRESS WRITE':
302 self.state = 'WRITE REGS'
303 elif self.state == 'READ REGS':
304 if cmd == 'DATA READ':
305 self.bytes.append([ss, es, databyte])
308 # Reset the opcode before received data, as this causes
309 # responses to be displayed incorrectly.
311 if len(self.bytes) > 0:
312 self.output_rx_bytes()
316 elif self.state == 'WRITE REGS':
317 if cmd == 'DATA WRITE':
318 self.bytes.append([ss, es, databyte])
321 self.output_tx_bytes()