diff --git a/CHANGELOG.md b/CHANGELOG.md index d1036dd33..16c7021e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Added `lf em 410x clone --htu` clone EM410x ID to Hitag µ/8265 (@douniwan5788) +- Added `lf hitag htu` support for Hitag µ/8265 (@douniwan5788) - Added `hf mfu aesauth` based on existing UL AES support (@doegox) - Changed `hf mfu sim` deny OTP changes with all zeros (@iceman1001) - Added missing file in CMakeLists.txt (@iceman1001) diff --git a/armsrc/Makefile b/armsrc/Makefile index 6ff1455de..9929b3ae3 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -72,7 +72,7 @@ else endif ifneq (,$(findstring WITH_HITAG,$(APP_CFLAGS))) - SRC_HITAG = hitag2_crypto.c hitag_common.c hitag2.c hitagS.c hitag2_crack.c + SRC_HITAG = hitag2_crypto.c hitag_common.c hitag2.c hitagS.c hitagu.c hitag2_crack.c APP_CFLAGS += -I../common/hitag2 else SRC_HITAG = diff --git a/armsrc/appmain.c b/armsrc/appmain.c index e4e903a45..7907cc071 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -41,6 +41,7 @@ #include "hitag2.h" #include "hitag2_crack.h" #include "hitagS.h" +#include "hitagu.h" #include "em4x50.h" #include "em4x70.h" #include "iclass.h" @@ -1232,6 +1233,25 @@ static void PacketReceived(PacketCommandNG *packet) { memcpy(mem, payload->data, payload->len); break; } + + case CMD_LF_HITAGU_READ: { + lf_hitag_data_t *payload = (lf_hitag_data_t *)packet->data.asBytes; + htu_read(payload, true); + break; + } + case CMD_LF_HITAGU_WRITE: { + lf_hitag_data_t *payload = (lf_hitag_data_t *)packet->data.asBytes; + htu_write_page(payload, true); + break; + } + case CMD_LF_HITAGU_SIMULATE: { + htu_simulate((bool)packet->oldarg[0], packet->oldarg[1], packet->data.asBytes, true); + break; + } + case CMD_LF_HITAGU_UID: { + htu_read_uid(NULL, true, true); + break; + } #endif #ifdef WITH_EM4x50 diff --git a/armsrc/felica.c b/armsrc/felica.c index 084ca6eee..b7f8b01c0 100644 --- a/armsrc/felica.c +++ b/armsrc/felica.c @@ -241,8 +241,8 @@ static uint8_t felica_select_card(felica_card_select_t *card) { // We try 10 times, or if answer was received. int len = 25; do { - // end-of-reception response packet data, wait approx. 501μs - // end-of-transmission command packet data, wait approx. 197μs + // end-of-reception response packet data, wait approx. 501µs + // end-of-transmission command packet data, wait approx. 197µs // polling card TransmitFor18092_AsReader(poll, sizeof(poll), NULL, 1, 0); diff --git a/armsrc/hitagu.c b/armsrc/hitagu.c new file mode 100644 index 000000000..d23dba6af --- /dev/null +++ b/armsrc/hitagu.c @@ -0,0 +1,897 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Low frequency HITAG µ (micro) functions + +#include "hitagu.h" +#include "hitag_common.h" + +#include "BigBuf.h" +#include "appmain.h" // tearoff_hook() +#include "cmd.h" +#include "commonutil.h" +#include "crc16.h" +#include "dbprint.h" +#include "fpgaloader.h" +#include "hitag2/hitag2_crypto.h" +#include "lfadc.h" +#include "protocols.h" +#include "proxmark3_arm.h" +#include "string.h" +#include "ticks.h" +#include "util.h" + +// Hitag µ specific definitions +#define HTU_SOF_BITS 4 // Start of frame bits is always 3 for Hitag µ (110) plus 1 bit error flag + +MOD M = MC4K; // Modulation type + +// Structure to hold the state of the Hitag µ tag +static struct hitagU_tag tag = { + .pstate = HT_READY, // Initial state is ready + .max_page = HITAGU_MAX_PAGE_STANDARD, // Default to standard version + .icr = 0, // Default ICR value +}; + +// Macros for managing authentication state +#define IS_AUTHENTICATED() (tag.pstate == HT_AUTHENTICATE) +#define SET_AUTHENTICATED() (tag.pstate = HT_AUTHENTICATE) +#define RESET_AUTHENTICATION() (tag.pstate = HT_READY) + +/* + * Update the maximum page number based on the tag's ICR (IC Revision) + */ +static void update_tag_max_page_by_icr(void) { + // Set max_page based on ICR value + switch (tag.icr) { + case HITAGU_ICR_STANDARD: + tag.max_page = HITAGU_MAX_PAGE_STANDARD; + DBG Dbprintf("Detected standard Hitag µ (ICR=0x%02X), max page: 0x%02X", tag.icr, tag.max_page); + break; + case HITAGU_ICR_ADVANCED: + tag.max_page = HITAGU_MAX_PAGE_ADVANCED; + DBG Dbprintf("Detected Hitag µ advanced (ICR=0x%02X), max page: 0x%02X", tag.icr, tag.max_page); + break; + case HITAGU_ICR_ADVANCED_PLUS: + tag.max_page = HITAGU_MAX_PAGE_ADVANCED_PLUS; + DBG Dbprintf("Detected Hitag µ advanced+ (ICR=0x%02X), max page: 0x%02X", tag.icr, tag.max_page); + break; + case HITAGU_ICR_8265: + tag.max_page = HITAGU_MAX_PAGE_8265; + DBG Dbprintf("Detected Hitag µ 8265 (ICR=0x%02X), max page: 0x%02X", tag.icr, tag.max_page); + break; + default: + // Unknown ICR, use standard size as fallback + tag.max_page = HITAGU_MAX_PAGE_STANDARD; + DBG Dbprintf("Unknown Hitag µ ICR: 0x%02X, defaulting to max page: 0x%02X", tag.icr, tag.max_page); + break; + } +} + +/* + * Update the maximum page number based on the tag's memory configuration + * This function checks both ICR and additional pattern-based detection + */ +static void update_tag_max_page(void) { + // First try to determine max_page from ICR + update_tag_max_page_by_icr(); + + // Additional tag type detection can be added here +} + +/* + * Handles all commands from a reader for Hitag µ + * Processes flags and commands, generates appropriate responses + */ +static void htu_handle_reader_command(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen) { + // Initialize response + *txlen = 0; + + if (rxlen < 5) { + return; // Command too short + } + + // Extract flags (5 bits) and command (6 bits if present) + uint8_t flags = (rx[0] >> 3) & 0x1F; + uint8_t command = 0; + + if (rxlen >= 11) { + // Extract 6-bit command if present + command = ((rx[0] & 0x07) << 3) | ((rx[1] >> 5) & 0x07); + } + + // Check flags + bool inv_flag = (flags & HITAGU_FLAG_INV); + bool crct_flag = (flags & HITAGU_FLAG_CRCT); + + // Handle based on flags and command + if (inv_flag) { + // Inventory mode - respond with UID (48 bits) + *txlen = concatbits(tx, *txlen, tag.uid, 0, HITAGU_UID_SIZE * 8, true); + } else if (command == HITAGU_CMD_LOGIN) { + // Login command + if (rxlen >= 43) { // 5+6+32 bits = 43 bits minimum + // Extract password - 32 bits after command + uint32_t password = 0; + for (int i = 0; i < 4; i++) { + int startBit = 11 + i * 8; // 5+6 bits of command + i*8 + uint8_t b = 0; + + for (int j = 0; j < 8; j++) { + int bitPos = startBit + j; + int pos = bitPos / 8; + int shift = 7 - (bitPos % 8); + b |= ((rx[pos] >> shift) & 0x01) << (7 - j); + } + password |= (b << (24 - i * 8)); + } + + // Check password + if (password == ((tag.password[0] << 24) | (tag.password[1] << 16) | (tag.password[2] << 8) | tag.password[3])) { + // Set authentication state + SET_AUTHENTICATED(); + + // Send success response + uint8_t resp_byte = 0x01; // Success code + *txlen = concatbits(tx, *txlen, &resp_byte, 0, 8, true); + } else { + // Authentication failed + RESET_AUTHENTICATION(); + + // Send failure response + uint8_t resp_byte = 0x00; // Failure code + *txlen = concatbits(tx, *txlen, &resp_byte, 0, 8, true); + } + } + } else if (command == HITAGU_CMD_SELECT) { + // Select command + if (rxlen >= 59) { // 5+6+48 bits = 59 bits minimum (48-bit UID) + // Extract UID to select - next 48 bits + uint8_t sel_uid[6] = {0}; + for (int i = 0; i < 6; i++) { + int startBit = 11 + i * 8; // 5+6 bits of command + i*8 + uint8_t b = 0; + + for (int j = 0; j < 8; j++) { + int bitPos = startBit + j; + int pos = bitPos / 8; + int shift = 7 - (bitPos % 8); + b |= ((rx[pos] >> shift) & 0x01) << (7 - j); + } + sel_uid[i] = b; + } + + // Check if UID matches + if (memcmp(sel_uid, tag.uid, 6) == 0) { + // Selected - send response with select data + uint8_t resp_data[4] = {0xCA, 0x24, 0x00, 0x00}; // Standard select response + *txlen = concatbits(tx, *txlen, resp_data, 0, 32, true); + } else { + // UID mismatch - no response + *txlen = 0; + } + } + } else if (command == HITAGU_CMD_READ_MULTIPLE_BLOCK) { + // Read command + if (rxlen >= 19) { // 5+6+8 bits = 19 bits minimum + // Extract page address - 8 bits after command + uint8_t page = 0; + for (int i = 0; i < 8; i++) { + int bitPos = 11 + i; // 5+6 bits of command + i + int pos = bitPos / 8; + int shift = 7 - (bitPos % 8); + page |= ((rx[pos] >> shift) & 0x01) << (7 - i); + } + + // Extract number of blocks to read if ADR flag is set + uint8_t read_len = 1; // Default to 1 page + if ((flags & HITAGU_FLAG_ADR) && rxlen >= 27) { + for (int i = 0; i < 8; i++) { + int bitPos = 19 + i; + int pos = bitPos / 8; + int shift = 7 - (bitPos % 8); + if (pos < (rxlen + 7) / 8) { + read_len |= ((rx[pos] >> shift) & 0x01) << (7 - i); + } + } + } + + // Security check: does this page require authentication? + bool needs_auth = false; + // Check if page is password-protected (e.g., config or password page) + if (page == HITAGU_PASSWORD_PADR) { + needs_auth = true; + } + + // Check authentication for protected pages + if (needs_auth && !IS_AUTHENTICATED()) { + // Not authenticated, cannot read protected pages + DBG Dbprintf("Page %d requires authentication", page); + + // Mark as unauthorized access + *txlen = 0; // No response + } else { + // Map page address (some pages may be aliased) + uint8_t real_page = page; + if (page >= 64 && tag.max_page <= 64) { + real_page = page & 0x3F; // Pages above 64 map to 0-63 + } + + // Read requested number of pages + for (int i = 0; i < read_len && i < 16; i++) { // Limit to 16 pages max + uint8_t curr_page = (real_page + i) % tag.max_page; + + // Special pages + if (curr_page == HITAGU_CONFIG_PADR) { + // Config page + *txlen = concatbits(tx, *txlen, (uint8_t *)&tag.config, 0, 32, true); + } else if (curr_page == HITAGU_PASSWORD_PADR) { + // Password page - only return if authenticated + if (IS_AUTHENTICATED()) { + *txlen = concatbits(tx, *txlen, tag.password, 0, 32, true); + } else { + // Return zeros if not authenticated + uint8_t zeros[4] = {0}; + *txlen = concatbits(tx, *txlen, zeros, 0, 32, true); + } + } else { + // Regular page + *txlen = concatbits(tx, *txlen, tag.data.pages[curr_page], 0, 32, true); + } + } + } + } + } else if (command == HITAGU_CMD_WRITE_SINGLE_BLOCK) { + // Write command + if (rxlen >= 51) { // 5+6+8+32 bits = 51 bits minimum + // Check if authenticated + if (!IS_AUTHENTICATED()) { + DBG Dbprintf("WRITE failed: not authenticated"); + *txlen = 0; // No response + } else { + // Extract page address - 8 bits after command + uint8_t page = 0; + for (int i = 0; i < 8; i++) { + int bitPos = 11 + i; // 5+6 bits of command + i + int pos = bitPos / 8; + int shift = 7 - (bitPos % 8); + page |= ((rx[pos] >> shift) & 0x01) << (7 - i); + } + + // Extract data - 32 bits after page address + uint8_t data[4] = {0}; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 8; j++) { + int bitPos = 19 + i * 8 + j; + int pos = bitPos / 8; + int shift = 7 - (bitPos % 8); + if (pos < (rxlen + 7) / 8) { + data[i] |= ((rx[pos] >> shift) & 0x01) << (7 - j); + } + } + } + + // Map page address + uint8_t real_page = page; + if (page >= 64 && tag.max_page <= 64) { + real_page = page & 0x3F; // Pages above 64 map to 0-63 + } + + // Special pages + if (real_page == HITAGU_CONFIG_PADR) { + // Write config + memcpy(&tag.config, data, 4); + DBG Dbprintf("WRITE CONFIG: %02X %02X %02X %02X", data[0], data[1], data[2], data[3]); + } else if (real_page == HITAGU_PASSWORD_PADR) { + // Write password + memcpy(tag.password, data, 4); + DBG Dbprintf("WRITE PASSWORD: %02X %02X %02X %02X", data[0], data[1], data[2], data[3]); + } else if (real_page < tag.max_page) { + // Regular page + memcpy(tag.data.pages[real_page], data, 4); + DBG Dbprintf("WRITE PAGE %02X: %02X %02X %02X %02X", real_page, data[0], data[1], data[2], data[3]); + } + + // Send success acknowledgment + uint8_t ack = 0x01; // Success acknowledgment + *txlen = concatbits(tx, *txlen, &ack, 0, 8, true); + } + } + } else if (command == HITAGU_CMD_SYSINFO) { + // System info command + // Prepare system info response with ICR field + uint8_t info[8] = {0}; + + // First byte: Error flag (0) + 7 reserved bits + info[0] = 0x00; + + // Additional bytes: System Memory Block Data + // MSN (Manufacturer Serial Number) - example values + info[1] = 0x12; + info[2] = 0x34; + + // MFC (Manufacturer Code) - example value + info[3] = 0x04; // NXP + + // ICR (IC Revision) + info[4] = tag.icr; + + // Reserved bytes + info[5] = 0x00; + info[6] = 0x00; + info[7] = 0x00; + + // Add the system info data to the response + *txlen = concatbits(tx, *txlen, info, 0, 64, true); + } else if (flags == HITAGU_CMD_STAY_QUIET) { + // Quiet command - no response needed + RESET_AUTHENTICATION(); + *txlen = 0; + } else { + // Unknown command + DBG Dbprintf("Unknown command or flags: flags=%02X, cmd=%02X", flags, command); + *txlen = 0; // No response + } + + // Add CRC if requested and there is response data + if (crct_flag && *txlen > 0) { + // Calculate CRC-16/XMODEM directly from tx + uint16_t crc = Crc16(tx, *txlen, 0, CRC16_POLY_CCITT, false, true); + + // Append CRC-16 (16 bits) + *txlen = concatbits(tx, *txlen, (uint8_t *)&crc, 0, 16, true); + } +} + +/* + * Simulates a Hitag µ Tag with the given data + */ +void htu_simulate(bool tag_mem_supplied, int8_t threshold, const uint8_t *data, bool ledcontrol) { + + uint8_t rx[HITAG_FRAME_LEN] = {0}; + size_t rxlen = 0; + uint8_t tx[HITAG_FRAME_LEN]; + size_t txlen = 0; + + // Free any allocated BigBuf memory + BigBuf_free(); + BigBuf_Clear_ext(false); + + DbpString("Starting Hitag µ simulation"); + + // Reset tag state + memset(&tag, 0, sizeof(tag)); + tag.max_page = 64; // Default maximum page + RESET_AUTHENTICATION(); + + // Read tag data into memory if supplied + if (tag_mem_supplied) { + DbpString("Loading Hitag µ memory..."); + // First 6 bytes are the UID (48 bits) + memcpy(tag.uid, data, 6); + // Rest is page data + memcpy(tag.data.pages, data + 6, sizeof(tag.data.pages)); + } + + // Update max_page based on configuration + update_tag_max_page(); + + // Debug output of tag data + DBG Dbprintf("UID: %02X%02X%02X%02X%02X%02X", tag.uid[0], tag.uid[1], tag.uid[2], tag.uid[3], tag.uid[4], tag.uid[5]); + + for (int i = 0; i <= tag.max_page; i++) { + DBG Dbprintf("Page[%2d]: %02X %02X %02X %02X", i, tag.data.pages[i][0], tag.data.pages[i][1], + tag.data.pages[i][2], tag.data.pages[i][3]); + } + + hitag_setup_fpga(0, threshold, ledcontrol); + + int overflow = 0; + + // Simulation main loop + while ((BUTTON_PRESS() == false) && (data_available() == false)) { + uint32_t start_time = 0; + + WDT_HIT(); + + // Receive commands from the reader + hitag_tag_receive_frame(rx, sizeof(rx), &rxlen, &start_time, ledcontrol, &overflow); + + // Check if frame was captured and store it + if (rxlen > 0) { + LogTraceBits(rx, rxlen, start_time, TIMESTAMP, true); + + // Disable timer 1 with external trigger to avoid triggers during our own modulation + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + + // Prepare tag response (tx) + memset(tx, 0x00, sizeof(tx)); + txlen = 0; + + // Process received reader command + htu_handle_reader_command(rx, rxlen, tx, &txlen); + + // Wait for HITAG_T_WAIT_RESP carrier periods after the last reader bit, + // not that since the clock counts since the rising edge, but T_Wait1 is + // with respect to the falling edge, we need to wait actually (T_Wait1 - T_Low) + // periods. The gap time T_Low varies (4..10). All timer values are in + // terms of T0 units + while (AT91C_BASE_TC0->TC_CV < T0 * (HITAG_T_WAIT_RESP - HITAG_T_LOW)) { + }; + + // Send and store the tag answer (if there is any) + if (txlen > 0) { + // Transmit the tag frame + start_time = TIMESTAMP; + hitag_tag_send_frame(tx, txlen, HTU_SOF_BITS, MC4K, ledcontrol); + LogTraceBits(tx, txlen, start_time, TIMESTAMP, false); + } + + // Enable and reset external trigger in timer for capturing future frames + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + // Reset the received frame and response timing info + memset(rx, 0x00, sizeof(rx)); + } + + // Reset the frame length + rxlen = 0; + // Save the timer overflow, will be 0 when frame was received + overflow += (AT91C_BASE_TC1->TC_CV / T0); + // Reset the timer to restart while-loop that receives frames + AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; + } + + hitag_cleanup(ledcontrol); + // Release allocated memory from BigBuf + BigBuf_free(); + + DbpString("Simulation stopped"); +} + +/* + * Send command to reader and receive answer from tag + */ +static int htu_reader_send_receive(uint8_t *tx, size_t txlen, uint8_t *rx, size_t sizeofrx, size_t *rxlen, + uint32_t t_wait, bool ledcontrol, uint8_t modulation, uint8_t sof_bits) { + // Reset the received frame + memset(rx, 0x00, sizeofrx); + + // Disable timer 1 with external trigger to avoid triggers during our own modulation + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + + DBG Dbprintf("tx %d bits:", txlen); + DBG Dbhexdump((txlen + 7) / 8, tx, false); + + // Wait until we can send the command + while (AT91C_BASE_TC0->TC_CV < T0 * t_wait) { + }; + + // Set up tracing + uint32_t start_time = TIMESTAMP; + + // Send the command - Hitag µ always requires SOF + hitag_reader_send_frame(tx, txlen, ledcontrol, true); + + // if (enable_page_tearoff && tearoff_hook() == PM3_ETEAROFF) { + // return PM3_ETEAROFF; + // } + + LogTraceBits(tx, txlen, start_time, TIMESTAMP, true); + + // Enable and reset external trigger in timer for capturing future frames + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + // Capture response - SOF is automatically stripped by hitag_reader_receive_frame + hitag_reader_receive_frame(rx, sizeofrx, rxlen, &start_time, ledcontrol, modulation, sof_bits); + + LogTraceBits(rx, *rxlen, start_time, TIMESTAMP, false); + DBG Dbprintf("rx %d bits:", *rxlen); + DBG Dbhexdump((*rxlen + 7) / 8, rx, false); + + // TODO: check Error flag + + return PM3_SUCCESS; +} + +/* + * Selects a tag using the READ UID, GET SYSTEM INFORMATION, and LOGIN commands + */ +static int htu_select_tag(const lf_hitag_data_t *payload, uint8_t *tx, size_t sizeoftx, uint8_t *rx, size_t sizeofrx, + int t_wait, bool ledcontrol) { + // Initialize response + size_t txlen = 0; + size_t rxlen = 0; + + // Setup FPGA and initialize + hitag_setup_fpga(FPGA_LF_EDGE_DETECT_READER_FIELD, 127, ledcontrol); + + // Prepare common flags and command variables + uint8_t flags = HITAGU_FLAG_CRCT; // Set appropriate flags for all commands + uint8_t command; + // uint8_t parameter; + + // 1. Send READ UID command + command = HITAGU_CMD_READ_UID; + txlen = concatbits(tx, txlen, &flags, 0, 5, true); + txlen = concatbits(tx, txlen, &command, 0, 6, true); + + // Append CRC-16 (16 bits) + uint16_t crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true); + txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true); + + // lf cmdread -d 64 -z 96 -o 160 -e W2400 -e S224 -e E336 -s 4096 -c W0S00100010000 + + // Send the READ UID command and receive the response + htu_reader_send_receive(tx, txlen, rx, sizeofrx, &rxlen, t_wait, ledcontrol, MC4K, HTU_SOF_BITS); + + // Check if the response is valid + if (rxlen < 1 + 48 + 16 || Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) { + DBG Dbprintf("Read UID command failed! %i", rxlen); + return -1; // Read UID failed + } + + // Process the UID from the response + concatbits(tag.uid, 0, rx, 1, 48, false); + + // 2. Send GET SYSTEM INFORMATION command + command = HITAGU_CMD_SYSINFO; + txlen = 0; // Reset txlen for the new command + txlen = concatbits(tx, txlen, &flags, 0, 5, true); + txlen = concatbits(tx, txlen, &command, 0, 6, true); + + // Append CRC-16 (16 bits) + crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true); + txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true); + + // Send the GET SYSTEM INFORMATION command and receive the response + htu_reader_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS); + + concatbits(&tag.icr, 0, rx, 9, 8, false); + + // Check if the response is valid + if (rxlen < 1 + 16 + 16 || Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) { + // 8265 bug? sometile lost Data field first bit + DBG Dbprintf("Get System Information command failed! %i", rxlen); + return -2; // Get System Information failed + } + + // 3. Read config block + command = HITAGU_CMD_READ_MULTIPLE_BLOCK; + txlen = 0; // Reset txlen for the new command + txlen = concatbits(tx, txlen, &flags, 0, 5, true); + txlen = concatbits(tx, txlen, &command, 0, 6, true); + + // Add block address + txlen = concatbits(tx, txlen, (uint8_t *)&"\xFF", 0, 8, true); + + // Add number of blocks, 0 means 1 block + txlen = concatbits(tx, txlen, (uint8_t *)&"\x00", 0, 8, true); + + // Append CRC-16 (16 bits) + crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true); + txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true); + + // Send read command and receive response + htu_reader_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS); + + // Check if the response is valid + if (rxlen < 1 + 32 + 16 || Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) { + DBG Dbprintf("Read config block command failed! %i", rxlen); + return -2; // Read config block failed + } + + // Process the config block from the response + concatbits(tag.config.asBytes, 0, rx, 1, 32, false); + reverse_arraybytes(tag.config.asBytes, HITAGU_BLOCK_SIZE); + + // 4. Send LOGIN command if necessary + if (payload && (payload->cmd == HTUF_82xx || payload->cmd == HTUF_PASSWORD)) { + command = HITAGU_CMD_LOGIN; // Set command for login + txlen = 0; // Reset txlen for the new command + txlen = concatbits(tx, txlen, &flags, 0, 5, true); + txlen = concatbits(tx, txlen, &command, 0, 6, true); + + txlen = concatbits(tx, txlen, payload->pwd, 0, HITAG_PASSWORD_SIZE * 8, false); + + // Append CRC-16 (16 bits) + crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true); + txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true); + + // Send the LOGIN command and receive the response + htu_reader_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS); + + // Check if login succeeded + if (rxlen < 1 + 16 || Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) { + DBG Dbprintf("Login command failed! %i", rxlen); + return -3; // Login failed + } else { + DBG DbpString("Login successful"); + } + + // flags |= HITAGU_FLAG_ADR; + // command = HITAGU_CMD_LOGIN; // Set command for login + // txlen = 0; // Reset txlen for the new command + // txlen = concatbits(tx, txlen, &flags, 0, 5, true); + // txlen = concatbits(tx, txlen, &command, 0, 6, true); + + // txlen = concatbits(tx, txlen, payload->uid, 0, HITAGU_UID_SIZE * 8, false); + // txlen = concatbits(tx, txlen, payload->pwd, 0, HITAG_PASSWORD_SIZE * 8, false); + + // // Append CRC-16 (16 bits) + // crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true); + // txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true); + + // // Send the LOGIN command and receive the response + // htu_reader_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS); + + // // Check if login succeeded + // if (rxlen < 1 + 16 || Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) { + // DBG Dbprintf("Login command failed! %i", rxlen); + // return -3; // Login failed + // } else { + // DbpString("Login successful"); + // } + } + + // If all commands are successful, update the tag's state + update_tag_max_page(); // Update max_page based on the new configuration + + return 0; // Selection successful +} + +/* + * Reads the UID of a Hitag µ tag using the INVENTORY command + */ +int htu_read_uid(uint64_t *uid, bool ledcontrol, bool send_answer) { + // Initialize response + uint8_t rx[HITAG_FRAME_LEN] = {0x00}; + uint8_t tx[HITAG_FRAME_LEN] = {0x00}; + int status = PM3_SUCCESS; + + // Use htu_select_tag to select the card and retrieve UID + int reason = htu_select_tag(NULL, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol); + if (reason != 0) { + DBG DbpString("Error: htu_read_uid Failed to select tag"); + status = PM3_ERFTRANS; + goto exit; + } + + DBG Dbprintf("HitagU UID: %02X%02X%02X%02X%02X%02X", tag.uid[0], tag.uid[1], tag.uid[2], tag.uid[3], tag.uid[4], tag.uid[5]); + + if (uid) { + *uid = BSWAP_48(*(uint64_t*)&tag.uid); + } + +exit: + hitag_cleanup(ledcontrol); + + if (send_answer) { + // Send UID + reply_reason(CMD_LF_HITAGU_UID, status, reason, tag.uid, sizeof(tag.uid)); + } + + // Reset authentication state + RESET_AUTHENTICATION(); + + return status; +} + +/* + * Reads a Hitag µ tag + */ +void htu_read(const lf_hitag_data_t *payload, bool ledcontrol) { + size_t rxlen = 0; + uint8_t rx[HITAG_FRAME_LEN] = {0x00}; + size_t txlen = 0; + uint8_t tx[HITAG_FRAME_LEN] = {0x00}; + int status = PM3_SUCCESS; + + // DBG { + // DbpString("htu_read"); + // Dbprintf("payload->page: %d", payload->page); + // Dbprintf("payload->page_count: %d", payload->page_count); + // Dbprintf("payload->cmd: %d", payload->cmd); + // Dbprintf("payload->uid: %02X%02X%02X%02X%02X%02X", payload->uid[0], payload->uid[1], payload->uid[2], + // payload->uid[3], payload->uid[4], payload->uid[5]); + // Dbprintf("payload->key: %02X%02X%02X%02X%02X%02X", payload->key[0], payload->key[1], payload->key[2], + // payload->key[3], payload->key[4], payload->key[5]); + // Dbprintf("payload->pwd: %02X%02X%02X%02X", payload->pwd[0], payload->pwd[1], payload->pwd[2], payload->pwd[3]); + // } + + // Use htu_select_tag to select the card and retrieve UID + int reason = htu_select_tag(payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol); + if (reason != 0) { + DbpString("Error: htu_read Failed to select tag"); + status = PM3_ERFTRANS; + goto exit; + } + + lf_htu_read_response_t card = { + .icr = tag.icr, + }; + memcpy(card.uid, tag.uid, HITAGU_UID_SIZE); + memcpy(card.config_page.asBytes, tag.config.asBytes, HITAGU_BLOCK_SIZE); + + uint8_t page_count_index = payload->page_count - 1; + + if (payload->page_count == 0) { + page_count_index = tag.max_page - payload->page - 1; + } + + // Step 2: Process the actual command + // Add flags (5 bits) + uint8_t flags = HITAGU_FLAG_CRCT; + txlen = concatbits(tx, txlen, &flags, 0, 5, true); + + // Read command format: [UID] [CRC-16] + + // Add command (6 bits) + uint8_t command = HITAGU_CMD_READ_MULTIPLE_BLOCK; + txlen = concatbits(tx, txlen, &command, 0, 6, true); + + // The 8265 chip has known issues when reading multiple blocks: + // - page_count = 1: Works correctly + // - page_count >= 2: Data field is left shifted by 1 bit + // - page_count = 2 or page+page_count exceeds valid page: Data field has an extra '1' bit + // - page_count = 3,4: Data field last bit is always '1' + // - page_count = 5: CRC is not appended + // - page_count >= 6: May cause next command to have no response + // Workaround: Read one block at a time + if (payload->mode == 0 /**for debug */ + && (payload->cmd == HTUF_82xx || tag.icr == HITAGU_ICR_8265 || !memcmp(tag.uid, "\x00\x00\x00\x00\x00\x00", 6))) { + + uint8_t page_addr; + for (int i = 0; i <= page_count_index; i++) { + page_addr = payload->page + i; + + txlen = 5 + 6; // restore txlen for the new command + txlen = concatbits(tx, txlen, &page_addr, 0, 8, true); + + // Add number of blocks, 0 means 1 block + txlen = concatbits(tx, txlen, (uint8_t *)&"\x00", 0, 8, true); + + // Append CRC-16 (16 bits) + uint16_t crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true); + txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true); + + // Send read command and receive response + htu_reader_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS); + + if (flags & HITAGU_FLAG_CRCT && Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) { + DBG Dbprintf("Error: response CRC invalid"); + card.pages_reason[i] = -1; + continue; + } + + // Check response + if (rxlen < 1 + HITAGU_BLOCK_SIZE * 8 + (flags & HITAGU_FLAG_CRCT ? 16 : 0)) { + DbpString("Error: invalid response received after read command"); + card.pages_reason[i] = -11; + } else { + DBG Dbprintf("Read successful, response: %d bits", rxlen); + // todo: For certain pages, update our cached data + card.pages_reason[i] = 1; + concatbits(card.pages[i], 0, rx, 1, HITAGU_BLOCK_SIZE * 8, false); + } + } + } else { + txlen = concatbits(tx, txlen, &payload->page, 0, 8, true); + + // Add number of blocks, 0 means 1 block + txlen = concatbits(tx, txlen, &page_count_index, 0, 8, true); + + // Append CRC-16 (16 bits) + uint16_t crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true); + txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true); + + // Send read command and receive response + htu_reader_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS); + + if (flags & HITAGU_FLAG_CRCT && Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) { + DBG Dbprintf("Error: response CRC invalid"); + status = PM3_ERFTRANS; + goto exit; + } + + // Check response + if (rxlen < 1 + HITAGU_BLOCK_SIZE * 8 + (flags & HITAGU_FLAG_CRCT ? 16 : 0)) { + DbpString("Error: invalid response received after read command"); + status = PM3_ERFTRANS; + } else { + DBG Dbprintf("Read successful, response: %d bits", rxlen); + // todo: For certain pages, update our cached data + concatbits((uint8_t *)card.pages, 0, rx, 1, rxlen - 1 - 16, false); + for (int i = 0; i < (rxlen - 1 - 16) / (HITAGU_BLOCK_SIZE * 8); i++) { + card.pages_reason[i] = 1; + } + } + } + +exit: + hitag_cleanup(ledcontrol); + // Send status to client + reply_reason(CMD_LF_HITAGU_READ, status, reason, (uint8_t *)&card, sizeof(card)); +} + +/* + * Writes a page to a Hitag µ tag + */ +void htu_write_page(const lf_hitag_data_t *payload, bool ledcontrol) { + size_t rxlen = 0; + uint8_t rx[HITAG_FRAME_LEN] = {0x00}; + size_t txlen = 0; + uint8_t tx[HITAG_FRAME_LEN] = {0x00}; + int status = PM3_SUCCESS; + + // DBG { + // DbpString("htu_write_page"); + // Dbprintf("payload->page: %d", payload->page); + // Dbprintf("payload->data: %02X%02X%02X%02X", payload->data[0], payload->data[1], payload->data[2], payload->data[3]); + // Dbprintf("payload->cmd: %d", payload->cmd); + // Dbprintf("payload->uid: %02X%02X%02X%02X%02X%02X", payload->uid[0], payload->uid[1], payload->uid[2], payload->uid[3], payload->uid[4], payload->uid[5]); + // Dbprintf("payload->key: %02X%02X%02X%02X%02X%02X", payload->key[0], payload->key[1], payload->key[2], payload->key[3], payload->key[4], payload->key[5]); + // Dbprintf("payload->pwd: %02X%02X%02X%02X", payload->pwd[0], payload->pwd[1], payload->pwd[2], payload->pwd[3]); + // Dbprintf("payload->mode: %d", payload->mode); + // } + + int reason = htu_select_tag(payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol); + if (reason != 0) { + status = PM3_ERFTRANS; + goto exit; + } + + // Step 2: Send write command + uint8_t flags = HITAGU_FLAG_CRCT; + + // Add flags (5 bits) for write operation + txlen = concatbits(tx, txlen, &flags, 0, 5, true); + + // Add write command (6 bits) + uint8_t command = HITAGU_CMD_WRITE_SINGLE_BLOCK; + txlen = concatbits(tx, txlen, &command, 0, 6, true); + + // Add page address (8 bits) + txlen = concatbits(tx, txlen, &payload->page, 0, 8, true); + + // Add data to write (32 bits) + txlen = concatbits(tx, txlen, payload->data, 0, 32, false); + + // Append CRC-16 (16 bits) + uint16_t crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true); + txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true); + + DBG Dbprintf("Writing to page 0x%02X", payload->page); + + // Send write command and receive response + htu_reader_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS); + + // Check response + if (payload->cmd == HTUF_82xx && rxlen == 0) { + // 8265 bug? no response on successful write command + reason = 0; + status = PM3_ENODATA; + } else if (rxlen != 1 + 16) { + DbpString("Error: htu_write_page No valid response received after write command"); + reason = -1; + status = PM3_ERFTRANS; + } else { + DBG Dbprintf("Write successful, response: %d bits", rxlen); + } + +exit: + hitag_cleanup(ledcontrol); + reply_reason(CMD_LF_HITAGU_WRITE, status, reason, NULL, 0); +} diff --git a/armsrc/hitagu.h b/armsrc/hitagu.h new file mode 100644 index 000000000..a1144e1e7 --- /dev/null +++ b/armsrc/hitagu.h @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Hitag µ functions +//----------------------------------------------------------------------------- + +#ifndef _HITAGU_H_ +#define _HITAGU_H_ + +#include "common.h" +#include "hitag.h" + +void htu_simulate(bool tag_mem_supplied, int8_t threshold, const uint8_t *data, bool ledcontrol); +void htu_read(const lf_hitag_data_t *payload, bool ledcontrol); +void htu_write_page(const lf_hitag_data_t *payload, bool ledcontrol); +int htu_read_uid(uint64_t *uid, bool ledcontrol, bool send_answer); + +#endif diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 46c1e0d3f..bc9c28fd7 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -64,11 +64,11 @@ SAM7S has several timers, we will use the source TIMER_CLOCK1 (aka AT91C_TC_CLKS TIMER_CLOCK1 = MCK/2, MCK is running at 48 MHz, Timer is running at 48/2 = 24 MHz New timer implementation in ticks.c, which is used in LFOPS.c - 1 μs = 1.5 ticks - 1 fc = 8 μs = 12 ticks + 1 µs = 1.5 ticks + 1 fc = 8 µs = 12 ticks Terms you find in different datasheets and how they match. -1 Cycle = 8 microseconds (μs) == 1 field clock (fc) +1 Cycle = 8 microseconds (µs) == 1 field clock (fc) Note about HITAG timing Hitag units (T0) have duration of 8 microseconds (us), which is 1/125000 per second (carrier) @@ -80,7 +80,7 @@ Hitag units (T0) have duration of 8 microseconds (us), which is 1/125000 per sec ========================================================================================================== ATA5577 Downlink Protocol Timings. - Note: All absolute times assume TC = 1 / fC = 8 μs (fC = 125 kHz) + Note: All absolute times assume TC = 1 / fC = 8 µs (fC = 125 kHz) Note: These timings are from the datasheet and doesn't map the best to the features of the RVD4 LF antenna. RDV4 LF antenna has high voltage and the drop of power when turning off the rf field takes about 1-2 TC longer. diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 274024fb7..854828df9 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -380,6 +380,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdlfhid.c ${PM3_ROOT}/client/src/cmdlfhitag.c ${PM3_ROOT}/client/src/cmdlfhitaghts.c + ${PM3_ROOT}/client/src/cmdlfhitagu.c ${PM3_ROOT}/client/src/cmdlfidteck.c ${PM3_ROOT}/client/src/cmdlfindala.c ${PM3_ROOT}/client/src/cmdlfio.c diff --git a/client/Makefile b/client/Makefile index cf4b65b5f..f743d6968 100644 --- a/client/Makefile +++ b/client/Makefile @@ -660,6 +660,7 @@ SRCS = mifare/aiddesfire.c \ cmdlfhid.c \ cmdlfhitag.c \ cmdlfhitaghts.c \ + cmdlfhitagu.c \ cmdlfidteck.c \ cmdlfindala.c \ cmdlfio.c \ diff --git a/client/experimental_lib/CMakeLists.txt b/client/experimental_lib/CMakeLists.txt index 1e17d3361..7d0c952b1 100644 --- a/client/experimental_lib/CMakeLists.txt +++ b/client/experimental_lib/CMakeLists.txt @@ -380,6 +380,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdlfhid.c ${PM3_ROOT}/client/src/cmdlfhitag.c ${PM3_ROOT}/client/src/cmdlfhitaghts.c + ${PM3_ROOT}/client/src/cmdlfhitagu.c ${PM3_ROOT}/client/src/cmdlfidteck.c ${PM3_ROOT}/client/src/cmdlfindala.c ${PM3_ROOT}/client/src/cmdlfio.c diff --git a/client/src/cmdhf.c b/client/src/cmdhf.c index c38e6da03..ae5f8b845 100644 --- a/client/src/cmdhf.c +++ b/client/src/cmdhf.c @@ -83,7 +83,7 @@ int CmdHFSearch(const char *Cmd) { int res = PM3_ESOFT; - uint8_t success[20] = {0}; + uint8_t success[COUNT_OF_PROTOCOLS] = {0}; PROMPT_CLEARLINE; PrintAndLogEx(INPLACE, " Searching for ThinFilm tag..."); diff --git a/client/src/cmdlf.c b/client/src/cmdlf.c index b2f8ba20c..7bf4dd553 100644 --- a/client/src/cmdlf.c +++ b/client/src/cmdlf.c @@ -41,6 +41,7 @@ #include "cmdlfhid.h" // for hid menu #include "cmdlfhitag.h" // for hitag menu #include "cmdlfhitaghts.h" // for hitag S sub commands +#include "cmdlfhitagu.h" // for hitag µ sub commands #include "cmdlfidteck.h" // for idteck menu #include "cmdlfio.h" // for ioprox menu #include "cmdlfcotag.h" // for COTAG menu @@ -1602,6 +1603,14 @@ static bool check_chiptype(bool getDeviceData) { retval = true; goto out; } + + // Hitag µ + if (read_htu_uid() == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Chipset detection: " _GREEN_("Hitag µ / 8265")); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf hitag htu`") " commands"); + retval = true; + goto out; + } } diff --git a/client/src/cmdlfem410x.c b/client/src/cmdlfem410x.c index 7f296336c..3d4d5f3f2 100644 --- a/client/src/cmdlfem410x.c +++ b/client/src/cmdlfem410x.c @@ -624,11 +624,12 @@ static int CmdEM410xSpoof(const char *Cmd) { static int CmdEM410xClone(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf em 410x clone", - "clone a EM410x ID to a T55x7, Q5/T5555, EM4305/4469 or Hitag S/8211/8268/8310 tag.", + "clone a EM410x ID to a T55x7, Q5/T5555, EM4305/4469, Hitag S/8211/8268/8310 or Hitag µ/8265 tag.", "lf em 410x clone --id 0F0368568B -> encode for T55x7 tag\n" "lf em 410x clone --id 0F0368568B --q5 -> encode for Q5/T5555 tag\n" "lf em 410x clone --id 0F0368568B --em -> encode for EM4305/4469\n" - "lf em 410x clone --id 0F0368568B --hts -> encode for Hitag S/8211/8268/8310" + "lf em 410x clone --id 0F0368568B --hts -> encode for Hitag S/8211/8268/8310\n" + "lf em 410x clone --id 0F0368568B --htu -> encode for Hitag µ/8265 tag" ); void *argtable[] = { @@ -638,6 +639,7 @@ static int CmdEM410xClone(const char *Cmd) { arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), arg_lit0(NULL, "hts", "optional - specify writing to Hitag S/8211/8268/8310 tag"), + arg_lit0(NULL, "htu", "optional - specify writing to Hitag µ/8265 tag"), arg_lit0(NULL, "electra", "optional - add Electra blocks to tag"), arg_param_end }; @@ -651,23 +653,23 @@ static int CmdEM410xClone(const char *Cmd) { bool q5 = arg_get_lit(ctx, 3); bool em = arg_get_lit(ctx, 4); bool hts = arg_get_lit(ctx, 5); - bool add_electra = arg_get_lit(ctx, 6); + bool htu = arg_get_lit(ctx, 6); + bool add_electra = arg_get_lit(ctx, 7); CLIParserFree(ctx); - if (q5 + em + hts > 1) { + if (q5 + em + hts + htu > 1) { PrintAndLogEx(FAILED, "Only specify one tag Type"); return PM3_EINVARG; } - if (hts) { - if (IfPm3Hitag() == false) { - PrintAndLogEx(FAILED, "Device not compiled to support Hitag"); - return PM3_EINVARG; - } - if (clk == 40) { - PrintAndLogEx(FAILED, "supported clock rates for Hitag are " _YELLOW_("16, 32, 64")); - return PM3_EINVARG; - } + if ((hts || htu) && IfPm3Hitag() == false) { + PrintAndLogEx(FAILED, "Device not compiled to support Hitag"); + return PM3_EINVARG; + } + + if ((hts || htu) && clk == 40) { + PrintAndLogEx(FAILED, "supported clock rates for Hitag are " _YELLOW_("16, 32, 64")); + return PM3_EINVARG; } // Allowed clock rates: 16, 32, 40 and 64 @@ -678,9 +680,9 @@ static int CmdEM410xClone(const char *Cmd) { uint64_t id = bytes_to_num(uid, uid_len); PrintAndLogEx(SUCCESS, "Preparing to clone EM4102 to " _YELLOW_("%s") " tag with EM Tag ID " _GREEN_("%010" PRIX64) " (RF/%d)", - q5 ? "Q5/T5555" : (em ? "EM4305/4469" : (hts ? "Hitag S/82xx" : "T55x7")), id, clk); + q5 ? "Q5/T5555" : (em ? "EM4305/4469" : (hts ? "Hitag S/82xx" : (htu ? "Hitag µ/82xx" : "T55x7"))), id, clk); - uint8_t data[HITAG_BLOCK_SIZE * 2] = {0xFF, 0x80}; // EM410X_HEADER 9 bits of one + uint8_t data[8] = {0xFF, 0x80}; // EM410X_HEADER 9 bits of one uint32_t databits = 9; uint8_t c_parity = 0; @@ -706,35 +708,45 @@ static int CmdEM410xClone(const char *Cmd) { lf_hitag_data_t packet; memset(&packet, 0, sizeof(packet)); - for (size_t steps = 0; steps < 3; steps++) { - switch (steps) { - case 0: - packet.data[0] = 0xCA; //compatiable for 82xx, no impact on Hitag S - // clk -> TTFDR1 TTFDR0 - // 32 -> 0x00 4 kBit/s - // 16 -> 0x10 8 kBit/s - // 64 -> 0x20 2 kBit/s - packet.data[1] = 0x04; + for (size_t step = 0; step < 3; step++) { + switch (step) { + case 0: { + hitags_config_t config = {0}; + config.MEMT = 0x02; // compatiable for 82xx, no impact on Hitag S + config.TTFM = 0x01; // 0 = "Block 0, Block 1, Block 2, Block 3", 1 = "Block 0, Block 1" + config.TTFC = 0x00; // Manchester + config.auth = 0x00; // Plain + + //compatiable for 82xx, no impact on Hitag S + config.RES1 = 0x01; + config.RES4 = 0x01; + config.RES5 = 0x01; switch (clk) { + case 64: + // 2 kBit/s + config.TTFDR = 0x02; + break; case 32: + // 4 kBit/s + config.TTFDR = 0x00; break; case 16: - packet.data[1] |= 0x10; - break; - case 64: - packet.data[1] |= 0x20; + // 8 kBit/s + config.TTFDR = 0x01; break; } - packet.data[2] = 0; - packet.data[3] = 0; //TODO: keep PWDH0? + //TODO: keep other fields? + memcpy(packet.data, &config, sizeof(config)); + // PrintAndLogEx(INFO, "packet.data: %s", sprint_hex(packet.data, sizeof(packet.data))); packet.page = 1; break; + } case 1: - memcpy(packet.data, &data[HITAG_BLOCK_SIZE * 0], HITAG_BLOCK_SIZE); + memcpy(packet.data, &data[HITAGS_PAGE_SIZE * 0], HITAGS_PAGE_SIZE); packet.page = 4; break; case 2: - memcpy(packet.data, &data[HITAG_BLOCK_SIZE * 1], HITAG_BLOCK_SIZE); + memcpy(packet.data, &data[HITAGS_PAGE_SIZE * 1], HITAGS_PAGE_SIZE); packet.page = 5; break; } @@ -748,10 +760,78 @@ static int CmdEM410xClone(const char *Cmd) { return PM3_ETIMEOUT; } if (resp.status != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "Something went wrong"); + PrintAndLogEx(WARNING, "Something went wrong in step %zu", step); return resp.status; } } + } else if (htu) { + lf_hitag_data_t packet; + memset(&packet, 0, sizeof(packet)); + + // Use password auth with default password + packet.cmd = HTUF_82xx; + memcpy(packet.pwd, "\x00\x00\x00\x00", HITAG_PASSWORD_SIZE); + // memcpy(packet.pwd, "\x9A\xC4\x99\x9C", HITAGU_BLOCK_SIZE); + + for (size_t step = 0; step < 3; step++) { + switch (step) { + case 0: { + // Configure datarate based on clock + // clk -> datarate + // 64 -> 0x00 2 kBit/s + // 32 -> 0x01 4 kBit/s + // 16 -> 0x10 8 kBit/s + hitagu82xx_config_t config = {0}; + + config.datarate_override = 0x00; // no datarate override + config.encoding = 0x00; // Manchester + config.ttf_mode = 0x01; // 01 = "Block 0, Block 1" + config.ttf = 0x01; // enable TTF + + switch (clk) { + case 64: + break; + case 32: + config.datarate = 0x01; + break; + case 16: + config.datarate = 0x02; + break; + } + packet.data[0] = reflect8(*(uint8_t*)&config); + packet.page = HITAGU_CONFIG_PADR; // Config block + break; + } + case 1: + memcpy(packet.data, &data[HITAGU_BLOCK_SIZE * 0], HITAGU_BLOCK_SIZE); + packet.page = 0; // Start writing EM410x data + break; + case 2: + memcpy(packet.data, &data[HITAGU_BLOCK_SIZE * 1], HITAGU_BLOCK_SIZE); + packet.page = 1; // Continue with second block + break; + } + + SendCommandNG(CMD_LF_HITAGU_WRITE, (uint8_t *)&packet, sizeof(packet)); + if (WaitForResponseTimeout(CMD_LF_HITAGU_WRITE, &resp, 4000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_ENODATA && resp.status != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Something went wrong in step %zu, retrying... Press " _GREEN_("") " to exit", step); + // 8265 Often fails during continuous command execution, need to retry + if (kbd_enter_pressed()) { + PrintAndLogEx(INFO, "Button pressed, user aborted"); + return PM3_EOPABORTED; + } + + step--; + continue; + } + //TODO: fix this + resp.status = PM3_SUCCESS; + } } else { struct { bool Q5; diff --git a/client/src/cmdlfhitag.c b/client/src/cmdlfhitag.c index 2cafdb161..81e38edc4 100644 --- a/client/src/cmdlfhitag.c +++ b/client/src/cmdlfhitag.c @@ -16,6 +16,8 @@ // Low frequency Hitag support //----------------------------------------------------------------------------- #include "cmdlfhitag.h" +#include "cmdlfhitaghts.h" +#include "cmdlfhitagu.h" #include #include "cmdparser.h" // command_t #include "comms.h" @@ -31,7 +33,6 @@ #include "pm3_cmd.h" // return codes #include "hitag2/hitag2_crypto.h" #include "util_posix.h" // msclock -#include "cmdlfhitaghts.h" static int CmdHelp(const char *Cmd); @@ -2458,6 +2459,7 @@ static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdLFHitagList, AlwaysAvailable, "List Hitag trace history"}, {"hts", CmdLFHitagS, AlwaysAvailable, "{ Hitag S/8211 operations }"}, + {"htu", CmdLFHitagU, AlwaysAvailable, "{ Hitag µ/8265 operations }"}, {"-----------", CmdHelp, IfPm3Hitag, "------------------------ " _CYAN_("General") " ------------------------"}, {"info", CmdLFHitagInfo, IfPm3Hitag, "Hitag 2 tag information"}, {"reader", CmdLFHitagReader, IfPm3Hitag, "Act like a Hitag 2 reader"}, diff --git a/client/src/cmdlfhitagu.c b/client/src/cmdlfhitagu.c new file mode 100644 index 000000000..da8594016 --- /dev/null +++ b/client/src/cmdlfhitagu.c @@ -0,0 +1,687 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Low frequency Hitag µ support +//----------------------------------------------------------------------------- +#include "cmdlfhitagu.h" + +#include "cliparser.h" +#include "cmddata.h" // setDemodBuff +#include "cmdparser.h" // command_t +#include "cmdtrace.h" +#include "commonutil.h" +#include "comms.h" +#include "crc16.h" +#include "fileutils.h" // savefile +#include "graph.h" // MAX_GRAPH_TRACE_LEN +#include "hitag.h" +#include "hitag2/hitag2_crypto.h" +#include "lfdemod.h" +#include "pm3_cmd.h" // return codes +#include "protocols.h" // defines +#include "util_posix.h" // msclock +#include + +static int CmdHelp(const char *Cmd); + +uint8_t hitagu_CRC_check(uint8_t *d, uint32_t nbit) { + if (nbit < 9) { + return 2; + } + + return (Crc16(d, nbit, 0, CRC16_POLY_CCITT, false, false) == 0); +} + +void annotateHitagU(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response) { + + if (is_response) { + + } else { + uint8_t flag = reflect8(cmd[0]) & 0x1F; + uint8_t command = ((reflect8(cmd[0]) >> 5) & 0x07) | ((reflect8(cmd[1]) & 0x07) << 3); + bool has_uid = false; + + size_t exp_len = snprintf(exp, size, "Flg:"); + + if (flag & HITAGU_FLAG_PEXT) { + exp_len += snprintf(exp + exp_len, size - exp_len, " PEXT"); + } + if (flag & HITAGU_FLAG_INV) { + exp_len += snprintf(exp + exp_len, size - exp_len, " INV"); + if (flag & HITAGU_FLAG_RFU) { + exp_len += snprintf(exp + exp_len, size - exp_len, " RFU"); + } + if (flag & HITAGU_FLAG_NOS) { + exp_len += snprintf(exp + exp_len, size - exp_len, " NOS"); + } + } else { + if (flag & HITAGU_FLAG_SEL) { + exp_len += snprintf(exp + exp_len, size - exp_len, " SEL"); + } + if (flag & HITAGU_FLAG_ADR) { + exp_len += snprintf(exp + exp_len, size - exp_len, " ADR"); + has_uid = true; + } + } + if (flag & HITAGU_FLAG_CRCT) { + exp_len += snprintf(exp + exp_len, size - exp_len, " CRCT"); + } + + exp_len += snprintf(exp + exp_len, size - exp_len, "|Cmd: "); + switch (command) { + case HITAGU_CMD_LOGIN: { + bool has_mfc = false; + if (cmdsize == 6 + has_uid * HITAGU_UID_SIZE || cmdsize == 8 + has_uid * HITAGU_UID_SIZE) { + exp_len += snprintf(exp + exp_len, size - exp_len, "8265 LOGIN"); + } else if (cmdsize == 7 + has_uid * HITAGU_UID_SIZE || cmdsize == 9 + has_uid * HITAGU_UID_SIZE) { + uint8_t mfc; + concatbits(&mfc, 0, cmd, 5 + 6 + 8 + 32, 8, false); + exp_len += snprintf(exp + exp_len, size - exp_len, "LOGIN mfc:%02x ", mfc); + has_mfc = true; + } + if (has_uid) { + uint8_t uid[HITAGU_UID_SIZE]; + concatbits(uid, 0, cmd, 5 + 6 + has_mfc * 8 + 32, HITAGU_UID_SIZE * 8, false); + exp_len += snprintf(exp + exp_len, size - exp_len, " uid:%s", sprint_hex_inrow(uid, HITAGU_UID_SIZE)); + } + uint8_t password[HITAG_PASSWORD_SIZE]; + concatbits(password, 0, cmd, 5 + 6 + has_mfc * 8 + has_uid * HITAGU_UID_SIZE * 8, HITAG_PASSWORD_SIZE * 8, false); + exp_len += snprintf(exp + exp_len, size - exp_len, " pwd:%s", sprint_hex_inrow(password, HITAG_PASSWORD_SIZE)); + break; + } + case HITAGU_CMD_INVENTORY: + exp_len += snprintf(exp + exp_len, size - exp_len, "INVENTORY"); + break; + case HITAGU_CMD_READ_MULTIPLE_BLOCK: { + uint8_t block_addr; + concatbits(&block_addr, 0, cmd, 5 + 6, 8, false); + + uint8_t block_count; + concatbits(&block_count, 0, cmd, 5 + 6 + 8, 8, false); + + exp_len += snprintf(exp + exp_len, size - exp_len, "READ MULTIPLE BLOCK start:%d num:%d", reflect8(block_addr), reflect8(block_count)); + break; + } + case HITAGU_CMD_WRITE_SINGLE_BLOCK: { + uint8_t block_addr; + concatbits(&block_addr, 0, cmd, 5 + 6, 8, false); + + uint8_t block_data[4]; + concatbits(block_data, 0, cmd, 5 + 6 + 8, 32, false); + + exp_len += snprintf(exp + exp_len, size - exp_len, "WRITE SINGLE BLOCK start:%d data:[%s]", + reflect8(block_addr), sprint_hex_inrow(block_data, 4)); + break; + } + case HITAGU_CMD_SELECT: + exp_len += snprintf(exp + exp_len, size - exp_len, "SELECT"); + break; + case HITAGU_CMD_SYSINFO: + exp_len += snprintf(exp + exp_len, size - exp_len, "GET SYSTEM INFORMATION"); + break; + case HITAGU_CMD_READ_UID: + exp_len += snprintf(exp + exp_len, size - exp_len, "READ UID"); + break; + case HITAGU_CMD_STAY_QUIET: + exp_len += snprintf(exp + exp_len, size - exp_len, "STAY QUIET"); + break; + default: + exp_len += snprintf(exp + exp_len, size - exp_len, "Unknown 0x%02X", command); + break; + } + } +} + +static bool htu_get_uid(uint64_t *uid) { + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAGU_UID, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_LF_HITAGU_UID, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return false; + } + + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(DEBUG, "DEBUG: Error - failed getting Hitag µ UID"); + return false; + } + + if (uid) { + *uid = bytes_to_num(resp.data.asBytes, HITAGU_UID_SIZE); + } + return true; +} + +int read_htu_uid(void) { + uint64_t uid = 0; + if (htu_get_uid(&uid) == false) { + return PM3_ESOFT; + } + + PrintAndLogEx(SUCCESS, "UID.... " _GREEN_("%012llX"), uid); + // PrintAndLogEx(SUCCESS, "TYPE... " _GREEN_("%s"), htu_get_type_str(uid)); + return PM3_SUCCESS; +} + +static int process_hitagu_common_args(CLIParserContext *ctx, lf_hitag_data_t *const packet) { + bool use_82xx = arg_get_lit(ctx, 1); + bool use_password = false; + uint8_t key[HITAG_PASSWORD_SIZE]; + int key_len = 0; + + int res = CLIParamHexToBuf(arg_get_str(ctx, 2), key, HITAG_PASSWORD_SIZE, &key_len); + if (res != 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + if (key_len != 0 && key_len != HITAG_PASSWORD_SIZE) { + PrintAndLogEx(WARNING, "Wrong KEY len expected 0 or 4, got %d", key_len); + return PM3_EINVARG; + } + + // complete options + if (key_len == 0 && use_82xx) { + memcpy(key, "\x00\x00\x00\x00", 4); + key_len = 4; + } else if (key_len != 0) { + use_password = true; + } + + memset(packet, 0, sizeof(*packet)); + + if (use_82xx) { + packet->cmd = HTUF_82xx; + memcpy(packet->pwd, key, sizeof(packet->pwd)); + PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag µ") " in 82xx mode"); + } else if (use_password) { + packet->cmd = HTUF_PASSWORD; + memcpy(packet->pwd, key, sizeof(packet->pwd)); + PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag µ") " in password mode"); + } else { + packet->cmd = HTUF_PLAIN; + memcpy(packet->pwd, key, sizeof(packet->pwd)); + PrintAndLogEx(INFO, "Access " _YELLOW_("Hitag µ") " in Plain mode"); + } + + return PM3_SUCCESS; +} + +static void print_error(int8_t reason) { + switch (reason) { + case 0: + PrintAndLogEx(FAILED, "No data"); + break; + case -2: + PrintAndLogEx(FAILED, "UID Request failed!"); + break; + case -3: + PrintAndLogEx(FAILED, "Select UID failed!"); + break; + case -4: + PrintAndLogEx(FAILED, "No write access on block. Not authorized?"); + break; + case -5: + PrintAndLogEx(FAILED, "Write failed! Wrong password?"); + break; + case -6: + PrintAndLogEx(FAILED, "Error, " _YELLOW_("AUT=1") " This tag is configured in Authentication Mode"); + break; + case -7: + PrintAndLogEx(FAILED, "Error, unknown function"); + break; + case -8: + PrintAndLogEx(FAILED, "Authenticate failed!"); + break; + case -9: + PrintAndLogEx(FAILED, "No write access on block"); + break; + case -10: + PrintAndLogEx(FAILED, "Write to block failed!"); + break; + case -11: + PrintAndLogEx(FAILED, "Read block failed!"); + break; + default: + // PM3_REASON_UNKNOWN + PrintAndLogEx(FAILED, "Error - Hitag µ failed"); + } +} + +static void hitagu_config_print(hitagu_config_t config) { + PrintAndLogEx(INFO, " Data Rate......... %s", (const char *[]) {"2 kbit/s", "4 kbit/s", "8 kbit/s", "Reserved"}[config.datarate]); + PrintAndLogEx(INFO, " Encoding.......... %s", config.encoding ? _YELLOW_("Bi-phase") : _YELLOW_("Manchester")); + PrintAndLogEx(INFO, " Password Protect W Bit 0-127(block 0-3) %s", config.pwdW0_127 ? _RED_("Yes") : _GREEN_("No")); + PrintAndLogEx(INFO, " Password Protect W Bit 128-511(block 4-15) %s", config.pwdW128_511 ? _RED_("Yes") : _GREEN_("No")); + PrintAndLogEx(INFO, " Password Protect W Bit 512-Max(block 16-Max) %s", config.pwdW512_max ? _RED_("Yes") : _GREEN_("No")); + PrintAndLogEx(INFO, " Password Protect RW Bit 512-Max(block 16-Max) %s", config.pwdRW512_max ? _RED_("Yes") : _GREEN_("No")); +} + +static void hitagu8265_config_print(hitagu82xx_config_t config) { + PrintAndLogEx(INFO, " Config Byte0: %s", sprint_hex((uint8_t *)&config, sizeof(config))); // for debug + // Check if datarate_override is set + if (config.datarate_override) { + PrintAndLogEx(INFO, " Data Rate........ %s", _YELLOW_("2 kbit/s")); + } else { + PrintAndLogEx(INFO, " Data Rate........ %s", + (const char *[]) {"2 kbit/s", "4 kbit/s", "8 kbit/s", "2 kbit/s"}[config.datarate]); + } + PrintAndLogEx(INFO, " Rate Override.... %s", config.datarate_override ? _RED_("Yes") : _GREEN_("No")); + PrintAndLogEx(INFO, " Encoding......... %s", config.encoding ? _YELLOW_("Bi-phase") : _YELLOW_("Manchester")); + + PrintAndLogEx(INFO, " TTF mode ........ %s", + (const char *[]) { + "Block 0, Block 1, Block 2, Block 3", + "Block 0, Block 1", + "Block 0, Block 1, Block 2, Block 3", + "Block 0, Block 1, Block 2, Block 3", + }[config.ttf_mode]); + PrintAndLogEx(INFO, " TTF.............. %s", config.ttf ? _GREEN_("Enabled") : _RED_("Disabled")); +} + +static int CmdLFHitagURead(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag htu rdbl", + "Read Hitag µ memory.\n\n" + " 82xx password mode: \n" + " - default password 00000000\n", + " lf hitag htu rdbl -p 1 -> Hitag µ, plain mode\n" + " lf hitag htu rdbl -p 1 --82xx -> 82xx, password mode, def pass\n" + " lf hitag htu rdbl -p 1 --82xx -k 9AC4999C -> 82xx, password mode\n"); + + void *argtable[] = {arg_param_begin, + arg_lit0("8", "82xx", "82xx mode"), + arg_str0("k", "key", "", "pwd, 4 hex bytes"), + arg_int0("p", "page", "", "block address to read from (def: 0)"), + arg_int0("c", "count", "", "how many blocks to read. '0' reads all blocks (def: 1)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + lf_hitag_data_t packet; + + if (process_hitagu_common_args(ctx, &packet) < 0) return PM3_EINVARG; + + uint32_t page = arg_get_int_def(ctx, 3, 0); + + if (page >= HITAGU_MAX_BLOCKS) { + PrintAndLogEx(WARNING, "Block address Invalid. Maximum is 255."); + return PM3_EINVARG; + } + + uint32_t count = arg_get_int_def(ctx, 4, 1); + + if (count > HITAGU_MAX_BLOCKS) { + PrintAndLogEx(WARNING, "No more than %d blocks can be read at once.", HITAGU_MAX_BLOCKS); + return PM3_EINVARG; + } + + CLIParserFree(ctx); + + packet.page = page; + packet.page_count = count; + // packet.mode = 1; // for debug + + PrintAndLogEx(INFO, "Read Hitag µ memory block %d, count %d", page, count); + + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAGU_READ, (uint8_t *)&packet, sizeof(packet)); + + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_LF_HITAGU_READ, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) { + print_error(resp.reason); + return PM3_ESOFT; + } + + lf_htu_read_response_t *card = (lf_htu_read_response_t *)resp.data.asBytes; + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); + + int user_blocks; + uint8_t icr = card->icr; + + if (icr == HITAGU_ICR_STANDARD) { + user_blocks = HITAGU_MAX_PAGE_STANDARD; + PrintAndLogEx(INFO, "Hitag µ Standard (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks); + } else if (icr == HITAGU_ICR_ADVANCED) { + user_blocks = HITAGU_MAX_PAGE_ADVANCED; + PrintAndLogEx(INFO, "Hitag µ Advanced (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks); + } else if (icr == HITAGU_ICR_ADVANCED_PLUS) { + user_blocks = HITAGU_MAX_PAGE_ADVANCED_PLUS; + PrintAndLogEx(INFO, "Hitag µ Advanced+ (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks); + } else if (icr == HITAGU_ICR_8265) { + user_blocks = HITAGU_MAX_PAGE_8265; + PrintAndLogEx(INFO, "Hitag µ 8265 (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks); + } else { + user_blocks = HITAGU_MAX_PAGE_STANDARD; + PrintAndLogEx(INFO, "Unknown ICR (0x%02X)", icr); + } + + if (packet.cmd == HTUF_82xx || icr == HITAGU_ICR_8265) { + hitagu8265_config_print(card->config_page.s82xx); + } else { + hitagu_config_print(card->config_page.s); + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Data") " ---------------------------"); + PrintAndLogEx(INFO, " # | 00 01 02 03 | ascii | perm | info"); + PrintAndLogEx(INFO, "----+-------------+-------+------+------"); + + if (count == 0) { + count = (user_blocks > page) ? (user_blocks - page) : HITAGU_MAX_BLOCKS; + } + + for (int i = 0; i < count; ++i) { + int page_addr = page + i; + if (page_addr >= HITAGU_MAX_BLOCKS) { + break; + } + if (card->pages_reason[i] > 0) { + PrintAndLogEx(SUCCESS, "% 3u | %s | " NOLF, page_addr, sprint_hex_ascii(card->pages[i], HITAGU_BLOCK_SIZE)); + + // access right + // 82xx + if ((packet.cmd == HTUF_82xx || icr == HITAGU_ICR_8265) && page_addr != HITAGU_PASSWORD_PADR) { + PrintAndLogEx(NORMAL, _YELLOW_("RO ") NOLF); + } else if ((packet.cmd == HTUF_82xx || icr == HITAGU_ICR_8265) && page_addr == HITAGU_PASSWORD_PADR) { + PrintAndLogEx(NORMAL, _RED_("R/WP") NOLF); + // Hitag µ + } else if (page_addr < HITAGU_MAX_PAGE_STANDARD) { + if (card->config_page.s.pwdW0_127) + PrintAndLogEx(NORMAL, _RED_("RO ") NOLF); + else + PrintAndLogEx(NORMAL, _GREEN_("RW ") NOLF); + } else if (HITAGU_MAX_PAGE_STANDARD <= page_addr && page_addr < HITAGU_MAX_PAGE_ADVANCED) { + if (card->config_page.s.pwdW128_511) + PrintAndLogEx(NORMAL, _RED_("RO ") NOLF); + else + PrintAndLogEx(NORMAL, _GREEN_("RW ") NOLF); + } else if (HITAGU_MAX_PAGE_ADVANCED <= page_addr && page_addr < HITAGU_MAX_PAGE_ADVANCED_PLUS) { + if (card->config_page.s.pwdRW512_max) { + PrintAndLogEx(NORMAL, _RED_("R/WP") NOLF); + } else { + if (card->config_page.s.pwdW512_max) + PrintAndLogEx(NORMAL, _RED_("RO ") NOLF); + else + PrintAndLogEx(NORMAL, _GREEN_("RW ") NOLF); + } + } else + PrintAndLogEx(NORMAL, _YELLOW_("UNK ") NOLF); + + PrintAndLogEx(NORMAL, " | " NOLF); + + // info + if (page_addr == HITAGU_PASSWORD_PADR) { + PrintAndLogEx(NORMAL, "Password"); + } else if (page_addr == HITAGU_CONFIG_PADR) { + PrintAndLogEx(NORMAL, "Config"); + } else + PrintAndLogEx(NORMAL, "Data"); + } else { + PrintAndLogEx(INFO, "% 3u | -- -- -- -- | .... | N/A | " NOLF, page_addr); + print_error(card->pages_reason[i]); + } + } + + PrintAndLogEx(INFO, "----+-------------+-------+------+------"); + PrintAndLogEx(INFO, " " _YELLOW_("RO") " = Read without password, write with password"); + PrintAndLogEx(INFO, " " _GREEN_("R/W") " = Read and write without password"); + PrintAndLogEx(INFO, " " _RED_("R/WP") " = Read and write with password"); + PrintAndLogEx(INFO, "----------------------------------------"); + return PM3_SUCCESS; +} + +static int CmdLFHitagUDump(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag htu dump", + "Read all Hitag µ memory and save to file\n" + " 82xx password mode: \n" + " - default password 00000000\n", + "lf hitag htu dump --82xx -> use def pwd\n" + "lf hitag htu dump --82xx -k 9AC4999C -> pwd mode\n"); + + void *argtable[] = {arg_param_begin, + arg_lit0("8", "82xx", "82xx mode"), + arg_str0("k", "key", "", "pwd, 4 hex bytes"), + arg_str0("f", "file", "", "specify file name"), + arg_lit0(NULL, "ns", "no save to file"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + lf_hitag_data_t packet; + memset(&packet, 0, sizeof(packet)); + + if (process_hitagu_common_args(ctx, &packet) < 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + bool nosave = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + + // read all pages + packet.page = 0; + packet.page_count = 0; + + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAGU_READ, (uint8_t *)&packet, sizeof(packet)); + + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_LF_HITAGU_READ, &resp, 5000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) { + print_error(resp.reason); + return PM3_ESOFT; + } + + lf_htu_read_response_t *card = (lf_htu_read_response_t *)resp.data.asBytes; + + int user_blocks; + uint8_t icr = card->icr; + + if (icr == HITAGU_ICR_STANDARD) { + user_blocks = HITAGU_MAX_PAGE_STANDARD; + PrintAndLogEx(INFO, "Hitag µ Standard (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks); + } else if (icr == HITAGU_ICR_ADVANCED) { + user_blocks = HITAGU_MAX_PAGE_ADVANCED; + PrintAndLogEx(INFO, "Hitag µ Advanced (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks); + } else if (icr == HITAGU_ICR_ADVANCED_PLUS) { + user_blocks = HITAGU_MAX_PAGE_ADVANCED_PLUS; + PrintAndLogEx(INFO, "Hitag µ Advanced+ (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks); + } else if (icr == HITAGU_ICR_8265) { + user_blocks = HITAGU_MAX_PAGE_8265; + PrintAndLogEx(INFO, "Hitag µ 8265 (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks); + } else { + user_blocks = HITAGU_MAX_PAGE_STANDARD; + PrintAndLogEx(INFO, "Unknown ICR (0x%02X)", icr); + } + + int mem_size = (user_blocks + 2) * HITAGU_BLOCK_SIZE; + + hitagu_config_t config = card->config_page.s; + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); + hitagu_config_print(config); + + if (nosave) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Called with no save option"); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; + } + + if (fnlen < 1) { + char *fptr = filename; + fptr += snprintf(filename, sizeof(filename), "lf-hitagu-"); + FillFileNameByUID(fptr, card->uid, "-dump", HITAGU_UID_SIZE); + } + + pm3_save_dump(filename, (uint8_t *)card->pages, mem_size, jsfHitag); + + return PM3_SUCCESS; +} + +static int CmdLFHitagUWrite(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag htu wrbl", + "Write a block in Hitag µ memory.\n" + " 82xx password mode: \n" + " - default password 00000000\n", + " lf hitag htu wrbl -p 6 -d 01020304 -> Hitag µ, plain mode\n" + " lf hitag htu wrbl -p 6 -d 01020304 --82xx -> use def pwd\n" + " lf hitag htu wrbl -p 6 -d 01020304 --82xx -k 9AC4999C -> 82xx, password mode\n"); + + void *argtable[] = {arg_param_begin, + arg_lit0("8", "82xx", "82xx mode"), + arg_str0("k", "key", "", "pwd, 4 hex bytes"), + arg_int1("p", "page", "", "block address to write to"), + arg_str1("d", "data", "", "data, 4 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + lf_hitag_data_t packet; + + if (process_hitagu_common_args(ctx, &packet) < 0) return PM3_EINVARG; + + int page = arg_get_int_def(ctx, 3, 0); + + uint8_t data[HITAGU_BLOCK_SIZE]; + int data_len = 0; + + int res = CLIParamHexToBuf(arg_get_str(ctx, 4), data, HITAGU_BLOCK_SIZE, &data_len); + if (res != 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + CLIParserFree(ctx); + + packet.page = page; + memcpy(packet.data, data, sizeof(packet.data)); + + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAGU_WRITE, (uint8_t *)&packet, sizeof(packet)); + + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_LF_HITAGU_WRITE, &resp, 4000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + if (resp.status == PM3_ETEAROFF) { + PrintAndLogEx(INFO, "Writing tear off triggered"); + return PM3_SUCCESS; + } + + if (resp.status != PM3_SUCCESS) { + print_error(resp.reason); + return resp.status; + } + + PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); + return PM3_SUCCESS; +} + +static int CmdLFHitagUReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag htu reader", "Act as a Hitag µ reader. Look for Hitag µ tags until Enter or the pm3 button is pressed\n", + "lf hitag htu reader\n" + "lf hitag htu reader -@ -> Continuous mode"); + + void *argtable[] = {arg_param_begin, arg_lit0("@", NULL, "continuous reader mode"), arg_param_end}; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + if (cm) { + PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); + } + + do { + // read UID + read_htu_uid(); + } while (cm && kbd_enter_pressed() == false); + + return PM3_SUCCESS; +} + +static int CmdLFHitagUSim(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag htu sim", + "Simulate Hitag µ transponder\n" + "You need to `lf hitag htu eload` first", + "lf hitag htu sim\n" + "lf hitag htu sim --82xx\n" + "lf hitag htu sim -t 30 -> set threshold to 30"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("8", "82xx", "simulate 82xx"), + arg_int0("t", "threshold", "", "set edge detect threshold (def: 127)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + // bool use_82xx = arg_get_lit(ctx, 1); // not implemented yet + int threshold = arg_get_int_def(ctx, 2, 127); + CLIParserFree(ctx); + + clearCommandBuffer(); + SendCommandMIX(CMD_LF_HITAGU_SIMULATE, false, threshold, 0, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdLFHitagUList(const char *Cmd) { return CmdTraceListAlias(Cmd, "lf hitag htu", "hitagu"); } + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help" }, + {"list", CmdLFHitagUList, AlwaysAvailable, "List Hitag µ trace history" }, + {"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("General") " ------------------------" }, + {"reader", CmdLFHitagUReader, IfPm3Hitag, "Act like a Hitag µ reader" }, + {"rdbl", CmdLFHitagURead, IfPm3Hitag, "Read Hitag µ block" }, + {"dump", CmdLFHitagUDump, IfPm3Hitag, "Dump Hitag µ blocks to a file" }, + {"wrbl", CmdLFHitagUWrite, IfPm3Hitag, "Write Hitag µ block" }, + {"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("Simulation") " -----------------------"}, + {"sim", CmdLFHitagUSim, IfPm3Hitag, "Simulate Hitag µ transponder" }, + {NULL, NULL, 0, NULL } +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdLFHitagU(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} diff --git a/client/src/cmdlfhitagu.h b/client/src/cmdlfhitagu.h new file mode 100644 index 000000000..af2153083 --- /dev/null +++ b/client/src/cmdlfhitagu.h @@ -0,0 +1,32 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Low frequency Hitag µ support +//----------------------------------------------------------------------------- + +#ifndef CMDLFHITAGU_H__ +#define CMDLFHITAGU_H__ + +#include "common.h" +#include "hitag.h" + +uint8_t hitagu_CRC_check(uint8_t *d, uint32_t nbit); +void annotateHitagU(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response); + +int CmdLFHitagU(const char *Cmd); + +int read_htu_uid(void); + +#endif //CMDLFHITAGU_H__ diff --git a/client/src/cmdtrace.c b/client/src/cmdtrace.c index 1d0344258..d563d4478 100644 --- a/client/src/cmdtrace.c +++ b/client/src/cmdtrace.c @@ -29,6 +29,7 @@ #include "fileutils.h" // for saveFile #include "cmdlfhitag.h" // annotate hitag #include "cmdlfhitaghts.h" // annotate hitags +#include "cmdlfhitagu.h" // annotate hitagu #include "pm3_cmd.h" // tracelog_hdr_t #include "cliparser.h" // args.. @@ -586,8 +587,12 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr case PROTO_HITAG1: case PROTO_HITAGS: crcStatus = hitag1_CRC_check(frame, (data_len * 8) - ((8 - parityBytes[0]) % 8)); - case PROTO_CRYPTORF: + break; + case PROTO_HITAGU: + crcStatus = hitagu_CRC_check(frame, (data_len * 8) - ((8 - parityBytes[0]) % 8)); + break; case PROTO_HITAG2: + case PROTO_CRYPTORF: default: break; } @@ -625,6 +630,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr && protocol != PROTO_HITAG1 && protocol != PROTO_HITAG2 && protocol != PROTO_HITAGS + && protocol != PROTO_HITAGU && protocol != THINFILM && protocol != FELICA && protocol != LTO @@ -647,7 +653,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02X! ", frame[j]); } - } else if (((protocol == PROTO_HITAG1) || (protocol == PROTO_HITAG2) || (protocol == PROTO_HITAGS))) { + } else if (((protocol == PROTO_HITAG1) || (protocol == PROTO_HITAG2) || (protocol == PROTO_HITAGS) || (protocol == PROTO_HITAGU))) { if (j == 0) { @@ -803,6 +809,9 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr case PROTO_HITAGS: annotateHitagS(explanation, sizeof(explanation), frame, (data_len * 8) - ((8 - parityBytes[0]) % 8), hdr->isResponse); break; + case PROTO_HITAGU: + annotateHitagU(explanation, sizeof(explanation), frame, data_len, hdr->isResponse); + break; case ICLASS: annotateIclass(explanation, sizeof(explanation), frame, data_len, hdr->isResponse); break; @@ -1314,6 +1323,7 @@ int CmdTraceList(const char *Cmd) { "trace list -t hitag1 -> interpret as " _YELLOW_("Hitag 1") "\n" "trace list -t hitag2 -> interpret as " _YELLOW_("Hitag 2") "\n" "trace list -t hitags -> interpret as " _YELLOW_("Hitag S") "\n" + "trace list -t hitagu -> interpret as " _YELLOW_("Hitag µ") "\n" "trace list -t iclass -> interpret as " _YELLOW_("iCLASS") "\n" "trace list -t legic -> interpret as " _YELLOW_("LEGIC") "\n" "trace list -t lto -> interpret as " _YELLOW_("LTO-CM") "\n" @@ -1381,6 +1391,7 @@ int CmdTraceList(const char *Cmd) { else if (strcmp(type, "hitag1") == 0) protocol = PROTO_HITAG1; else if (strcmp(type, "hitag2") == 0) protocol = PROTO_HITAG2; else if (strcmp(type, "hitags") == 0) protocol = PROTO_HITAGS; + else if (strcmp(type, "hitagu") == 0) protocol = PROTO_HITAGU; else if (strcmp(type, "iclass") == 0) protocol = ICLASS; else if (strcmp(type, "legic") == 0) protocol = LEGIC; else if (strcmp(type, "lto") == 0) protocol = LTO; @@ -1470,8 +1481,8 @@ int CmdTraceList(const char *Cmd) { if (protocol == ISO_7816_4) PrintAndLogEx(INFO, _YELLOW_("ISO7816-4 / Smartcard") " - Timings n/a"); - if (protocol == PROTO_HITAG1 || protocol == PROTO_HITAG2 || protocol == PROTO_HITAGS) { - PrintAndLogEx(INFO, _YELLOW_("Hitag 1 / Hitag 2 / Hitag S") " - Timings in ETU (8us)"); + if (protocol == PROTO_HITAG1 || protocol == PROTO_HITAG2 || protocol == PROTO_HITAGS || protocol == PROTO_HITAGU) { + PrintAndLogEx(INFO, _YELLOW_("Hitag 1 / Hitag 2 / Hitag S / Hitag µ") " - Timings in ETU (8us)"); } if (protocol == PROTO_FMCOS20) { @@ -1552,7 +1563,7 @@ int CmdTraceList(const char *Cmd) { } // reset hitag state machine - if (protocol == PROTO_HITAG1 || protocol == PROTO_HITAG2 || protocol == PROTO_HITAGS) { + if (protocol == PROTO_HITAG1 || protocol == PROTO_HITAG2 || protocol == PROTO_HITAGS || protocol == PROTO_HITAGU) { annotateHitag2_init(); } diff --git a/common/crc16.c b/common/crc16.c index 812cd3481..e55b20bdc 100644 --- a/common/crc16.c +++ b/common/crc16.c @@ -142,12 +142,17 @@ uint16_t update_crc16(uint16_t crc, uint8_t c) { } // two ways. msb or lsb loop. -uint16_t Crc16(uint8_t const *d, size_t length, uint16_t remainder, uint16_t polynomial, bool refin, bool refout) { - if (length == 0) +uint16_t Crc16(uint8_t const *d, size_t bitlength, uint16_t remainder, uint16_t polynomial, bool refin, bool refout) { + if (bitlength == 0) return (~remainder); - for (uint32_t i = 0; i < length; ++i) { - uint8_t c = d[i]; + uint8_t offset = 8 - (bitlength % 8); + // front padding with 0s won't change the CRC result + uint8_t prebits = 0; + for (uint32_t i = 0; i < (bitlength + 7) / 8; ++i) { + uint8_t c = prebits | d[i] >> offset; + prebits = d[i] << (8 - offset); + if (refin) c = reflect8(c); // xor in at msb diff --git a/common/crc16.h b/common/crc16.h index 80758d95b..c8aecaf21 100644 --- a/common/crc16.h +++ b/common/crc16.h @@ -47,7 +47,7 @@ typedef enum { uint16_t update_crc16_ex(uint16_t crc, uint8_t c, uint16_t polynomial); uint16_t update_crc16(uint16_t crc, uint8_t c); -uint16_t Crc16(uint8_t const *d, size_t length, uint16_t remainder, uint16_t polynomial, bool refin, bool refout); +uint16_t Crc16(uint8_t const *d, size_t bitlength, uint16_t remainder, uint16_t polynomial, bool refin, bool refout); uint16_t Crc16ex(CrcType_t ct, const uint8_t *d, size_t n); void compute_crc(CrcType_t ct, const uint8_t *d, size_t n, uint8_t *first, uint8_t *second); diff --git a/doc/magic_cards_notes.md b/doc/magic_cards_notes.md index e103b1188..a1d4e7350 100644 --- a/doc/magic_cards_notes.md +++ b/doc/magic_cards_notes.md @@ -155,14 +155,20 @@ This is the cheapest and most common ID82xx chip available. It is usually sold a #### Characteristics -* Chip is likely a cut down version of Hitag μ (micro) clone +* Chip is likely a cut down version of Hitag µ (micro) clone * UID `00 00 00 00 00 00` * Password protection (4b), usually "00000000"(default) or "9AC4999C"(FURUI) -* CON0 - * bit 0-1 -> data rate ’00’... 2kbit/s ’01’... 4kbit/s ’10’... 8kbit/s ’11’... 2kbit/s - * bit 2 when set, fixed to MC 2kbit/s - * bit 3-6 reversed? all blocks always read without password and write with password - * bit 7 -> enable TTF +* Config block 0xFF + * Byte0 + * bit 0-1 : Data Rate. ’00’ -> 2kbit/s, ’01’ -> 4kbit/s, ’10’ -> 8kbit/s, ’11’ -> 2kbit/s + * bit 2 : 1 -> fixed to 2kbit/s + * bit 3 : 0 -> Manchester, 1 -> Bi-phase + * bit 4 : TTF blocks. 0 -> "Block 0, Block 1, Block 2, Block 3", 1 -> "Block 0, Block 1" + * bit 5-6 : reversed? all blocks always read without password and write with password + * bit 7 : 1 -> enable TTF + * Byte1 only bit 0 changable + * Byte2 fixed 0x00 + * Byte3 only higher nibble changable * Currently unimplemented in proxmark3 client * Other names: * ID8210 (CN) diff --git a/include/hitag.h b/include/hitag.h index 330ce8ddf..efc854d90 100644 --- a/include/hitag.h +++ b/include/hitag.h @@ -13,7 +13,7 @@ // // See LICENSE.txt for the text of the license. //----------------------------------------------------------------------------- -// Hitag 2, Hitag S +// Hitag 2, Hitag S, Hitag µ //----------------------------------------------------------------------------- @@ -39,6 +39,26 @@ #define HITAGS_UID_PADR 0 #define HITAGS_CONFIG_PADR 1 +// Add Hitag µ specific definitions +#define HITAGU_UID_SIZE 6 +#define HITAGU_BLOCK_SIZE HITAG_BLOCK_SIZE +#define HITAGU_MAX_BLOCKS 0x100 +#define HITAGU_MAX_BYTE_SIZE (HITAGU_MAX_BLOCKS * HITAGU_BLOCK_SIZE) +#define HITAGU_CONFIG_PADR 0xFF +#define HITAGU_PASSWORD_PADR 0xFE + +// Hitag µ IC Revision (ICR) values +#define HITAGU_ICR_STANDARD 0x10 // Standard Hitag µ +#define HITAGU_ICR_ADVANCED 0x20 // Hitag µ advanced +#define HITAGU_ICR_ADVANCED_PLUS 0x30 // Hitag µ advanced+ +#define HITAGU_ICR_8265 0x80 // 8265 + +// Hitag µ memory sizes based on ICR +#define HITAGU_MAX_PAGE_STANDARD 0x04 // 4 blocks (0x00-0x03) for standard Hitag µ +#define HITAGU_MAX_PAGE_ADVANCED 0x10 // 16 blocks (0x00-0x0F) for Hitag µ advanced +#define HITAGU_MAX_PAGE_ADVANCED_PLUS 0x37 // 56 blocks (0x00-0x36) for Hitag µ advanced+ +#define HITAGU_MAX_PAGE_8265 0x0F // 15 blocks (0x00-0x0E) for 8265 + // need to see which limits these cards has #define HITAG1_MAX_BYTE_SIZE 64 #define HITAG_MAX_BYTE_SIZE (64 * HITAG_BLOCK_SIZE) @@ -58,18 +78,24 @@ typedef enum { HTSF_82xx, HTSF_CHALLENGE, HTSF_KEY, - HTS_LAST_CMD = HTSF_KEY, + HTS_LAST_CMD = HTSF_KEY, HT1F_PLAIN, HT1F_AUTHENTICATE, - HT1_LAST_CMD = HT1F_AUTHENTICATE, + HT1_LAST_CMD = HT1F_AUTHENTICATE, HT2F_PASSWORD, HT2F_AUTHENTICATE, HT2F_CRYPTO, HT2F_TEST_AUTH_ATTEMPTS, HT2F_UID_ONLY, - HT2_LAST_CMD = HT2F_UID_ONLY, + HT2_LAST_CMD = HT2F_UID_ONLY, + + // Add Hitag µ commands + HTUF_PLAIN, + HTUF_82xx, + HTUF_PASSWORD, + HTU_LAST_CMD = HTUF_PASSWORD, } PACKED hitag_function; //--------------------------------------------------------- @@ -150,6 +176,57 @@ struct hitagS_tag { } PACKED; +// Configuration byte 0 bit definitions +#define HITAGU_BYTE0_DATARATE_MASK 0x03 // Bits 0-1: data rate +#define HITAGU_BYTE0_DATARATE_2K 0x00 // 00 = 2kbit/s +#define HITAGU_BYTE0_DATARATE_4K 0x01 // 01 = 4kbit/s +#define HITAGU_BYTE0_DATARATE_8K 0x02 // 10 = 8kbit/s +#define HITAGU_BYTE0_ENCODING_MASK 0x04 // Bit 2: encoding +#define HITAGU_BYTE0_ENCODING_MANCHESTER 0x00 // 0 = Manchester +#define HITAGU_BYTE0_ENCODING_BIPHASE 0x01 // 1 = Biphase + +// Hitag µ configuration structure +typedef struct { + // byte0 + uint8_t datarate: 2; + uint8_t encoding: 1; + uint8_t pwdW0_127: 1; + uint8_t pwdW128_511: 1; + uint8_t pwdW512_max: 1; + uint8_t pwdRW512_max: 1; +} PACKED hitagu_config_t; + +typedef struct { + // byte0 + uint8_t datarate : 2; // 00 = 2kbit/s, 01 = 4kbit/s, 10 = 8kbit/s, 11 = 2kbit/s + uint8_t datarate_override : 1; // 0 = datarate, 1 = 2kbit/s + uint8_t encoding : 1; // 0 = Manchester, 1 = Biphase + + uint8_t reserved : 1; + uint8_t ttf_mode : 2; // 00/10/11 = "Block 0, Block 1, Block 2, Block 3", 01 = "Block 0, Block 1" + uint8_t ttf : 1; +} PACKED hitagu82xx_config_t; + +// Hitag µ tag structure +struct hitagU_tag { + PSTATE pstate; // protocol-state + TSATE tstate; // tag-state + + int max_page; + uint8_t uid[HITAGU_UID_SIZE]; + union { + uint8_t asBytes[HITAGU_BLOCK_SIZE]; + hitagu_config_t s; + hitagu82xx_config_t s82xx; + } config; + uint8_t password[HITAG_PASSWORD_SIZE]; + uint8_t icr; // IC Revision value - determines memory size + + union { + uint8_t pages[HITAGU_MAX_BLOCKS][HITAGU_BLOCK_SIZE]; + } data; +} PACKED; + typedef struct { hitag_function cmd; uint8_t page; @@ -170,6 +247,9 @@ typedef struct { // Hitag S section uint8_t mode; + + // Hitag µ section + uint8_t uid[HITAGU_UID_SIZE]; } PACKED lf_hitag_data_t; typedef struct { @@ -185,4 +265,18 @@ typedef struct { int8_t pages_reason[HITAGS_MAX_PAGES]; uint8_t pages[HITAGS_MAX_PAGES][HITAGS_PAGE_SIZE]; } PACKED lf_hts_read_response_t; + +// Hitag µ read response structure +typedef struct { + union { + uint8_t asBytes[HITAGU_BLOCK_SIZE]; + hitagu_config_t s; + hitagu82xx_config_t s82xx; + } config_page; + uint8_t uid[HITAGU_UID_SIZE]; + uint8_t icr; // IC Revision value for memory size detection + int8_t pages_reason[HITAGU_MAX_PAGE_ADVANCED_PLUS]; + uint8_t pages[HITAGU_MAX_PAGE_ADVANCED_PLUS][HITAGU_BLOCK_SIZE]; +} PACKED lf_htu_read_response_t; + #endif diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index a71e09a1e..d23f00870 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -282,7 +282,7 @@ typedef struct { typedef struct { // 64KB SRAM -> 524288 bits(max sample num) < 2^30 -uint32_t samples : + uint32_t samples : LF_SAMPLES_BITS; bool realtime : 1; bool verbose : 1; @@ -602,6 +602,12 @@ typedef struct { #define CMD_LF_HITAGS_WRITE 0x0375 #define CMD_LF_HITAGS_UID 0x037A +// For Hitag µ +#define CMD_LF_HITAGU_READ 0x037B +#define CMD_LF_HITAGU_WRITE 0x037C +#define CMD_LF_HITAGU_SIMULATE 0x037D +#define CMD_LF_HITAGU_UID 0x037E + #define CMD_LF_HITAG_ELOAD 0x0376 #define CMD_HF_ISO14443A_ANTIFUZZ 0x0380 diff --git a/include/protocols.h b/include/protocols.h index 7048263d5..43cb94c29 100644 --- a/include/protocols.h +++ b/include/protocols.h @@ -455,12 +455,14 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define LTO 12 #define PROTO_HITAG2 13 #define PROTO_HITAGS 14 -#define PROTO_CRYPTORF 15 -#define SEOS 16 -#define PROTO_MFPLUS 17 -#define PROTO_TEXKOM 18 -#define PROTO_XEROX 19 -#define PROTO_FMCOS20 20 +#define PROTO_HITAGU 15 +#define PROTO_CRYPTORF 16 +#define SEOS 17 +#define PROTO_MFPLUS 18 +#define PROTO_TEXKOM 19 +#define PROTO_XEROX 20 +#define PROTO_FMCOS20 21 +#define COUNT_OF_PROTOCOLS 22 // Picopass fuses #define FUSE_FPERS 0x80 @@ -948,6 +950,25 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define HITAGS_WRITE_BLOCK 0x90 // 1001 WRITE BLOCK #define HITAGS_QUIET 0x70 // 0111 QUIET +// Hitag µ flags +#define HITAGU_FLAG_PEXT 0x01 // 0b00001 - Protocol EXTension flag +#define HITAGU_FLAG_INV 0x02 // 0b00010 - INVentory flag +#define HITAGU_FLAG_CRCT 0x04 // 0b00100 - CRC Transponder flag +#define HITAGU_FLAG_SEL 0x08 // 0b01000 - SELect flag (when INV=0) +#define HITAGU_FLAG_ADR 0x10 // 0b10000 - ADdRess flag (when INV=0) +#define HITAGU_FLAG_RFU 0x08 // 0b01000 - Reserved For Use flag (when INV=1, always 0) +#define HITAGU_FLAG_NOS 0x10 // 0b10000 - Number Of Slots flag (when INV=1) + +// Hitag µ commands (6-bit) +#define HITAGU_CMD_LOGIN 0x28 // 0b101000 - Login command +#define HITAGU_CMD_INVENTORY 0x00 // 0b000000 - Inventory command +#define HITAGU_CMD_READ_MULTIPLE_BLOCK 0x12 // 0b010010 - Read multiple block command +#define HITAGU_CMD_WRITE_SINGLE_BLOCK 0x14 // 0b010100 - Write single block command +#define HITAGU_CMD_SELECT 0x18 // 0b011000 - Select command +#define HITAGU_CMD_SYSINFO 0x17 // 0b010111 - Get system information command +#define HITAGU_CMD_READ_UID 0x02 // 0b000010 - Read UID command +#define HITAGU_CMD_STAY_QUIET 0x01 // 0b000001 - Stay quiet command + // LTO-CM commands #define LTO_REQ_STANDARD 0x45 #define LTO_REQ_ALL 0x4A