diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c9d28547..ca8053022 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 pretty Hitag S config parsing (@CiRIP) +- Moved Hitag S operations into separate submenu: `lf hitag s` (@CiRIP) - Added MFC keys for Sofia public transport cards (@user890104) - Added `lf em 410x clone --hs` clone EM410x ID to Hitag S/8211 (@douniwan5788) - Fixed Hitag S read/write in plain mode (@douniwan5788) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 4169b8b57..c2ce3d125 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -375,6 +375,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdlfguard.c ${PM3_ROOT}/client/src/cmdlfhid.c ${PM3_ROOT}/client/src/cmdlfhitag.c + ${PM3_ROOT}/client/src/cmdlfhitaghts.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 9cd65bf83..2da13c683 100644 --- a/client/Makefile +++ b/client/Makefile @@ -659,6 +659,7 @@ SRCS = mifare/aiddesfire.c \ cmdlfgallagher.c \ cmdlfhid.c \ cmdlfhitag.c \ + cmdlfhitaghts.c \ cmdlfidteck.c \ cmdlfindala.c \ cmdlfio.c \ diff --git a/client/experimental_lib/CMakeLists.txt b/client/experimental_lib/CMakeLists.txt index 2b665414a..c71b68156 100644 --- a/client/experimental_lib/CMakeLists.txt +++ b/client/experimental_lib/CMakeLists.txt @@ -376,6 +376,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdlfguard.c ${PM3_ROOT}/client/src/cmdlfhid.c ${PM3_ROOT}/client/src/cmdlfhitag.c + ${PM3_ROOT}/client/src/cmdlfhitaghts.c ${PM3_ROOT}/client/src/cmdlfidteck.c ${PM3_ROOT}/client/src/cmdlfindala.c ${PM3_ROOT}/client/src/cmdlfio.c diff --git a/client/src/cmdlfhitag.c b/client/src/cmdlfhitag.c index 5f2b5468f..24b2c3fa4 100644 --- a/client/src/cmdlfhitag.c +++ b/client/src/cmdlfhitag.c @@ -31,6 +31,7 @@ #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); @@ -2477,6 +2478,7 @@ static int CmdLFHitag2Selftest(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdLFHitagList, AlwaysAvailable, "List Hitag trace history"}, + {"hts", CmdLFHitagS, AlwaysAvailable, "{ Hitag S/8211 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/cmdlfhitaghts.c b/client/src/cmdlfhitaghts.c new file mode 100644 index 000000000..0ecf44a5b --- /dev/null +++ b/client/src/cmdlfhitaghts.c @@ -0,0 +1,346 @@ +//----------------------------------------------------------------------------- +// 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 S support +//----------------------------------------------------------------------------- + +#include "cmdlfhitaghts.h" +#include +#include "cmdparser.h" // command_t +#include "comms.h" +#include "cmdtrace.h" +#include "commonutil.h" +#include "hitag.h" +#include "fileutils.h" // savefile +#include "protocols.h" // defines +#include "cliparser.h" +#include "crc.h" +#include "graph.h" // MAX_GRAPH_TRACE_LEN +#include "lfdemod.h" +#include "cmddata.h" // setDemodBuff +#include "pm3_cmd.h" // return codes +#include "hitag2/hitag2_crypto.h" +#include "util_posix.h" // msclock + +static int CmdHelp(const char *Cmd); + + +static int CmdLFHitagSRead(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag hts read", + "Read Hitag S memory.\n\n" + " Crypto mode: \n" + " - key format ISK high + ISK low\n" + " - default key 4F4E4D494B52 (ONMIKR)\n", + " lf hitag s read -> Hitag S, plain mode\n" + " lf hitag hts read --nrar 0102030411223344 -> Hitag S, challenge mode\n" + " lf hitag hts read --crypto -> Hitag S, crypto mode, def key\n" + " lf hitag hts read -k 4F4E4D494B52 -> Hitag S, crypto mode\n\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0(NULL, "nrar", "", "nonce / answer writer, 8 hex bytes"), + arg_lit0(NULL, "crypto", "crypto mode"), + arg_str0("k", "key", "", "key, 4 or 6 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool use_plain = false; + + uint8_t nrar[8]; + int nrar_len = 0; + + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), nrar, sizeof(nrar), &nrar_len); + if (res != 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + bool use_nrar = nrar_len > 0; + bool use_crypto = arg_get_lit(ctx, 2); + + uint8_t key[6]; + int key_len = 0; + + res = CLIParamHexToBuf(arg_get_str(ctx, 3), key, sizeof(key), &key_len); + if (res != 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + CLIParserFree(ctx); + + if (key_len && key_len != HITAGS_CRYPTOKEY_SIZE) { + PrintAndLogEx(WARNING, "Wrong KEY len expected %d, got %d", HITAGS_CRYPTOKEY_SIZE, key_len); + return PM3_EINVARG; + } + + if (nrar_len && nrar_len != HITAGS_NRAR_SIZE) { + PrintAndLogEx(WARNING, "Wrong NR/AR len expected %d, got %d", HITAGS_NRAR_SIZE, nrar_len); + return PM3_EINVARG; + } + + if (!key_len && use_crypto) { + memcpy(key, "ONMIKR", 6); + key_len = 6; + } + + // check coherence + uint8_t auth_methods = (use_plain + use_nrar + use_crypto); + if (auth_methods > 1) { + PrintAndLogEx(WARNING, "Specify only one authentication mode"); + return PM3_EINVARG; + } else if (auth_methods == 0) { + use_plain = true; + } + + lf_hitag_data_t packet; + memset(&packet, 0, sizeof(packet)); + + int pm3cmd = CMD_LF_HITAGS_READ; + + if (use_nrar) { + packet.cmd = RHTSF_CHALLENGE; + memcpy(packet.NrAr, nrar, sizeof(packet.NrAr)); + } + + if (use_crypto) { + packet.cmd = RHTSF_KEY; + memcpy(packet.key, key, sizeof(packet.key)); + } + + clearCommandBuffer(); + SendCommandNG(pm3cmd, (uint8_t *) &packet, sizeof(packet)); + + PacketResponseNG resp; + if (WaitForResponseTimeout(pm3cmd, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(DEBUG, "DEBUG: Error - hitag failed"); + return PM3_ESOFT; + } + + // ?? + if (use_nrar) { + return PM3_SUCCESS; + } + + uint8_t *data = resp.data.asBytes; + + hitags_config_t config = hitags_config_unpack(data + HITAGS_PAGE_SIZE); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); + + hitags_config_print(config); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Data") " ----------------------------------"); + + uint32_t size = (const int[]) {4, 32, 256, 0}[config.memory_type]; + + print_hex_break(data, size, HITAGS_PAGE_SIZE); + + return PM3_SUCCESS; +} + +static int CmdLFHitagSWrite(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag hts write", + "Write a page in Hitag S memory.\n" + " Crypto mode: \n" + " - key format ISK high + ISK low\n" + " - default key 4F4E4D494B52 (ONMIKR)\n", + " lf hitag write -p 6 -d 01020304 -> Hitag S, plain mode\n" + " lf hitag hts write -p 6 -d 01020304 --nrar 0102030411223344 -> Hitag S, challenge mode\n" + " lf hitag hts write -p 6 -d 01020304 --crypto -> Hitag S, crypto mode, default key\n" + " lf hitag hts write -p 6 -d 01020304 -k 4F4E4D494B52 -> Hitag S, crypto mode\n\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0(NULL, "nrar", "", "nonce / answer writer, 8 hex bytes"), + arg_lit0(NULL, "crypto", "crypto mode"), + arg_str0("k", "key", "", "key, 6 hex bytes"), + arg_int1("p", "page", "", "page address to write to"), + arg_str1("d", "data", "", "data, 4 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + bool use_plain = false; + + uint8_t nrar[8]; + int nrar_len = 0; + + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), nrar, sizeof(nrar), &nrar_len); + if (res != 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + bool use_nrar = nrar_len > 0; + bool use_crypto = arg_get_lit(ctx, 2); + + uint8_t key[6]; + int key_len = 0; + + res = CLIParamHexToBuf(arg_get_str(ctx, 3), key, sizeof(key), &key_len); + if (res != 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + int page = arg_get_int_def(ctx, 4, 0); + + uint8_t data[4]; + int data_len = 0; + + res = CLIParamHexToBuf(arg_get_str(ctx, 5), data, sizeof(data), &data_len); + if (res != 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + CLIParserFree(ctx); + + if (key_len && key_len != HITAGS_CRYPTOKEY_SIZE) { + PrintAndLogEx(WARNING, "Wrong KEY len expected %d, got %d", HITAGS_CRYPTOKEY_SIZE, key_len); + return PM3_EINVARG; + } + + if (nrar_len && nrar_len != HITAGS_NRAR_SIZE) { + PrintAndLogEx(WARNING, "Wrong NR/AR len expected %d, got %d", HITAGS_NRAR_SIZE, nrar_len); + return PM3_EINVARG; + } + + if (!key_len && use_crypto) { + memcpy(key, "ONMIKR", 6); + key_len = 6; + } + + // check coherence + uint8_t auth_methods = (use_plain + use_nrar + use_crypto); + if (auth_methods > 1) { + PrintAndLogEx(WARNING, "Specify only one authentication mode"); + return PM3_EINVARG; + } else if (auth_methods == 0) { + use_plain = true; + } + + lf_hitag_data_t packet; + memset(&packet, 0, sizeof(packet)); + + packet.page = page; + memcpy(packet.data, data, sizeof(data)); + + if (use_nrar) { + packet.cmd = WHTSF_CHALLENGE; + memcpy(packet.NrAr, nrar, sizeof(packet.NrAr)); + } + + if (use_crypto) { + packet.cmd = WHTSF_KEY; + memcpy(packet.key, key, sizeof(packet.key)); + } + + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *) &packet, sizeof(packet)); + + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_LF_HITAGS_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) { + PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )"); + return resp.status; + } + + PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); + return PM3_SUCCESS; +} + +static int CmdLFHitagSList(const char *Cmd) { + return CmdTraceListAlias(Cmd, "lf hitag hts", "hitags"); +} + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"list", CmdLFHitagSList, AlwaysAvailable, "List Hitag S trace history"}, + {"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_( + "General") " ------------------------"}, + {"read", CmdLFHitagSRead, IfPm3Hitag, "Read Hitag S memory"}, + {"write", CmdLFHitagSWrite, IfPm3Hitag, "Write Hitag S page"}, + {NULL, NULL, 0, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void) Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdLFHitagS(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} + +hitags_config_t hitags_config_unpack(const uint8_t *config_bytes) { + hitags_config_t result = { + .memory_type = (config_bytes[0] >> 0) & 0x03, + .authentication = (config_bytes[1] >> 7) & 0x01, + .ttf_coding = (config_bytes[1] >> 6) & 0x01, + .ttf_data_rate = (config_bytes[1] >> 4) & 0x03, + .ttf_mode = (config_bytes[1] >> 2) & 0x03, + .lock_config = (config_bytes[1] >> 1) & 0x01, + .lock_key = (config_bytes[1] >> 0) & 0x01 + }; + return result; +} + +void hitags_config_print(hitags_config_t config) { + PrintAndLogEx(INFO, " Memory type...... " _GREEN_("%s"), + (const char *[]) {"Hitag S 32", "Hitag S 256", "Hitag S 2048", + "Unknown Hitag S/8211"}[config.memory_type]); + + PrintAndLogEx(INFO, " Authenticaion.... %s", config.authentication ? _YELLOW_("Yes") : "No"); + + PrintAndLogEx(INFO, " TTF coding....... %s", + (const char *[]) {"Manchester", "Biphase"}[config.ttf_coding]); + + PrintAndLogEx(INFO, " TTF data rate.... %s", + (const char *[]) {"4 kBit", "8 kBit", "2 kBit", + "2 kBit and Pigeon Race Standard"}[config.ttf_data_rate]); + + PrintAndLogEx(INFO, " TTF mode......... %s", + (const char *[]) {"TTF Mode disabled (= RTF Mode)", "Page 4, Page 5", + "Page 4, Page 5, Page 6, Page 7", "Page 4"}[config.ttf_mode]); + + PrintAndLogEx(INFO, " Config locked.... %s", config.lock_config ? _RED_("Yes") : _GREEN_("No")); + PrintAndLogEx(INFO, " Key/PWD locked... %s", config.lock_key ? _RED_("Yes") : _GREEN_("No")); +} diff --git a/client/src/cmdlfhitaghts.h b/client/src/cmdlfhitaghts.h new file mode 100644 index 000000000..7849d5f85 --- /dev/null +++ b/client/src/cmdlfhitaghts.h @@ -0,0 +1,65 @@ +//----------------------------------------------------------------------------- +// 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 S support +//----------------------------------------------------------------------------- + +#ifndef CMDLFHITAGS_H__ +#define CMDLFHITAGS_H__ + +#include "common.h" + +typedef struct { + enum { + HITAGS_MEMORY_32, + HITAGS_MEMORY_256, + HITAGS_MEMORY_2048, + HITAGS_MEMORY_UNKNOWN + } memory_type; + + bool authentication; + + enum { + HITAGS_CODING_MANCHESTER, + HITAGS_CODING_BIPHASE + } ttf_coding; + + enum { + HITAGS_DR_4KBIT, + HITAGS_DR_8KBIT, + HITAGS_DR_2KBIT, + HITAGS_DR_2KBIT_PIGEON + } ttf_data_rate; + + enum { + HITAGS_TTF_DISABLED, + HITAGS_TTF_PAGE45, + HITAGS_TTF_PAGE4567, + HITAGS_TTF_PAGE4 + } ttf_mode; + + bool lock_config; + bool lock_key; +} hitags_config_t; + +int CmdLFHitagS(const char *Cmd); + +hitags_config_t hitags_config_unpack(const uint8_t *config_bytes); + +void hitags_config_pack(hitags_config_t config, uint8_t *out); + +void hitags_config_print(hitags_config_t config); + +#endif //CMDLFHITAGS_H__ diff --git a/include/hitag.h b/include/hitag.h index 7e0219276..aa8ad7c9c 100644 --- a/include/hitag.h +++ b/include/hitag.h @@ -30,9 +30,16 @@ #define HITAG2_MAX_BLOCKS 8 #define HITAG2_MAX_BYTE_SIZE (HITAG2_MAX_BLOCKS * HITAG_BLOCK_SIZE) +#define HITAGS_NRAR_SIZE 8 +#define HITAGS_CRYPTOKEY_SIZE 6 +#define HITAGS_UID_SIZE 4 +#define HITAGS_PAGE_SIZE 4 +#define HITAGS_BLOCK_SIZE 4 +#define HITAGS_MAX_PAGES 64 +#define HITAGS_MAX_BYTE_SIZE (HITAGS_MAX_PAGES * HITAGS_PAGE_SIZE) + // need to see which limits these cards has #define HITAG1_MAX_BYTE_SIZE 64 -#define HITAGS_MAX_BYTE_SIZE 64 #define HITAGU_MAX_BYTE_SIZE 64 #define HITAG_MAX_BYTE_SIZE (64 * HITAG_BLOCK_SIZE)