| 1 | //----------------------------------------------------------------------------- |
| 2 | // Copyright (C) 2015 Piwi |
| 3 | // |
| 4 | // This code is licensed to you under the terms of the GNU GPL, version 2 or, |
| 5 | // at your option, any later version. See the LICENSE.txt file for the text of |
| 6 | // the license. |
| 7 | //----------------------------------------------------------------------------- |
| 8 | // High frequency Topaz (NFC Type 1) commands |
| 9 | //----------------------------------------------------------------------------- |
| 10 | |
| 11 | #include "cmdhftopaz.h" |
| 12 | |
| 13 | #include <stdio.h> |
| 14 | #include <stdlib.h> |
| 15 | #include <string.h> |
| 16 | #include <unistd.h> |
| 17 | #include "cmdmain.h" |
| 18 | #include "cmdparser.h" |
| 19 | #include "cmdhf14a.h" |
| 20 | #include "ui.h" |
| 21 | #include "mifare.h" |
| 22 | #include "comms.h" |
| 23 | #include "iso14443crc.h" |
| 24 | #include "protocols.h" |
| 25 | #include "taginfo.h" |
| 26 | |
| 27 | #define TOPAZ_STATIC_MEMORY (0x0f * 8) // 15 blocks with 8 Bytes each |
| 28 | |
| 29 | // a struct to describe a memory area which contains lock bits and the corresponding lockable memory area |
| 30 | typedef struct dynamic_lock_area { |
| 31 | struct dynamic_lock_area *next; |
| 32 | uint16_t byte_offset; // the address of the lock bits |
| 33 | uint16_t size_in_bits; |
| 34 | uint16_t first_locked_byte; // the address of the lockable area |
| 35 | uint16_t bytes_locked_per_bit; |
| 36 | } dynamic_lock_area_t; |
| 37 | |
| 38 | |
| 39 | static struct { |
| 40 | uint8_t HR01[2]; |
| 41 | uint8_t uid[7]; |
| 42 | uint16_t size; |
| 43 | uint8_t data_blocks[TOPAZ_STATIC_MEMORY/8][8]; // this memory is always there |
| 44 | uint8_t *dynamic_memory; // this memory can be there |
| 45 | dynamic_lock_area_t *dynamic_lock_areas; // lock area descriptors |
| 46 | } topaz_tag; |
| 47 | |
| 48 | |
| 49 | static void topaz_switch_on_field(void) |
| 50 | { |
| 51 | UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_SELECT | ISO14A_NO_DISCONNECT | ISO14A_TOPAZMODE, 0, 0}}; |
| 52 | SendCommand(&c); |
| 53 | } |
| 54 | |
| 55 | |
| 56 | static void topaz_switch_off_field(void) |
| 57 | { |
| 58 | UsbCommand c = {CMD_READER_ISO_14443a, {0, 0, 0}}; |
| 59 | SendCommand(&c); |
| 60 | } |
| 61 | |
| 62 | |
| 63 | // send a raw topaz command, returns the length of the response (0 in case of error) |
| 64 | static int topaz_send_cmd_raw(uint8_t *cmd, uint8_t len, uint8_t *response) |
| 65 | { |
| 66 | UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_RAW | ISO14A_NO_DISCONNECT | ISO14A_TOPAZMODE, len, 0}}; |
| 67 | memcpy(c.d.asBytes, cmd, len); |
| 68 | SendCommand(&c); |
| 69 | |
| 70 | UsbCommand resp; |
| 71 | WaitForResponse(CMD_ACK, &resp); |
| 72 | |
| 73 | if (resp.arg[0] > 0) { |
| 74 | memcpy(response, resp.d.asBytes, resp.arg[0]); |
| 75 | } |
| 76 | |
| 77 | return resp.arg[0]; |
| 78 | } |
| 79 | |
| 80 | |
| 81 | // calculate CRC bytes and send topaz command, returns the length of the response (0 in case of error) |
| 82 | static int topaz_send_cmd(uint8_t *cmd, uint8_t len, uint8_t *response) |
| 83 | { |
| 84 | if (len > 1) { |
| 85 | uint8_t first, second; |
| 86 | ComputeCrc14443(CRC_14443_B, cmd, len-2, &first, &second); |
| 87 | cmd[len-2] = first; |
| 88 | cmd[len-1] = second; |
| 89 | } |
| 90 | |
| 91 | return topaz_send_cmd_raw(cmd, len, response); |
| 92 | } |
| 93 | |
| 94 | |
| 95 | // select a topaz tag. Send WUPA and RID. |
| 96 | static int topaz_select(uint8_t *atqa, uint8_t *rid_response) |
| 97 | { |
| 98 | // ToDo: implement anticollision |
| 99 | |
| 100 | uint8_t wupa_cmd[] = {TOPAZ_WUPA}; |
| 101 | uint8_t rid_cmd[] = {TOPAZ_RID, 0, 0, 0, 0, 0, 0, 0, 0}; |
| 102 | |
| 103 | topaz_switch_on_field(); |
| 104 | |
| 105 | if (!topaz_send_cmd(wupa_cmd, sizeof(wupa_cmd), atqa)) { |
| 106 | topaz_switch_off_field(); |
| 107 | return -1; // WUPA failed |
| 108 | } |
| 109 | |
| 110 | if (!topaz_send_cmd(rid_cmd, sizeof(rid_cmd), rid_response)) { |
| 111 | topaz_switch_off_field(); |
| 112 | return -2; // RID failed |
| 113 | } |
| 114 | |
| 115 | return 0; // OK |
| 116 | } |
| 117 | |
| 118 | |
| 119 | // read all of the static memory of a selected Topaz tag. |
| 120 | static int topaz_rall(uint8_t *uid, uint8_t *response) |
| 121 | { |
| 122 | uint8_t rall_cmd[] = {TOPAZ_RALL, 0, 0, 0, 0, 0, 0, 0, 0}; |
| 123 | |
| 124 | memcpy(&rall_cmd[3], uid, 4); |
| 125 | if (!topaz_send_cmd(rall_cmd, sizeof(rall_cmd), response)) { |
| 126 | topaz_switch_off_field(); |
| 127 | return -1; // RALL failed |
| 128 | } |
| 129 | |
| 130 | return 0; |
| 131 | } |
| 132 | |
| 133 | |
| 134 | // read a block (8 Bytes) of a selected Topaz tag. |
| 135 | static int topaz_read_block(uint8_t *uid, uint8_t blockno, uint8_t *block_data) |
| 136 | { |
| 137 | uint8_t read8_cmd[] = {TOPAZ_READ8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| 138 | uint8_t read8_response[11]; |
| 139 | |
| 140 | read8_cmd[1] = blockno; |
| 141 | memcpy(&read8_cmd[10], uid, 4); |
| 142 | if (!topaz_send_cmd(read8_cmd, sizeof(read8_cmd), read8_response)) { |
| 143 | topaz_switch_off_field(); |
| 144 | return -1; // READ8 failed |
| 145 | } |
| 146 | |
| 147 | memcpy(block_data, &read8_response[1], 8); |
| 148 | |
| 149 | return 0; |
| 150 | } |
| 151 | |
| 152 | |
| 153 | // read a segment (16 blocks = 128 Bytes) of a selected Topaz tag. Works only for tags with dynamic memory. |
| 154 | static int topaz_read_segment(uint8_t *uid, uint8_t segno, uint8_t *segment_data) |
| 155 | { |
| 156 | uint8_t rseg_cmd[] = {TOPAZ_RSEG, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| 157 | uint8_t rseg_response[131]; |
| 158 | |
| 159 | rseg_cmd[1] = segno << 4; |
| 160 | memcpy(&rseg_cmd[10], uid, 4); |
| 161 | if (!topaz_send_cmd(rseg_cmd, sizeof(rseg_cmd), rseg_response)) { |
| 162 | topaz_switch_off_field(); |
| 163 | return -1; // RSEG failed |
| 164 | } |
| 165 | |
| 166 | memcpy(segment_data, &rseg_response[1], 128); |
| 167 | |
| 168 | return 0; |
| 169 | } |
| 170 | |
| 171 | |
| 172 | // search for the lock area descriptor for the lockable area including byteno |
| 173 | static dynamic_lock_area_t *get_dynamic_lock_area(uint16_t byteno) |
| 174 | { |
| 175 | dynamic_lock_area_t *lock_area; |
| 176 | |
| 177 | lock_area = topaz_tag.dynamic_lock_areas; |
| 178 | |
| 179 | while (lock_area != NULL) { |
| 180 | if (byteno < lock_area->first_locked_byte) { |
| 181 | lock_area = lock_area->next; |
| 182 | } else { |
| 183 | return lock_area; |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | return NULL; |
| 188 | } |
| 189 | |
| 190 | |
| 191 | // check if a memory byte is locked. |
| 192 | static bool topaz_byte_is_locked(uint16_t byteno) |
| 193 | { |
| 194 | uint8_t *lockbits; |
| 195 | uint16_t locked_bytes_per_bit; |
| 196 | dynamic_lock_area_t *lock_area; |
| 197 | |
| 198 | if (byteno < TOPAZ_STATIC_MEMORY) { |
| 199 | lockbits = &topaz_tag.data_blocks[0x0e][0]; |
| 200 | locked_bytes_per_bit = 8; |
| 201 | } else { |
| 202 | lock_area = get_dynamic_lock_area(byteno); |
| 203 | if (lock_area == NULL) { |
| 204 | return false; |
| 205 | } else { |
| 206 | lockbits = &topaz_tag.dynamic_memory[lock_area->byte_offset - TOPAZ_STATIC_MEMORY]; |
| 207 | locked_bytes_per_bit = lock_area->bytes_locked_per_bit; |
| 208 | byteno = byteno - lock_area->first_locked_byte; |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | uint16_t blockno = byteno / locked_bytes_per_bit; |
| 213 | if(lockbits[blockno/8] & (0x01 << (blockno % 8))) { |
| 214 | return true; |
| 215 | } else { |
| 216 | return false; |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | |
| 221 | // read and print the Capability Container |
| 222 | static int topaz_print_CC(uint8_t *data) |
| 223 | { |
| 224 | if(data[0] != 0xe1) { |
| 225 | topaz_tag.size = TOPAZ_STATIC_MEMORY; |
| 226 | return -1; // no NDEF message |
| 227 | } |
| 228 | |
| 229 | PrintAndLog("Capability Container: %02x %02x %02x %02x", data[0], data[1], data[2], data[3]); |
| 230 | PrintAndLog(" %02x: NDEF Magic Number", data[0]); |
| 231 | PrintAndLog(" %02x: version %d.%d supported by tag", data[1], (data[1] & 0xF0) >> 4, data[1] & 0x0f); |
| 232 | uint16_t memsize = (data[2] + 1) * 8; |
| 233 | topaz_tag.size = memsize; |
| 234 | topaz_tag.dynamic_memory = malloc(memsize - TOPAZ_STATIC_MEMORY); |
| 235 | PrintAndLog(" %02x: Physical Memory Size of this tag: %d bytes", data[2], memsize); |
| 236 | PrintAndLog(" %02x: %s / %s", data[3], |
| 237 | (data[3] & 0xF0) ? "(RFU)" : "Read access granted without any security", |
| 238 | (data[3] & 0x0F)==0 ? "Write access granted without any security" : (data[3] & 0x0F)==0x0F ? "No write access granted at all" : "(RFU)"); |
| 239 | return 0; |
| 240 | } |
| 241 | |
| 242 | |
| 243 | // return type, length and value of a TLV, starting at memory position *TLV_ptr |
| 244 | static void get_TLV(uint8_t **TLV_ptr, uint8_t *TLV_type, uint16_t *TLV_length, uint8_t **TLV_value) |
| 245 | { |
| 246 | *TLV_length = 0; |
| 247 | *TLV_value = NULL; |
| 248 | |
| 249 | *TLV_type = **TLV_ptr; |
| 250 | *TLV_ptr += 1; |
| 251 | switch (*TLV_type) { |
| 252 | case 0x00: // NULL TLV. |
| 253 | case 0xFE: // Terminator TLV. |
| 254 | break; |
| 255 | case 0x01: // Lock Control TLV |
| 256 | case 0x02: // Reserved Memory TLV |
| 257 | case 0x03: // NDEF message TLV |
| 258 | case 0xFD: // proprietary TLV |
| 259 | *TLV_length = **TLV_ptr; |
| 260 | *TLV_ptr += 1; |
| 261 | if (*TLV_length == 0xff) { |
| 262 | *TLV_length = **TLV_ptr << 8; |
| 263 | *TLV_ptr += 1; |
| 264 | *TLV_length |= **TLV_ptr; |
| 265 | *TLV_ptr += 1; |
| 266 | } |
| 267 | *TLV_value = *TLV_ptr; |
| 268 | *TLV_ptr += *TLV_length; |
| 269 | break; |
| 270 | default: // RFU |
| 271 | break; |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | |
| 276 | // lock area TLVs contain no information on the start of the respective lockable area. Lockable areas |
| 277 | // do not include the lock bits and reserved memory. We therefore need to adjust the start of the |
| 278 | // respective lockable areas accordingly |
| 279 | static void adjust_lock_areas(uint16_t block_start, uint16_t block_size) |
| 280 | { |
| 281 | dynamic_lock_area_t *lock_area = topaz_tag.dynamic_lock_areas; |
| 282 | while (lock_area != NULL) { |
| 283 | if (lock_area->first_locked_byte <= block_start) { |
| 284 | lock_area->first_locked_byte += block_size; |
| 285 | } |
| 286 | lock_area = lock_area->next; |
| 287 | } |
| 288 | } |
| 289 | |
| 290 | |
| 291 | // read and print the lock area and reserved memory TLVs |
| 292 | static void topaz_print_control_TLVs(uint8_t *memory) |
| 293 | { |
| 294 | uint8_t *TLV_ptr = memory; |
| 295 | uint8_t TLV_type = 0; |
| 296 | uint16_t TLV_length; |
| 297 | uint8_t *TLV_value; |
| 298 | bool lock_TLV_present = false; |
| 299 | bool reserved_memory_control_TLV_present = false; |
| 300 | uint16_t next_lockable_byte = 0x0f * 8; // first byte after static memory area |
| 301 | |
| 302 | while(*TLV_ptr != 0x03 && *TLV_ptr != 0xFD && *TLV_ptr != 0xFE) { |
| 303 | // all Lock Control TLVs shall be present before the NDEF message TLV, the proprietary TLV (and the Terminator TLV) |
| 304 | get_TLV(&TLV_ptr, &TLV_type, &TLV_length, &TLV_value); |
| 305 | if (TLV_type == 0x01) { // a Lock Control TLV |
| 306 | uint8_t pages_addr = TLV_value[0] >> 4; |
| 307 | uint8_t byte_offset = TLV_value[0] & 0x0f; |
| 308 | uint16_t size_in_bits = TLV_value[1] ? TLV_value[1] : 256; |
| 309 | uint16_t size_in_bytes = (size_in_bits + 7)/8; |
| 310 | uint16_t bytes_per_page = 1 << (TLV_value[2] & 0x0f); |
| 311 | uint16_t bytes_locked_per_bit = 1 << (TLV_value[2] >> 4); |
| 312 | uint16_t area_start = pages_addr * bytes_per_page + byte_offset; |
| 313 | PrintAndLog("Lock Area of %d bits at byte offset 0x%04x. Each Lock Bit locks %d bytes.", |
| 314 | size_in_bits, |
| 315 | area_start, |
| 316 | bytes_locked_per_bit); |
| 317 | lock_TLV_present = true; |
| 318 | dynamic_lock_area_t *old = topaz_tag.dynamic_lock_areas; |
| 319 | dynamic_lock_area_t *new = topaz_tag.dynamic_lock_areas; |
| 320 | if (old == NULL) { |
| 321 | new = topaz_tag.dynamic_lock_areas = (dynamic_lock_area_t *)malloc(sizeof(dynamic_lock_area_t)); |
| 322 | } else { |
| 323 | while(old->next != NULL) { |
| 324 | old = old->next; |
| 325 | } |
| 326 | new = old->next = (dynamic_lock_area_t *)malloc(sizeof(dynamic_lock_area_t)); |
| 327 | } |
| 328 | new->next = NULL; |
| 329 | if (area_start <= next_lockable_byte) { |
| 330 | // lock areas are not lockable |
| 331 | next_lockable_byte += size_in_bytes; |
| 332 | } |
| 333 | new->first_locked_byte = next_lockable_byte; |
| 334 | new->byte_offset = area_start; |
| 335 | new->size_in_bits = size_in_bits; |
| 336 | new->bytes_locked_per_bit = bytes_locked_per_bit; |
| 337 | next_lockable_byte += size_in_bits * bytes_locked_per_bit; |
| 338 | } |
| 339 | if (TLV_type == 0x02) { // a Reserved Memory Control TLV |
| 340 | uint8_t pages_addr = TLV_value[0] >> 4; |
| 341 | uint8_t byte_offset = TLV_value[0] & 0x0f; |
| 342 | uint16_t size_in_bytes = TLV_value[1] ? TLV_value[1] : 256; |
| 343 | uint8_t bytes_per_page = 1 << (TLV_value[2] & 0x0f); |
| 344 | uint16_t area_start = pages_addr * bytes_per_page + byte_offset; |
| 345 | PrintAndLog("Reserved Memory of %d bytes at byte offset 0x%02x.", |
| 346 | size_in_bytes, |
| 347 | area_start); |
| 348 | reserved_memory_control_TLV_present = true; |
| 349 | adjust_lock_areas(area_start, size_in_bytes); // reserved memory areas are not lockable |
| 350 | if (area_start <= next_lockable_byte) { |
| 351 | next_lockable_byte += size_in_bytes; |
| 352 | } |
| 353 | } |
| 354 | } |
| 355 | |
| 356 | if (!lock_TLV_present) { |
| 357 | PrintAndLog("(No Lock Control TLV present)"); |
| 358 | } |
| 359 | |
| 360 | if (!reserved_memory_control_TLV_present) { |
| 361 | PrintAndLog("(No Reserved Memory Control TLV present)"); |
| 362 | } |
| 363 | } |
| 364 | |
| 365 | |
| 366 | // read all of the dynamic memory |
| 367 | static int topaz_read_dynamic_data(void) |
| 368 | { |
| 369 | // first read the remaining block of segment 0 |
| 370 | if(topaz_read_block(topaz_tag.uid, 0x0f, &topaz_tag.dynamic_memory[0]) == -1) { |
| 371 | PrintAndLog("Error while reading dynamic memory block %02x. Aborting...", 0x0f); |
| 372 | return -1; |
| 373 | } |
| 374 | |
| 375 | // read the remaining segments |
| 376 | uint8_t max_segment = topaz_tag.size / 128 - 1; |
| 377 | for(uint8_t segment = 1; segment <= max_segment; segment++) { |
| 378 | if(topaz_read_segment(topaz_tag.uid, segment, &topaz_tag.dynamic_memory[(segment-1)*128+8]) == -1) { |
| 379 | PrintAndLog("Error while reading dynamic memory block %02x. Aborting...", 0x0f); |
| 380 | return -1; |
| 381 | } |
| 382 | } |
| 383 | |
| 384 | return 0; |
| 385 | } |
| 386 | |
| 387 | |
| 388 | // read and print the dynamic memory |
| 389 | static void topaz_print_dynamic_data(void) |
| 390 | { |
| 391 | if (topaz_tag.size > TOPAZ_STATIC_MEMORY) { |
| 392 | PrintAndLog("Dynamic Data blocks:"); |
| 393 | if (topaz_read_dynamic_data() == 0) { |
| 394 | PrintAndLog("block# | offset | Data | Locked(y/n)"); |
| 395 | char line[80]; |
| 396 | for (uint16_t blockno = 0x0f; blockno < topaz_tag.size/8; blockno++) { |
| 397 | uint8_t *block_data = &topaz_tag.dynamic_memory[(blockno-0x0f)*8]; |
| 398 | char lockbits[9]; |
| 399 | for (uint16_t j = 0; j < 8; j++) { |
| 400 | sprintf(&line[3*j], "%02x ", block_data[j]); |
| 401 | lockbits[j] = topaz_byte_is_locked(blockno*8+j) ? 'y' : 'n'; |
| 402 | } |
| 403 | lockbits[8] = '\0'; |
| 404 | PrintAndLog(" 0x%02x | 0x%04x | %s| %-3s", blockno, blockno*8, line, lockbits); |
| 405 | } |
| 406 | } |
| 407 | } |
| 408 | } |
| 409 | |
| 410 | |
| 411 | static void topaz_print_lifecycle_state(uint8_t *data) |
| 412 | { |
| 413 | // to be done |
| 414 | } |
| 415 | |
| 416 | |
| 417 | static void topaz_print_NDEF(uint8_t *data) |
| 418 | { |
| 419 | // to be done. |
| 420 | } |
| 421 | |
| 422 | |
| 423 | // read a Topaz tag and print some usefull information |
| 424 | int CmdHFTopazReader(const char *Cmd) |
| 425 | { |
| 426 | int status; |
| 427 | uint8_t atqa[2]; |
| 428 | uint8_t rid_response[8]; |
| 429 | uint8_t *uid_echo = &rid_response[2]; |
| 430 | uint8_t rall_response[124]; |
| 431 | |
| 432 | status = topaz_select(atqa, rid_response); |
| 433 | |
| 434 | if (status == -1) { |
| 435 | PrintAndLog("Error: couldn't receive ATQA"); |
| 436 | return -1; |
| 437 | } |
| 438 | |
| 439 | PrintAndLog("ATQA : %02x %02x", atqa[1], atqa[0]); |
| 440 | if (atqa[1] != 0x0c && atqa[0] != 0x00) { |
| 441 | PrintAndLog("Tag doesn't support the Topaz protocol."); |
| 442 | topaz_switch_off_field(); |
| 443 | return -1; |
| 444 | } |
| 445 | |
| 446 | if (status == -2) { |
| 447 | PrintAndLog("Error: tag didn't answer to RID"); |
| 448 | topaz_switch_off_field(); |
| 449 | return -1; |
| 450 | } |
| 451 | |
| 452 | topaz_tag.HR01[0] = rid_response[0]; |
| 453 | topaz_tag.HR01[1] = rid_response[1]; |
| 454 | |
| 455 | // ToDo: CRC check |
| 456 | PrintAndLog("HR0 : %02x (%sa Topaz tag (%scapable of carrying a NDEF message), %s memory map)", rid_response[0], |
| 457 | (rid_response[0] & 0xF0) == 0x10 ? "" : "not ", |
| 458 | (rid_response[0] & 0xF0) == 0x10 ? "" : "not ", |
| 459 | (rid_response[0] & 0x0F) == 0x01 ? "static" : "dynamic"); |
| 460 | PrintAndLog("HR1 : %02x", rid_response[1]); |
| 461 | |
| 462 | status = topaz_rall(uid_echo, rall_response); |
| 463 | |
| 464 | if(status == -1) { |
| 465 | PrintAndLog("Error: tag didn't answer to RALL"); |
| 466 | topaz_switch_off_field(); |
| 467 | return -1; |
| 468 | } |
| 469 | |
| 470 | memcpy(topaz_tag.uid, rall_response+2, 7); |
| 471 | PrintAndLog("UID : %02x %02x %02x %02x %02x %02x %02x", |
| 472 | topaz_tag.uid[6], |
| 473 | topaz_tag.uid[5], |
| 474 | topaz_tag.uid[4], |
| 475 | topaz_tag.uid[3], |
| 476 | topaz_tag.uid[2], |
| 477 | topaz_tag.uid[1], |
| 478 | topaz_tag.uid[0]); |
| 479 | PrintAndLog(" UID[6] (Manufacturer Byte) = %02x, Manufacturer: %s", |
| 480 | topaz_tag.uid[6], |
| 481 | getManufacturerName(topaz_tag.uid[6])); |
| 482 | |
| 483 | memcpy(topaz_tag.data_blocks, rall_response+2, 0x0f*8); |
| 484 | PrintAndLog(""); |
| 485 | PrintAndLog("Static Data blocks 00 to 0c:"); |
| 486 | PrintAndLog("block# | offset | Data | Locked(y/n)"); |
| 487 | char line[80]; |
| 488 | for (uint16_t i = 0; i <= 0x0c; i++) { |
| 489 | char lockbits[9]; |
| 490 | for (uint16_t j = 0; j < 8; j++) { |
| 491 | sprintf(&line[3*j], "%02x ", topaz_tag.data_blocks[i][j] /*rall_response[2 + 8*i + j]*/); |
| 492 | lockbits[j] = topaz_byte_is_locked(i*8+j) ? 'y' : 'n'; |
| 493 | } |
| 494 | lockbits[8] = '\0'; |
| 495 | PrintAndLog(" 0x%02x | 0x%04x | %s| %-3s", i, i*8, line, lockbits); |
| 496 | } |
| 497 | |
| 498 | PrintAndLog(""); |
| 499 | PrintAndLog("Static Reserved block 0d:"); |
| 500 | for (uint16_t j = 0; j < 8; j++) { |
| 501 | sprintf(&line[3*j], "%02x ", topaz_tag.data_blocks[0x0d][j]); |
| 502 | } |
| 503 | PrintAndLog(" 0x%02x | 0x%04x | %s| %-3s", 0x0d, 0x0d*8, line, "n/a"); |
| 504 | |
| 505 | PrintAndLog(""); |
| 506 | PrintAndLog("Static Lockbits and OTP Bytes:"); |
| 507 | for (uint16_t j = 0; j < 8; j++) { |
| 508 | sprintf(&line[3*j], "%02x ", topaz_tag.data_blocks[0x0e][j]); |
| 509 | } |
| 510 | PrintAndLog(" 0x%02x | 0x%04x | %s| %-3s", 0x0e, 0x0e*8, line, "n/a"); |
| 511 | |
| 512 | PrintAndLog(""); |
| 513 | |
| 514 | status = topaz_print_CC(&topaz_tag.data_blocks[1][0]); |
| 515 | |
| 516 | if (status == -1) { |
| 517 | PrintAndLog("No NDEF message data present"); |
| 518 | topaz_switch_off_field(); |
| 519 | return 0; |
| 520 | } |
| 521 | |
| 522 | PrintAndLog(""); |
| 523 | topaz_print_control_TLVs(&topaz_tag.data_blocks[1][4]); |
| 524 | |
| 525 | PrintAndLog(""); |
| 526 | topaz_print_dynamic_data(); |
| 527 | |
| 528 | topaz_print_lifecycle_state(&topaz_tag.data_blocks[1][0]); |
| 529 | |
| 530 | topaz_print_NDEF(&topaz_tag.data_blocks[1][0]); |
| 531 | |
| 532 | topaz_switch_off_field(); |
| 533 | return 0; |
| 534 | } |
| 535 | |
| 536 | |
| 537 | int CmdHFTopazCmdRaw(const char *Cmd) |
| 538 | { |
| 539 | PrintAndLog("not yet implemented. Use hf 14 raw with option -T."); |
| 540 | return 0; |
| 541 | } |
| 542 | |
| 543 | |
| 544 | static int CmdHelp(const char *Cmd); |
| 545 | |
| 546 | |
| 547 | static command_t CommandTable[] = |
| 548 | { |
| 549 | {"help", CmdHelp, 1, "This help"}, |
| 550 | {"reader", CmdHFTopazReader, 0, "Act like a Topaz reader"}, |
| 551 | {"snoop", CmdHF14ASnoop, 0, "Eavesdrop a Topaz reader-tag communication"}, |
| 552 | {"raw", CmdHFTopazCmdRaw, 0, "Send raw hex data to tag"}, |
| 553 | {NULL, NULL, 0, NULL} |
| 554 | }; |
| 555 | |
| 556 | |
| 557 | int CmdHFTopaz(const char *Cmd) { |
| 558 | (void)WaitForResponseTimeout(CMD_ACK,NULL,100); |
| 559 | CmdsParse(CommandTable, Cmd); |
| 560 | return 0; |
| 561 | } |
| 562 | |
| 563 | |
| 564 | static int CmdHelp(const char *Cmd) |
| 565 | { |
| 566 | CmdsHelp(CommandTable); |
| 567 | return 0; |
| 568 | } |
| 569 | |
| 570 | |