| 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 <stdio.h> |
| 12 | #include <stdlib.h> |
| 13 | #include <string.h> |
| 14 | #include <unistd.h> |
| 15 | #include "cmdmain.h" |
| 16 | #include "cmdparser.h" |
| 17 | #include "cmdhftopaz.h" |
| 18 | #include "cmdhf14a.h" |
| 19 | #include "ui.h" |
| 20 | #include "mifare.h" |
| 21 | #include "proxmark3.h" |
| 22 | #include "iso14443crc.h" |
| 23 | #include "protocols.h" |
| 24 | #include "cmdhf.h" |
| 25 | |
| 26 | #define TOPAZ_MAX_MEMORY 2048 |
| 27 | |
| 28 | static struct { |
| 29 | uint8_t HR01[2]; |
| 30 | uint8_t uid[7]; |
| 31 | uint8_t size; |
| 32 | uint8_t data_blocks[TOPAZ_MAX_MEMORY/8][8]; |
| 33 | uint8_t *dynamic_lock_areas; |
| 34 | uint8_t *dynamic_reserved_areas; |
| 35 | } topaz_tag; |
| 36 | |
| 37 | static void topaz_switch_on_field(void) |
| 38 | { |
| 39 | UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_SELECT | ISO14A_NO_DISCONNECT | ISO14A_TOPAZMODE, 0, 0}}; |
| 40 | SendCommand(&c); |
| 41 | } |
| 42 | |
| 43 | |
| 44 | static void topaz_switch_off_field(void) |
| 45 | { |
| 46 | UsbCommand c = {CMD_READER_ISO_14443a, {0, 0, 0}}; |
| 47 | SendCommand(&c); |
| 48 | } |
| 49 | |
| 50 | |
| 51 | static int topaz_send_cmd_raw(uint8_t *cmd, uint8_t len, uint8_t *response) |
| 52 | { |
| 53 | UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_RAW | ISO14A_NO_DISCONNECT | ISO14A_TOPAZMODE, len, 0}}; |
| 54 | memcpy(c.d.asBytes, cmd, len); |
| 55 | SendCommand(&c); |
| 56 | |
| 57 | UsbCommand resp; |
| 58 | WaitForResponse(CMD_ACK, &resp); |
| 59 | |
| 60 | if (resp.arg[0] > 0) { |
| 61 | memcpy(response, resp.d.asBytes, resp.arg[0]); |
| 62 | } |
| 63 | |
| 64 | return resp.arg[0]; |
| 65 | } |
| 66 | |
| 67 | |
| 68 | static int topaz_send_cmd(uint8_t *cmd, uint8_t len, uint8_t *response) |
| 69 | { |
| 70 | if (len > 1) { |
| 71 | uint8_t first, second; |
| 72 | ComputeCrc14443(CRC_14443_B, cmd, len-2, &first, &second); |
| 73 | cmd[len-2] = first; |
| 74 | cmd[len-1] = second; |
| 75 | } |
| 76 | |
| 77 | return topaz_send_cmd_raw(cmd, len, response); |
| 78 | } |
| 79 | |
| 80 | |
| 81 | static int topaz_select(uint8_t *atqa, uint8_t *rid_response) |
| 82 | { |
| 83 | // ToDo: implement anticollision |
| 84 | |
| 85 | uint8_t wupa_cmd[] = {TOPAZ_WUPA}; |
| 86 | uint8_t rid_cmd[] = {TOPAZ_RID, 0, 0, 0, 0, 0, 0, 0, 0}; |
| 87 | |
| 88 | topaz_switch_on_field(); |
| 89 | |
| 90 | if (!topaz_send_cmd(wupa_cmd, sizeof(wupa_cmd), atqa)) { |
| 91 | topaz_switch_off_field(); |
| 92 | return -1; // WUPA failed |
| 93 | } |
| 94 | |
| 95 | if (!topaz_send_cmd(rid_cmd, sizeof(rid_cmd), rid_response)) { |
| 96 | topaz_switch_off_field(); |
| 97 | return -2; // RID failed |
| 98 | } |
| 99 | |
| 100 | return 0; // OK |
| 101 | } |
| 102 | |
| 103 | |
| 104 | static int topaz_rall(uint8_t *uid, uint8_t *response) |
| 105 | { |
| 106 | uint8_t rall_cmd[] = {TOPAZ_RALL, 0, 0, 0, 0, 0, 0, 0, 0}; |
| 107 | |
| 108 | memcpy(&rall_cmd[3], uid, 4); |
| 109 | if (!topaz_send_cmd(rall_cmd, sizeof(rall_cmd), response)) { |
| 110 | topaz_switch_off_field(); |
| 111 | return -1; // RALL failed |
| 112 | } |
| 113 | |
| 114 | return 0; |
| 115 | } |
| 116 | |
| 117 | |
| 118 | static bool topaz_block_is_locked(uint8_t blockno, uint8_t *lockbits) |
| 119 | { |
| 120 | if(lockbits[blockno/8] >> (blockno % 8) & 0x01) { |
| 121 | return true; |
| 122 | } else { |
| 123 | return false; |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | |
| 128 | static int topaz_print_CC(uint8_t *data) |
| 129 | { |
| 130 | if(data[0] != 0xe1) { |
| 131 | return -1; // no NDEF message |
| 132 | } |
| 133 | |
| 134 | PrintAndLog("Capability Container: %02x %02x %02x %02x", data[0], data[1], data[2], data[3]); |
| 135 | PrintAndLog(" %02x: NDEF Magic Number", data[0]); |
| 136 | PrintAndLog(" %02x: version %d.%d supported by tag", data[1], (data[1] & 0xF0) >> 4, data[1] & 0x0f); |
| 137 | PrintAndLog(" %02x: Physical Memory Size of this tag: %d bytes", data[2], (data[2] + 1) * 8); |
| 138 | PrintAndLog(" %02x: %s / %s", data[3], |
| 139 | (data[3] & 0xF0) ? "(RFU)" : "Read access granted without any security", |
| 140 | (data[3] & 0x0F)==0 ? "Write access granted without any security" : (data[3] & 0x0F)==0x0F ? "No write access granted at all" : "(RFU)"); |
| 141 | return 0; |
| 142 | } |
| 143 | |
| 144 | |
| 145 | static void get_TLV(uint8_t **TLV_ptr, uint8_t *tag, uint16_t *length, uint8_t **value) |
| 146 | { |
| 147 | *length = 0; |
| 148 | *value = NULL; |
| 149 | |
| 150 | *tag = **TLV_ptr; |
| 151 | *TLV_ptr += 1; |
| 152 | switch (*tag) { |
| 153 | case 0x00: // NULL TLV. |
| 154 | case 0xFE: // Terminator TLV. |
| 155 | break; |
| 156 | case 0x01: // Lock Control TLV |
| 157 | case 0x02: // Reserved Memory TLV |
| 158 | case 0x03: // NDEF message TLV |
| 159 | case 0xFD: // proprietary TLV |
| 160 | *length = **TLV_ptr; |
| 161 | *TLV_ptr += 1; |
| 162 | if (*length == 0xff) { |
| 163 | *length = **TLV_ptr << 8; |
| 164 | *TLV_ptr += 1; |
| 165 | *length |= **TLV_ptr; |
| 166 | *TLV_ptr += 1; |
| 167 | } |
| 168 | *value = *TLV_ptr; |
| 169 | *TLV_ptr += *length; |
| 170 | break; |
| 171 | default: // RFU |
| 172 | break; |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | |
| 177 | static bool topaz_print_lock_control_TLVs(uint8_t *memory) |
| 178 | { |
| 179 | uint8_t *TLV_ptr = memory; |
| 180 | uint8_t tag = 0; |
| 181 | uint16_t length; |
| 182 | uint8_t *value; |
| 183 | bool lock_TLV_present = false; |
| 184 | |
| 185 | while(*TLV_ptr != 0x03 && *TLV_ptr != 0xFD && *TLV_ptr != 0xFE) { |
| 186 | // all Lock Control TLVs shall be present before the NDEF message TLV, the proprietary TLV (and the Terminator TLV) |
| 187 | get_TLV(&TLV_ptr, &tag, &length, &value); |
| 188 | if (tag == 0x01) { // the Lock Control TLV |
| 189 | uint8_t pages_addr = value[0] >> 4; |
| 190 | uint8_t byte_offset = value[0] & 0x0f; |
| 191 | uint8_t size_in_bits = value[1] ? value[1] : 255; |
| 192 | uint8_t bytes_per_page = 1 << (value[2] & 0x0f); |
| 193 | uint8_t bytes_locked_per_bit = 1 << (value[2] >> 4); |
| 194 | PrintAndLog("Lock Area of %d bits at byte offset 0x%02x. Each Lock Bit locks %d bytes.", |
| 195 | size_in_bits, |
| 196 | pages_addr * bytes_per_page + byte_offset, |
| 197 | bytes_locked_per_bit); |
| 198 | lock_TLV_present = true; |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | if (!lock_TLV_present) { |
| 203 | PrintAndLog("(No Lock Control TLV present)"); |
| 204 | return -1; |
| 205 | } else { |
| 206 | return 0; |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | |
| 211 | static int topaz_print_reserved_memory_control_TLVs(uint8_t *memory) |
| 212 | { |
| 213 | uint8_t *TLV_ptr = memory; |
| 214 | uint8_t tag = 0; |
| 215 | uint16_t length; |
| 216 | uint8_t *value; |
| 217 | bool reserved_memory_control_TLV_present = false; |
| 218 | |
| 219 | while(*TLV_ptr != 0x03 && *TLV_ptr != 0xFD && *TLV_ptr != 0xFE) { |
| 220 | // all Reserved Memory Control TLVs shall be present before the NDEF message TLV, the proprietary TLV (and the Terminator TLV) |
| 221 | get_TLV(&TLV_ptr, &tag, &length, &value); |
| 222 | if (tag == 0x02) { // the Reserved Memory Control TLV |
| 223 | uint8_t pages_addr = value[0] >> 4; |
| 224 | uint8_t byte_offset = value[0] & 0x0f; |
| 225 | uint8_t size_in_bytes = value[1] ? value[1] : 255; |
| 226 | uint8_t bytes_per_page = 1 << (value[2] & 0x0f); |
| 227 | PrintAndLog("Reserved Memory of %d bytes at byte offset 0x%02x.", |
| 228 | size_in_bytes, |
| 229 | pages_addr * bytes_per_page + byte_offset); |
| 230 | reserved_memory_control_TLV_present = true; |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | if (!reserved_memory_control_TLV_present) { |
| 235 | PrintAndLog("(No Reserved Memory Control TLV present)"); |
| 236 | return -1; |
| 237 | } else { |
| 238 | return 0; |
| 239 | } |
| 240 | } |
| 241 | |
| 242 | |
| 243 | static void topaz_print_lifecycle_state(uint8_t *data) |
| 244 | { |
| 245 | |
| 246 | } |
| 247 | |
| 248 | |
| 249 | static void topaz_print_NDEF(uint8_t *data) |
| 250 | { |
| 251 | |
| 252 | } |
| 253 | |
| 254 | |
| 255 | int CmdHFTopazReader(const char *Cmd) { |
| 256 | int status; |
| 257 | uint8_t atqa[2]; |
| 258 | uint8_t rid_response[8]; |
| 259 | uint8_t *uid_echo = &rid_response[2]; |
| 260 | uint8_t rall_response[130]; |
| 261 | bool verbose = TRUE; |
| 262 | |
| 263 | char ctmp = param_getchar(Cmd, 0); |
| 264 | if ( ctmp == 'S' || ctmp == 's') verbose = FALSE; |
| 265 | |
| 266 | status = topaz_select(atqa, rid_response); |
| 267 | |
| 268 | if (status == -1) { |
| 269 | if (verbose) PrintAndLog("Error: couldn't receive ATQA"); |
| 270 | return -1; |
| 271 | } |
| 272 | |
| 273 | PrintAndLog("ATQA : %02x %02x", atqa[1], atqa[0]); |
| 274 | if (atqa[1] != 0x0c && atqa[0] != 0x00) { |
| 275 | PrintAndLog("Tag doesn't support the Topaz protocol."); |
| 276 | topaz_switch_off_field(); |
| 277 | return -1; |
| 278 | } |
| 279 | |
| 280 | if (status == -2) { |
| 281 | PrintAndLog("Error: tag didn't answer to RID"); |
| 282 | topaz_switch_off_field(); |
| 283 | return -1; |
| 284 | } |
| 285 | |
| 286 | topaz_tag.HR01[0] = rid_response[0]; |
| 287 | topaz_tag.HR01[1] = rid_response[1]; |
| 288 | |
| 289 | // ToDo: CRC check |
| 290 | PrintAndLog("HR0 : %02x (%sa Topaz tag (%scapable of carrying a NDEF message), %s memory map)", rid_response[0], |
| 291 | (rid_response[0] & 0xF0) == 0x10 ? "" : "not ", |
| 292 | (rid_response[0] & 0xF0) == 0x10 ? "" : "not ", |
| 293 | (rid_response[0] & 0x0F) == 0x01 ? "static" : "dynamic"); |
| 294 | PrintAndLog("HR1 : %02x", rid_response[1]); |
| 295 | |
| 296 | status = topaz_rall(uid_echo, rall_response); |
| 297 | |
| 298 | if(status == -1) { |
| 299 | PrintAndLog("Error: tag didn't answer to RALL"); |
| 300 | topaz_switch_off_field(); |
| 301 | return -1; |
| 302 | } |
| 303 | |
| 304 | memcpy(topaz_tag.uid, rall_response+2, 7); |
| 305 | PrintAndLog("UID : %02x %02x %02x %02x %02x %02x %02x", |
| 306 | topaz_tag.uid[6], |
| 307 | topaz_tag.uid[5], |
| 308 | topaz_tag.uid[4], |
| 309 | topaz_tag.uid[3], |
| 310 | topaz_tag.uid[2], |
| 311 | topaz_tag.uid[1], |
| 312 | topaz_tag.uid[0]); |
| 313 | |
| 314 | PrintAndLog(" UID[6] (Manufacturer Byte) = %02x, Manufacturer: %s", |
| 315 | topaz_tag.uid[6], |
| 316 | getTagInfo(topaz_tag.uid[6])); |
| 317 | |
| 318 | memcpy(topaz_tag.data_blocks, rall_response+2, 0x10*8); |
| 319 | PrintAndLog(""); |
| 320 | PrintAndLog("Static Data blocks 00 to 0c:"); |
| 321 | PrintAndLog("block# | offset | Data | Locked?"); |
| 322 | char line[80]; |
| 323 | for (uint16_t i = 0; i <= 0x0c; i++) { |
| 324 | for (uint16_t j = 0; j < 8; j++) { |
| 325 | sprintf(&line[3*j], "%02x ", topaz_tag.data_blocks[i][j] /*rall_response[2 + 8*i + j]*/); |
| 326 | } |
| 327 | PrintAndLog(" 0x%02x | 0x%02x | %s| %-3s", i, i*8, line, topaz_block_is_locked(i, &topaz_tag.data_blocks[0x0e][0]) ? "yes" : "no"); |
| 328 | } |
| 329 | |
| 330 | PrintAndLog(""); |
| 331 | PrintAndLog("Static Reserved block 0d:"); |
| 332 | for (uint16_t j = 0; j < 8; j++) { |
| 333 | sprintf(&line[3*j], "%02x ", topaz_tag.data_blocks[0x0d][j]); |
| 334 | } |
| 335 | PrintAndLog(" 0x%02x | 0x%02x | %s| %-3s", 0x0d, 0x0d*8, line, "n/a"); |
| 336 | |
| 337 | PrintAndLog(""); |
| 338 | PrintAndLog("Static Lockbits and OTP Bytes:"); |
| 339 | for (uint16_t j = 0; j < 8; j++) { |
| 340 | sprintf(&line[3*j], "%02x ", topaz_tag.data_blocks[0x0e][j]); |
| 341 | } |
| 342 | PrintAndLog(" 0x%02x | 0x%02x | %s| %-3s", 0x0e, 0x0e*8, line, "n/a"); |
| 343 | |
| 344 | PrintAndLog(""); |
| 345 | |
| 346 | status = topaz_print_CC(&topaz_tag.data_blocks[1][0]); |
| 347 | |
| 348 | if (status == -1) { |
| 349 | PrintAndLog("No NDEF message present"); |
| 350 | topaz_switch_off_field(); |
| 351 | return 0; |
| 352 | } |
| 353 | |
| 354 | PrintAndLog(""); |
| 355 | bool lock_TLV_present = topaz_print_lock_control_TLVs(&topaz_tag.data_blocks[1][4]); |
| 356 | if ( lock_TLV_present ) { |
| 357 | PrintAndLog(""); |
| 358 | } |
| 359 | |
| 360 | PrintAndLog(""); |
| 361 | bool reserved_mem_present = topaz_print_reserved_memory_control_TLVs(&topaz_tag.data_blocks[1][4]); |
| 362 | if (reserved_mem_present) { |
| 363 | PrintAndLog(""); |
| 364 | } |
| 365 | |
| 366 | topaz_print_lifecycle_state(&topaz_tag.data_blocks[1][0]); |
| 367 | |
| 368 | topaz_print_NDEF(&topaz_tag.data_blocks[1][0]); |
| 369 | |
| 370 | topaz_switch_off_field(); |
| 371 | return 0; |
| 372 | } |
| 373 | |
| 374 | int CmdHFTopazSim(const char *Cmd) { |
| 375 | PrintAndLog("not yet implemented"); |
| 376 | return 0; |
| 377 | } |
| 378 | |
| 379 | int CmdHFTopazCmdRaw(const char *Cmd) { |
| 380 | PrintAndLog("not yet implemented"); |
| 381 | return 0; |
| 382 | } |
| 383 | |
| 384 | int CmdHFTopazList(const char *Cmd) { |
| 385 | CmdHFList("topaz"); |
| 386 | return 0; |
| 387 | } |
| 388 | |
| 389 | static int CmdHelp(const char *Cmd); |
| 390 | |
| 391 | static command_t CommandTable[] = |
| 392 | { |
| 393 | {"help", CmdHelp, 1, "This help"}, |
| 394 | {"reader", CmdHFTopazReader, 0, "Act like a Topaz reader"}, |
| 395 | {"sim", CmdHFTopazSim, 0, "<UID> -- Simulate Topaz tag"}, |
| 396 | {"sniff", CmdHF14ASniff, 0, "Sniff Topaz reader-tag communication"}, |
| 397 | {"raw", CmdHFTopazCmdRaw, 0, "Send raw hex data to tag"}, |
| 398 | {"list", CmdHFTopazList, 0, "[Deprecated] List Topaz history"}, |
| 399 | {NULL, NULL, 0, NULL} |
| 400 | }; |
| 401 | |
| 402 | int CmdHFTopaz(const char *Cmd) { |
| 403 | // flush |
| 404 | //WaitForResponseTimeout(CMD_ACK,NULL,100); |
| 405 | clearCommandBuffer(); |
| 406 | |
| 407 | // parse |
| 408 | CmdsParse(CommandTable, Cmd); |
| 409 | return 0; |
| 410 | } |
| 411 | |
| 412 | static int CmdHelp(const char *Cmd) |
| 413 | { |
| 414 | CmdsHelp(CommandTable); |
| 415 | return 0; |
| 416 | } |
| 417 | |
| 418 | |