diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 5165b5cad..65453f24b 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -36,9 +36,9 @@ #include "protocols.h" #include "generator.h" #include "desfire_crypto.h" // UL-C authentication helpers +#include "mifare.h" // for iso14a_polling_frame_t structure #define MAX_ISO14A_TIMEOUT 524288 - // this timeout is in MS static uint32_t iso14a_timeout; @@ -141,7 +141,7 @@ Default HF 14a config is set to: forcecl3 = 0 (auto) forcerats = 0 (auto) */ -static hf14a_config hf14aconfig = { 0, 0, 0, 0, 0 } ; +static hf14a_config hf14aconfig = { 0, 0, 0, 0, 0, {{0}, 0, 0, 0} }; // Polling frames and configurations @@ -166,31 +166,37 @@ struct Crypto1State crypto1_state = {0, 0}; void printHf14aConfig(void) { DbpString(_CYAN_("HF 14a config")); - Dbprintf(" [a] Anticol override.... %s%s%s", + Dbprintf(" [a] Anticol override.............. %s%s%s", (hf14aconfig.forceanticol == 0) ? _GREEN_("std") " ( follow standard )" : "", (hf14aconfig.forceanticol == 1) ? _RED_("force") " ( always do anticol )" : "", (hf14aconfig.forceanticol == 2) ? _RED_("skip") " ( always skip anticol )" : "" ); - Dbprintf(" [b] BCC override........ %s%s%s", + Dbprintf(" [b] BCC override.................. %s%s%s", (hf14aconfig.forcebcc == 0) ? _GREEN_("std") " ( follow standard )" : "", (hf14aconfig.forcebcc == 1) ? _RED_("fix") " ( fix bad BCC )" : "", (hf14aconfig.forcebcc == 2) ? _RED_("ignore") " ( ignore bad BCC, always use card BCC )" : "" ); - Dbprintf(" [2] CL2 override........ %s%s%s", + Dbprintf(" [2] CL2 override.................. %s%s%s", (hf14aconfig.forcecl2 == 0) ? _GREEN_("std") " ( follow standard )" : "", (hf14aconfig.forcecl2 == 1) ? _RED_("force") " ( always do CL2 )" : "", (hf14aconfig.forcecl2 == 2) ? _RED_("skip") " ( always skip CL2 )" : "" ); - Dbprintf(" [3] CL3 override........ %s%s%s", + Dbprintf(" [3] CL3 override.................. %s%s%s", (hf14aconfig.forcecl3 == 0) ? _GREEN_("std") " ( follow standard )" : "", (hf14aconfig.forcecl3 == 1) ? _RED_("force") " ( always do CL3 )" : "", (hf14aconfig.forcecl3 == 2) ? _RED_("skip") " ( always skip CL3 )" : "" ); - Dbprintf(" [r] RATS override....... %s%s%s", + Dbprintf(" [r] RATS override................. %s%s%s", (hf14aconfig.forcerats == 0) ? _GREEN_("std") " ( follow standard )" : "", (hf14aconfig.forcerats == 1) ? _RED_("force") " ( always do RATS )" : "", (hf14aconfig.forcerats == 2) ? _RED_("skip") " ( always skip RATS )" : "" ); + Dbprintf(" [p] Polling loop annotation....... %s %*D", + (hf14aconfig.polling_loop_annotation.frame_length <= 0) ? _YELLOW_("disabled") : _GREEN_("enabled"), + hf14aconfig.polling_loop_annotation.frame_length, + hf14aconfig.polling_loop_annotation.frame, + "" + ); } /** @@ -213,6 +219,13 @@ void setHf14aConfig(const hf14a_config *hc) { hf14aconfig.forcecl3 = hc->forcecl3; if ((hc->forcerats >= 0) && (hc->forcerats <= 2)) hf14aconfig.forcerats = hc->forcerats; + + if (hc->polling_loop_annotation.frame_length > 0) { + memcpy(&hf14aconfig.polling_loop_annotation, &hc->polling_loop_annotation, sizeof(iso14a_polling_frame_t)); + } else if (hc->polling_loop_annotation.frame_length < 0) { + // Reset if set to empty + hf14aconfig.polling_loop_annotation.frame_length = 0; + } } hf14a_config *getHf14aConfig(void) { @@ -448,7 +461,6 @@ RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) { Uart.parityBits |= ((Uart.shiftReg >> 8) & 0x01); // store parity bit Uart.bitCount = 0; Uart.shiftReg = 0; - // Every 8 data bytes, store 8 parity bits into a parity byte if ((Uart.len & 0x0007) == 0) { // every 8 data bytes Uart.parity[Uart.parityLen++] = Uart.parityBits; // store 8 parity bits @@ -592,7 +604,7 @@ RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_t if (Demod.bitCount > 0) { // there are some remaining data bits Demod.shiftReg >>= (9 - Demod.bitCount); // right align the decoded bits - Demod.output[Demod.len++] = Demod.shiftReg & 0xff; // and add them to the output + Demod.output[Demod.len++] = (Demod.shiftReg & 0xff); // and add them to the output Demod.parityBits <<= 1; // add a (void) parity bit Demod.parityBits <<= (8 - (Demod.len & 0x0007)); // left align remaining parity bits Demod.parity[Demod.parityLen++] = Demod.parityBits; // and store them @@ -1421,7 +1433,6 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, // "precompiled" responses. // These exist for speed reasons. There are no time in the anti collision phase to calculate responses. // There are 12 predefined responses with a total of 84 bytes data to transmit. - // // Coded responses need one byte per bit to transfer (data, parity, start, stop, correction) // 85 * 8 data bits, 85 * 1 parity bits, 12 start bits, 12 stop bits, 12 correction bits // 85 * 8 + 85 + 12 + 12 + 12 == 801 @@ -2702,16 +2713,37 @@ static int GetATQA(uint8_t *resp, uint16_t resp_len, uint8_t *resp_par, iso14a_p uint32_t save_iso14a_timeout = iso14a_get_timeout(); iso14a_set_timeout(1236 / 128 + 1); // response to WUPA is expected at exactly 1236/fc. No need to wait longer. + + // Create temporary polling parameters structure that might include both standard and custom frames + iso14a_polling_parameters_t temp_params; + memcpy(&temp_params, polling_parameters, sizeof(iso14a_polling_parameters_t)); + + // If we have a custom polling frame annotation, add it to the temporary structure + if (hf14aconfig.polling_loop_annotation.frame_length > 0) { + // Only add if we have space in the frames array + if (temp_params.frame_count < ARRAYLEN(temp_params.frames)) { + // Add the custom frame at the end of the frames array + memcpy(&temp_params.frames[temp_params.frame_count], + &hf14aconfig.polling_loop_annotation, + sizeof(iso14a_polling_frame_t)); + temp_params.frame_count++; + } + + // Increase timeout if polling loop annotation is provided, as target may respond slower + if (temp_params.extra_timeout == 0) { + temp_params.extra_timeout = 250; + } + } bool first_try = true; - uint32_t retry_timeout = WUPA_RETRY_TIMEOUT * polling_parameters->frame_count + polling_parameters->extra_timeout; - uint32_t start_time = 0; int len; - + uint32_t retry_timeout = WUPA_RETRY_TIMEOUT * temp_params.frame_count + temp_params.extra_timeout; + uint32_t start_time = 0; uint8_t current_frame = 0; - + + // Use the temporary polling parameters do { - iso14a_polling_frame_t *frame_parameters = &polling_parameters->frames[current_frame]; + iso14a_polling_frame_t *frame_parameters = &temp_params.frames[current_frame]; if (frame_parameters->last_byte_bits == 8) { ReaderTransmit(frame_parameters->frame, frame_parameters->frame_length, NULL); @@ -2729,12 +2761,11 @@ static int GetATQA(uint8_t *resp, uint16_t resp_len, uint8_t *resp_par, iso14a_p // We set the start_time here otherwise in some cases we miss the window and only ever try once if (first_try) { start_time = GetTickCount(); + first_try = false; } - first_try = false; - // Go over frame configurations, loop back when we reach the end - current_frame = current_frame < (polling_parameters->frame_count - 1) ? current_frame + 1 : 0; + current_frame = current_frame < (temp_params.frame_count - 1) ? current_frame + 1 : 0; } while (len == 0 && GetTickCountDelta(start_time) <= retry_timeout); iso14a_set_timeout(save_iso14a_timeout); diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index 3174cc47e..b38003b42 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -43,6 +43,8 @@ #include "mifare/desfirecore.h" // desfire context #include "mifare/mifaredefault.h" #include "preferences.h" // get/set device debug level +#include "pm3_cmd.h" + static bool g_apdu_in_framing_enable = true; bool Get_apdu_in_framing(void) { @@ -333,6 +335,15 @@ static int hf_14a_config_example(void) { PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --atqa force --bcc ignore --cl2 force --cl3 skip -rats skip")); PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu setuid --uid 04112233445566")); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --std")); + + PrintAndLogEx(NORMAL, "\nExamples of polling loop annotations used to enable anticollision on mobile targets:"); + PrintAndLogEx(NORMAL, _CYAN_(" ECP Express Transit EMV")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --pla 6a02c801000300027900000000")); + PrintAndLogEx(NORMAL, _CYAN_(" ECP VAS Only")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --pla 6a01000002")); + PrintAndLogEx(NORMAL, _CYAN_(" ECP Access Wildcard")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --pla 6a02c3020002ffff")); + return PM3_SUCCESS; } static int CmdHf14AConfig(const char *Cmd) { @@ -341,25 +352,26 @@ static int CmdHf14AConfig(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14a config", "Configure 14a settings (use with caution)\n" - " `-v` also prints examples for reviving Gen2 cards", - "hf 14a config -> Print current configuration\n" - "hf 14a config --std -> Reset default configuration (follow standard)\n" - "hf 14a config --atqa std -> Follow standard\n" - "hf 14a config --atqa force -> Force execution of anticollision\n" - "hf 14a config --atqa skip -> Skip anticollision\n" - "hf 14a config --bcc std -> Follow standard\n" - "hf 14a config --bcc fix -> Fix bad BCC in anticollision\n" - "hf 14a config --bcc ignore -> Ignore bad BCC and use it as such\n" - "hf 14a config --cl2 std -> Follow standard\n" - "hf 14a config --cl2 force -> Execute CL2\n" - "hf 14a config --cl2 skip -> Skip CL2\n" - "hf 14a config --cl3 std -> Follow standard\n" - "hf 14a config --cl3 force -> Execute CL3\n" - "hf 14a config --cl3 skip -> Skip CL3\n" - "hf 14a config --rats std -> Follow standard\n" - "hf 14a config --rats force -> Execute RATS\n" - "hf 14a config --rats skip -> Skip RATS"); - + " `-v` also prints examples for reviving Gen2 cards & configuring polling loop annotations", + "hf 14a config -> Print current configuration\n" + "hf 14a config --std -> Reset default configuration (follow standard)\n" + "hf 14a config --atqa std -> Follow standard\n" + "hf 14a config --atqa force -> Force execution of anticollision\n" + "hf 14a config --atqa skip -> Skip anticollision\n" + "hf 14a config --bcc std -> Follow standard\n" + "hf 14a config --bcc fix -> Fix bad BCC in anticollision\n" + "hf 14a config --bcc ignore -> Ignore bad BCC and use it as such\n" + "hf 14a config --cl2 std -> Follow standard\n" + "hf 14a config --cl2 force -> Execute CL2\n" + "hf 14a config --cl2 skip -> Skip CL2\n" + "hf 14a config --cl3 std -> Follow standard\n" + "hf 14a config --cl3 force -> Execute CL3\n" + "hf 14a config --cl3 skip -> Skip CL3\n" + "hf 14a config --rats std -> Follow standard\n" + "hf 14a config --rats force -> Execute RATS\n" + "hf 14a config --rats skip -> Skip RATS\n" + "hf 14a config --pla -> Set polling loop annotation (max 22 bytes)\n" + "hf 14a config --pla skip -> Disable polling loop annotation\n"); void *argtable[] = { arg_param_begin, arg_str0(NULL, "atqa", "", "Configure ATQA<>anticollision behavior"), @@ -367,14 +379,17 @@ static int CmdHf14AConfig(const char *Cmd) { arg_str0(NULL, "cl2", "", "Configure SAK<>CL2 behavior"), arg_str0(NULL, "cl3", "", "Configure SAK<>CL3 behavior"), arg_str0(NULL, "rats", "", "Configure RATS behavior"), + arg_str0(NULL, "pla", "", "Configure polling loop annotation"), arg_lit0(NULL, "std", "Reset default configuration: follow all standard"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - bool defaults = arg_get_lit(ctx, 6); + bool defaults = arg_get_lit(ctx, 7); + bool verbose = arg_get_lit(ctx, 8); + int vlen = 0; - char value[10]; + char value[64]; int atqa = defaults ? 0 : -1; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)value, sizeof(value), &vlen); if (vlen > 0) { @@ -436,11 +451,43 @@ static int CmdHf14AConfig(const char *Cmd) { } } - bool verbose = arg_get_lit(ctx, 7); + // Handle polling loop annotation parameter + iso14a_polling_frame_t pla = { + // -1 signals that PLA has to be disabled, 0 signals that no change has to be made + .frame_length = defaults ? -1 : 0, + .last_byte_bits = 8, + .extra_delay = 5 + }; + CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)value, sizeof(value), &vlen); + if (vlen > 0 && (strncmp((char *)value, "skip", 4) || strncmp((char *)value, "std", 3)) == 0) { + pla.frame_length = -1; + } else if (vlen > 0) { + // Convert hex string to bytes + int length = 0; + if (param_gethex_to_eol((char *)value, 0, pla.frame, sizeof(pla.frame), &length) != 0) { + PrintAndLogEx(ERR, "Error parsing polling loop annotation bytes"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + pla.frame_length = length; + + // Validate length before adding CRC + if (pla.frame_length < 1 || pla.frame_length > 22) { + PrintAndLogEx(ERR, "Polling loop annotation length invalid: min %d; max %d", 1, 22); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t first, second; + compute_crc(CRC_14443_A, pla.frame, pla.frame_length, &first, &second); + pla.frame[pla.frame_length++] = first; + pla.frame[pla.frame_length++] = second; + PrintAndLogEx(INFO, "Added CRC16A to polling loop annotation: %s", sprint_hex(pla.frame, pla.frame_length)); + } CLIParserFree(ctx); - // validations + // Handle empty command if (strlen(Cmd) == 0) { return hf14a_setconfig(NULL, verbose); } @@ -449,12 +496,14 @@ static int CmdHf14AConfig(const char *Cmd) { hf_14a_config_example(); } + // Initialize config with all parameters hf14a_config config = { .forceanticol = atqa, .forcebcc = bcc, .forcecl2 = cl2, .forcecl3 = cl3, - .forcerats = rats + .forcerats = rats, + .polling_loop_annotation = pla }; return hf14a_setconfig(&config, verbose); diff --git a/include/mifare.h b/include/mifare.h index 69665cdc8..4754a6f20 100644 --- a/include/mifare.h +++ b/include/mifare.h @@ -108,23 +108,6 @@ typedef enum ISO14A_COMMAND { ISO14A_CRYPTO1MODE = (1 << 14) } iso14a_command_t; -// Defines a frame that will be used in a polling sequence -// ECP Frames are up to (7 + 16) bytes long, 24 bytes should cover future and other cases -typedef struct { - uint8_t frame[24]; - uint8_t frame_length; - uint8_t last_byte_bits; - uint16_t extra_delay; -} PACKED iso14a_polling_frame_t; - -// Defines polling sequence configuration -// 6 would be enough for 4 magsafe, 1 wupa, 1 ecp, -typedef struct { - iso14a_polling_frame_t frames[6]; - uint8_t frame_count; - uint16_t extra_timeout; -} PACKED iso14a_polling_parameters_t; - typedef struct { uint8_t *response; uint8_t *modulation; diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 89600e8e4..2c7e86898 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -131,6 +131,27 @@ typedef struct { bool verbose; } PACKED sample_config; + +// Defines a frame that will be used in a polling sequence +// Polling loop annotations are up to (7 + 16) bytes long, 24 bytes should cover future and other cases +typedef struct { + uint8_t frame[24]; + // negative values can be used to carry special info + int8_t frame_length; + uint8_t last_byte_bits; + uint16_t extra_delay; +} PACKED iso14a_polling_frame_t; + + +// Defines polling sequence configuration +// 6 would be enough for 4 magsafe, 1 wupa, 1 pla, +typedef struct { + iso14a_polling_frame_t frames[6]; + int8_t frame_count; + uint16_t extra_timeout; +} PACKED iso14a_polling_parameters_t; + + // A struct used to send hf14a-configs over USB typedef struct { int8_t forceanticol; // 0:auto 1:force executing anticol 2:force skipping anticol @@ -138,6 +159,7 @@ typedef struct { int8_t forcecl2; // 0:auto 1:force executing CL2 2:force skipping CL2 int8_t forcecl3; // 0:auto 1:force executing CL3 2:force skipping CL3 int8_t forcerats; // 0:auto 1:force executing RATS 2:force skipping RATS + iso14a_polling_frame_t polling_loop_annotation; // Polling loop annotation } PACKED hf14a_config; // Tracelog Header struct