1## 2## This file is part of the libsigrokdecode project. 3## 4## Copyright (C) 2018 Michalis Pappas <mpappas@fastmail.fm> 5## 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. 10## 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. 15## 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/>. 18## 19 20import sigrokdecode as srd 21 22WORD_ADDR_RESET = 0x00 23WORD_ADDR_SLEEP = 0x01 24WORD_ADDR_IDLE = 0x02 25WORD_ADDR_COMMAND = 0x03 26 27WORD_ADDR = {0x00: 'RESET', 0x01: 'SLEEP', 0x02: 'IDLE', 0x03: 'COMMAND'} 28 29OPCODE_COUNTER = 0x24 30OPCODE_DERIVE_KEY = 0x1c 31OPCODE_DEV_REV = 0x30 32OPCODE_ECDH = 0x43 33OPCODE_GEN_DIG = 0x15 34OPCODE_GEN_KEY = 0x40 35OPCODE_HMAC = 0x11 36OPCODE_CHECK_MAC = 0x28 37OPCODE_LOCK = 0x17 38OPCODE_MAC = 0x08 39OPCODE_NONCE = 0x16 40OPCODE_PAUSE = 0x01 41OPCODE_PRIVWRITE = 0x46 42OPCODE_RANDOM = 0x1b 43OPCODE_READ = 0x02 44OPCODE_SHA = 0x47 45OPCODE_SIGN = 0x41 46OPCODE_UPDATE_EXTRA = 0x20 47OPCODE_VERIFY = 0x45 48OPCODE_WRITE = 0x12 49 50OPCODES = { 51 0x01: 'Pause', 52 0x02: 'Read', 53 0x08: 'MAC', 54 0x11: 'HMAC', 55 0x12: 'Write', 56 0x15: 'GenDig', 57 0x16: 'Nonce', 58 0x17: 'Lock', 59 0x1b: 'Random', 60 0x1c: 'DeriveKey', 61 0x20: 'UpdateExtra', 62 0x24: 'Counter', 63 0x28: 'CheckMac', 64 0x30: 'DevRev', 65 0x40: 'GenKey', 66 0x41: 'Sign', 67 0x43: 'ECDH', 68 0x45: 'Verify', 69 0x46: 'PrivWrite', 70 0x47: 'SHA', 71} 72 73ZONE_CONFIG = 0x00 74ZONE_OTP = 0x01 75ZONE_DATA = 0x02 76 77ZONES = {0x00: 'CONFIG', 0x01: 'OTP', 0x02: 'DATA'} 78 79STATUS_SUCCESS = 0x00 80STATUS_CHECKMAC_FAIL = 0x01 81STATUS_PARSE_ERROR = 0x03 82STATUS_EXECUTION_ERROR = 0x0f 83STATUS_READY = 0x11 84STATUS_CRC_COMM_ERROR = 0xff 85 86STATUS = { 87 0x00: 'Command success', 88 0x01: 'Checkmac failure', 89 0x03: 'Parse error', 90 0x0f: 'Execution error', 91 0x11: 'Ready', 92 0xff: 'CRC / communications error', 93} 94 95class Decoder(srd.Decoder): 96 api_version = 3 97 id = 'atsha204a' 98 name = 'ATSHA204A' 99 longname = 'Microchip ATSHA204A' 100 desc = 'Microchip ATSHA204A family crypto authentication protocol.' 101 license = 'gplv2+' 102 inputs = ['i2c'] 103 outputs = [] 104 tags = ['Security/crypto', 'IC', 'Memory'] 105 annotations = ( 106 ('waddr', 'Word address'), 107 ('count', 'Count'), 108 ('opcode', 'Opcode'), 109 ('param1', 'Param1'), 110 ('param2', 'Param2'), 111 ('data', 'Data'), 112 ('crc', 'CRC'), 113 ('status', 'Status'), 114 ('warning', 'Warning'), 115 ) 116 annotation_rows = ( 117 ('frame', 'Frame', (0, 1, 2, 3, 4, 5, 6)), 118 ('status', 'Status', (7,)), 119 ('warnings', 'Warnings', (8,)), 120 ) 121 122 def __init__(self): 123 self.reset() 124 125 def reset(self): 126 self.state = 'IDLE' 127 self.waddr = self.opcode = -1 128 self.ss_block = self.es_block = 0 129 self.bytes = [] 130 131 def start(self): 132 self.out_ann = self.register(srd.OUTPUT_ANN) 133 134 def output_tx_bytes(self): 135 b = self.bytes 136 if len(b) < 1: # Ignore wakeup. 137 return 138 self.waddr = b[0][2] 139 self.put_waddr(b[0]) 140 if self.waddr == WORD_ADDR_COMMAND: 141 count = b[1][2] 142 self.put_count(b[1]) 143 if len(b) - 1 != count: 144 self.put_warning(b[0][0], b[-1][1], 145 'Invalid frame length: Got {}, expecting {} '.format( 146 len(b) - 1, count)) 147 return 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]]) 154 155 def output_rx_bytes(self): 156 b = self.bytes 157 count = b[0][2] 158 self.put_count(b[0]) 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]) 168 else: 169 self.put_data(b[1:-2]) 170 self.put_crc([b[-2], b[-1]]) 171 172 def putx(self, s, data): 173 self.put(s[0], s[1], self.out_ann, data) 174 175 def puty(self, s, data): 176 self.put(s[0][0], s[1][1], self.out_ann, data) 177 178 def putz(self, ss, es, data): 179 self.put(ss, es, self.out_ann, data) 180 181 def put_waddr(self, s): 182 self.putx(s, [0, ['Word addr: %s' % WORD_ADDR[s[2]]]]) 183 184 def put_count(self, s): 185 self.putx(s, [1, ['Count: %s' % s[2]]]) 186 187 def put_opcode(self, s): 188 self.putx(s, [2, ['Opcode: %s' % OPCODES[s[2]]]]) 189 190 def put_param1(self, s): 191 op = self.opcode 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, \ 195 OPCODE_VERIFY): 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')]]) 215 else: 216 self.putx(s, [3, ['Param1: %02X' % s[2]]]) 217 218 def put_param2(self, s): 219 op = self.opcode 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])]]) 235 else: 236 self.puty(s, [4, ['-']]) 237 238 def put_data(self, s): 239 if len(s) == 0: 240 return 241 op = self.opcode 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)]]) 259 else: # Just value. 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)]]) 274 else: # Just value. 275 self.putz(s[0][0], s[-1][1], [5, ['Value: %s' % ' '.join(format(i[2], '02x') for i in s)]]) 276 else: 277 self.putz(s[0][0], s[-1][1], [5, ['Data: %s' % ' '.join(format(i[2], '02x') for i in s)]]) 278 279 def put_crc(self, s): 280 self.puty(s, [6, ['CRC: {:02X} {:02X}'.format(s[0][2], s[1][2])]]) 281 282 def put_status(self, ss, es, status): 283 self.putz(ss, es, [7, ['Status: %s' % STATUS[status]]]) 284 285 def put_warning(self, ss, es, msg): 286 self.putz(ss, es, [8, ['Warning: %s' % msg]]) 287 288 def decode(self, ss, es, data): 289 cmd, databyte = data 290 # State machine. 291 if self.state == 'IDLE': 292 # Wait for an I²C START condition. 293 if cmd != 'START': 294 return 295 self.state = 'GET SLAVE ADDR' 296 self.ss_block = ss 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]) 306 elif cmd == 'STOP': 307 self.es_block = es 308 # Reset the opcode before received data, as this causes 309 # responses to be displayed incorrectly. 310 self.opcode = -1 311 if len(self.bytes) > 0: 312 self.output_rx_bytes() 313 self.waddr = -1 314 self.bytes = [] 315 self.state = 'IDLE' 316 elif self.state == 'WRITE REGS': 317 if cmd == 'DATA WRITE': 318 self.bytes.append([ss, es, databyte]) 319 elif cmd == 'STOP': 320 self.es_block = es 321 self.output_tx_bytes() 322 self.bytes = [] 323 self.state = 'IDLE' 324