diff --git a/CHANGELOG.md b/CHANGELOG.md index 0db55155d..eccad5f86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ 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] + - Add `hf 14a config` to deal with badly configured cards: invalid ATQA/BCC/SAK (@doegox)" - Mikron JSC Russia Ultralight EV1 41 pages tag type support (@McEloff) - Add test for Ultralight gen2 magic 'hf search' (@McEloff) - Add test for Ultralight EV1 gen2 magic 'hf search' (@McEloff) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 53ca77793..1a62155b9 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -355,8 +355,11 @@ static void SendStatus(void) { I2C_print_status(); #endif #ifdef WITH_LF - printConfig(); // LF Sampling config + printLFConfig(); // LF Sampling config printT55xxConfig(); // LF T55XX Config +#endif +#ifdef WITH_ISO14443a + printHf14aConfig(); // HF 14a config #endif printConnSpeed(); DbpString(_CYAN_("Various")); @@ -739,7 +742,7 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_LF_SAMPLING_PRINT_CONFIG: { - printConfig(); + printLFConfig(); break; } case CMD_LF_SAMPLING_GET_CONFIG: { @@ -1058,8 +1061,8 @@ static void PacketReceived(PacketCommandNG *packet) { uint8_t uid[8]; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - SetTag15693Uid(payload->uid); - break; + SetTag15693Uid(payload->uid); + break; } #endif @@ -1145,6 +1148,21 @@ static void PacketReceived(PacketCommandNG *packet) { #endif #ifdef WITH_ISO14443a + case CMD_HF_ISO14443A_PRINT_CONFIG: { + printHf14aConfig(); + break; + } + case CMD_HF_ISO14443A_GET_CONFIG: { + hf14a_config *hf14aconfig = getHf14aConfig(); + reply_ng(CMD_HF_ISO14443A_GET_CONFIG, PM3_SUCCESS, (uint8_t *)hf14aconfig, sizeof(hf14a_config)); + break; + } + case CMD_HF_ISO14443A_SET_CONFIG: { + hf14a_config c; + memcpy(&c, packet->data.asBytes, sizeof(hf14a_config)); + setHf14aConfig(&c); + break; + } case CMD_HF_ISO14443A_SNIFF: { SniffIso14443a(packet->data.asBytes[0]); reply_ng(CMD_HF_ISO14443A_SNIFF, PM3_SUCCESS, NULL, 0); diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 97f17396a..d3563ceec 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -122,6 +122,47 @@ static uint32_t LastProxToAirDuration; #define SEC_Y 0x00 #define SEC_Z 0xc0 +/* +Default HF 14a config is set to: + forceanticol = false + obeybadbcc = false + forcecl2 = 0 (auto) + forcecl3 = 0 (auto) +*/ +static hf14a_config hf14aconfig = { 0, 0, 0, 0 } ; + +void printHf14aConfig(void) { + DbpString(_CYAN_("HF 14a config")); + Dbprintf("[a] Force std anticol.....%s", (hf14aconfig.forceanticol) ? _RED_("Yes") " (even if bad ATQA)" : _GREEN_("No")); + Dbprintf("[b] Force obey bad BCC....%s", (hf14aconfig.obeybadbcc) ? _RED_("Yes") : _GREEN_("No")); + Dbprintf("[2] Force CL2 override ...%s%s%s", (hf14aconfig.forcecl2==0) ? _GREEN_("No") : "", (hf14aconfig.forcecl2==1) ? _RED_("Yes: Always do CL2") : "", (hf14aconfig.forcecl2==-1) ? _RED_("Yes: Always skip CL2") : ""); + Dbprintf("[3] Force CL3 override ...%s%s%s", (hf14aconfig.forcecl3==0) ? _GREEN_("No") : "", (hf14aconfig.forcecl3==1) ? _RED_("Yes: Always do CL3") : "", (hf14aconfig.forcecl3==-1) ? _RED_("Yes: Always skip CL3") : ""); +} + +/** + * Called from the USB-handler to set the 14a configuration + * The 14a config is used for card selection sequence. + * + * Values set to '-1' implies no change + * @brief setSamplingConfig + * @param sc + */ +void setHf14aConfig(hf14a_config *hc) { + + if (hc->forceanticol > -1) + hf14aconfig.forceanticol = (hc->forceanticol > 0) ? 1 : 0; + if (hc->obeybadbcc > -1) + hf14aconfig.obeybadbcc = (hc->obeybadbcc > 0) ? 1 : 0; + if ((hc->forcecl2 >= -1) && (hc->forcecl2 <= 1)) + hf14aconfig.forcecl2 = hc->forcecl2; + if ((hc->forcecl3 >= -1) && (hc->forcecl3 <= 1)) + hf14aconfig.forcecl3 = hc->forcecl3; +} + +hf14a_config *getHf14aConfig(void) { + return &hf14aconfig; +} + void iso14a_set_trigger(bool enable) { g_trigger = enable; } @@ -2335,7 +2376,8 @@ int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32 uint8_t resp[MAX_FRAME_SIZE] = {0}; // theoretically. A usual RATS will be much smaller uint8_t resp_par[MAX_PARITY_SIZE] = {0}; - uint8_t sak = 0x04; // cascade uid + uint8_t sak; // cascade uid + bool do_cascade = 1; int cascade_level = 0; if (p_card) { @@ -2359,17 +2401,19 @@ int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32 memset(uid_ptr, 0, 10); } - // check for proprietary anticollision: - if ((resp[0] & 0x1F) == 0) return 3; + if ( ! hf14aconfig.forceanticol ) { + // check for proprietary anticollision: + if ((resp[0] & 0x1F) == 0) return 3; + } // OK we will select at least at cascade 1, lets see if first byte of UID was 0x88 in // which case we need to make a cascade 2 request and select - this is a long UID // While the UID is not complete, the 3nd bit (from the right) is set in the SAK. - for (; sak & 0x04; cascade_level++) { + for (; do_cascade; cascade_level++) { // SELECT_* (L1: 0x93, L2: 0x95, L3: 0x97) uint8_t sel_all[] = { ISO14443A_CMD_ANTICOLL_OR_SELECT, 0x20 }; uint8_t sel_uid[] = { ISO14443A_CMD_ANTICOLL_OR_SELECT, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t uid_resp[4] = {0}; + uint8_t uid_resp[5] = {0}; // UID + original BCC sel_uid[0] = sel_all[0] = 0x93 + cascade_level * 2; if (anticollision) { @@ -2378,7 +2422,7 @@ int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32 if (!ReaderReceive(resp, resp_par)) return 0; if (Demod.collisionPos) { // we had a collision and need to construct the UID bit by bit - memset(uid_resp, 0, 4); + memset(uid_resp, 0, 5); uint16_t uid_resp_bits = 0; uint16_t collision_answer_offset = 0; // anti-collision-loop: @@ -2390,7 +2434,7 @@ int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32 } uid_resp[uid_resp_bits / 8] |= 1 << (uid_resp_bits % 8); // next time select the card(s) with a 1 in the collision position uid_resp_bits++; - // construct anticollosion command: + // construct anticollision command: sel_uid[1] = ((2 + uid_resp_bits / 8) << 4) | (uid_resp_bits & 0x07); // length of data in bytes and bits for (uint16_t i = 0; i <= uid_resp_bits / 8; i++) { sel_uid[2 + i] = uid_resp[i]; @@ -2406,7 +2450,7 @@ int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32 } } else { // no collision, use the response to SELECT_ALL as current uid - memcpy(uid_resp, resp, 4); + memcpy(uid_resp, resp, 5); // UID + original BCC } } else { @@ -2425,8 +2469,22 @@ int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32 // Construct SELECT UID command sel_uid[1] = 0x70; // transmitting a full UID (1 Byte cmd, 1 Byte NVB, 4 Byte UID, 1 Byte BCC, 2 Bytes CRC) - memcpy(sel_uid + 2, uid_resp, 4); // the UID received during anticollision, or the provided UID - sel_uid[6] = sel_uid[2] ^ sel_uid[3] ^ sel_uid[4] ^ sel_uid[5]; // calculate and add BCC + + if (anticollision) { + memcpy(sel_uid + 2, uid_resp, 5); // the UID received during anticollision with original BCC + uint8_t bcc = sel_uid[2] ^ sel_uid[3] ^ sel_uid[4] ^ sel_uid[5]; // calculate BCC + if (sel_uid[6] != bcc) { + Dbprintf("BCC%d incorrect, got 0x%02x, expected 0x%02x. Will use " NOLF, cascade_level, sel_uid[6], bcc); + if (! hf14aconfig.obeybadbcc) { + sel_uid[6] = bcc; + } + Dbprintf("0x%02x", sel_uid[6]); + } + } else { + memcpy(sel_uid + 2, uid_resp, 4); // the provided UID + sel_uid[6] = sel_uid[2] ^ sel_uid[3] ^ sel_uid[4] ^ sel_uid[5]; // calculate and add BCC + } + AddCrc14A(sel_uid, 7); // calculate and add CRC ReaderTransmit(sel_uid, sizeof(sel_uid), NULL); @@ -2436,7 +2494,21 @@ int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32 sak = resp[0]; // Test if more parts of the uid are coming - if ((sak & 0x04) /* && uid_resp[0] == 0x88 */) { + do_cascade = (((sak & 0x04) /* && uid_resp[0] == 0x88 */) > 0); + if (cascade_level==0) { + if (hf14aconfig.forcecl2==-1) { + do_cascade = false; + } else if (hf14aconfig.forcecl2==1) { + do_cascade = true; + } // else 0==auto + } else if (cascade_level==1) { + if (hf14aconfig.forcecl3==-1) { + do_cascade = false; + } else if (hf14aconfig.forcecl3==1) { + do_cascade = true; + } // else 0==auto + } + if (do_cascade) { // Remove first byte, 0x88 is not an UID byte, it CT, see page 3 of: // http://www.nxp.com/documents/application_note/AN10927.pdf uid_resp[0] = uid_resp[1]; @@ -2458,7 +2530,7 @@ int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32 p_card->sak = sak; } - // PICC compilant with iso14443a-4 ---> (SAK & 0x20 != 0) + // PICC compliant with iso14443a-4 ---> (SAK & 0x20 != 0) if ((sak & 0x20) == 0) return 2; // RATS, Request for answer to select diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index 94b1d5a2d..d1df5534c 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -96,6 +96,9 @@ typedef struct { # define CheckCrc14A(data, len) check_crc(CRC_14443_A, (data), (len)) #endif +void printHf14aConfig(void); +void setHf14aConfig(hf14a_config *hc); +hf14a_config *getHf14aConfig(void); void iso14a_set_timeout(uint32_t timeout); uint32_t iso14a_get_timeout(void); diff --git a/armsrc/lfsampling.c b/armsrc/lfsampling.c index eec8d7ab4..d5f66c22e 100644 --- a/armsrc/lfsampling.c +++ b/armsrc/lfsampling.c @@ -36,7 +36,7 @@ static BitstreamOut data = {0, 0, 0}; // internal struct to keep track of samples gathered static sampling_t samples = {0, 0, 0, 0}; -void printConfig(void) { +void printLFConfig(void) { uint32_t d = config.divisor; DbpString(_CYAN_("LF Sampling config")); Dbprintf(" [q] divisor.............%d ( "_GREEN_("%d.%02d kHz")" )", d, 12000 / (d + 1), ((1200000 + (d + 1) / 2) / (d + 1)) - ((12000 / (d + 1)) * 100)); @@ -97,7 +97,7 @@ void setSamplingConfig(sample_config *sc) { config.samples_to_skip = sc->samples_to_skip; if (sc->verbose) - printConfig(); + printLFConfig(); } sample_config *getSamplingConfig(void) { @@ -376,7 +376,7 @@ uint32_t DoPartialAcquisition(int trigger_threshold, bool verbose, uint32_t samp static uint32_t ReadLF(bool reader_field, bool verbose, uint32_t sample_size) { if (verbose) - printConfig(); + printLFConfig(); LFSetupFPGAForADC(config.divisor, reader_field); uint32_t ret = DoAcquisition_config(verbose, sample_size); diff --git a/armsrc/lfsampling.h b/armsrc/lfsampling.h index 137738163..f60324d70 100644 --- a/armsrc/lfsampling.h +++ b/armsrc/lfsampling.h @@ -99,7 +99,7 @@ void setSamplingConfig(sample_config *sc); sample_config *getSamplingConfig(void); -void printConfig(void); +void printLFConfig(void); void printSamples(void); #endif // __LFSAMPLING_H diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index 6ce4d077e..46daffefe 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -168,6 +168,23 @@ const char *getTagInfo(uint8_t uid) { static uint16_t frameLength = 0; uint16_t atsFSC[] = {16, 24, 32, 40, 48, 64, 96, 128, 256}; +static int usage_hf_14a_config(void) { + PrintAndLogEx(NORMAL, "Usage: hf 14a config [a 0|1] [b 0|1]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h This help"); + PrintAndLogEx(NORMAL, " a 0|1 Force standard anticollision sequence even if ATQA tells not to do it"); + PrintAndLogEx(NORMAL, " b 0|1 Obey bad BCC in anticollision sequence"); + PrintAndLogEx(NORMAL, " 2 0|1|-1 Override CL2 decision: 0=auto, 1=always perform CL2, -1=never perform CL2"); + PrintAndLogEx(NORMAL, " 3 0|1|-1 Override CL3 decision: 0=auto, 1=always perform CL3, -1=never perform CL3"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config ")" Print current configuration"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a ")" Force standard anticollision"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 0 ")" Restore ATQA interpretation"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config b ")" Force obey bad BCC in anticollision"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config b 0 ")" Restore BCC computation in anticollision"); + return PM3_SUCCESS; +} + static int usage_hf_14a_sim(void) { // PrintAndLogEx(NORMAL, "\n Emulating ISO/IEC 14443 type A tag with 4,7 or 10 byte UID\n"); PrintAndLogEx(NORMAL, "\n Emulating ISO/IEC 14443 type A tag with 4,7 byte UID\n"); @@ -234,6 +251,98 @@ static int CmdHF14AList(const char *Cmd) { return 0; } +int hf14a_getconfig(hf14a_config *config) { + if (!session.pm3_present) return PM3_ENOTTY; + + if (config == NULL) + return PM3_EINVARG; + + clearCommandBuffer(); + + SendCommandNG(CMD_HF_ISO14443A_GET_CONFIG, NULL, 0); + PacketResponseNG resp; + if (!WaitForResponseTimeout(CMD_HF_ISO14443A_GET_CONFIG, &resp, 2000)) { + PrintAndLogEx(WARNING, "command execution time out"); + return PM3_ETIMEOUT; + } + memcpy(config, resp.data.asBytes, sizeof(hf14a_config)); + return PM3_SUCCESS; +} + +int hf14a_setconfig(hf14a_config *config) { + if (!session.pm3_present) return PM3_ENOTTY; + + clearCommandBuffer(); + if (config != NULL) + SendCommandNG(CMD_HF_ISO14443A_SET_CONFIG, (uint8_t *)config, sizeof(hf14a_config)); + else + SendCommandNG(CMD_HF_ISO14443A_PRINT_CONFIG, NULL, 0); + + return PM3_SUCCESS; +} + +static int CmdHf14AConfig(const char *Cmd) { + + if (!session.pm3_present) return PM3_ENOTTY; + + // if called with no params, just print the device config + if (strlen(Cmd) == 0) { + return hf14a_setconfig(NULL); + } + + hf14a_config config = { + .forceanticol = -1, + .obeybadbcc = -1, + .forcecl2 = -2, + .forcecl3 = -2 + }; + + bool errors = false; + char cl[3] = {0}; + uint8_t cmdp = 0; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (param_getchar(Cmd, cmdp)) { + case 'h': + return usage_hf_14a_config(); + case 'a': + config.forceanticol = (param_getchar(Cmd, cmdp + 1) == '1'); + cmdp += 2; + break; + case 'b': + config.obeybadbcc = (param_getchar(Cmd, cmdp + 1) == '1'); + cmdp += 2; + break; + case '2': + param_getstr(Cmd, cmdp + 1, cl, sizeof(cl)); + if (strcmp(cl, "-1") == 0) { + config.forcecl2 = -1; + } else { + config.forcecl2 = strcmp(cl, "1") == 0; + } + cmdp += 2; + break; + case '3': + param_getstr(Cmd, cmdp + 1, cl, sizeof(cl)); + if (strcmp(cl, "-1") == 0) { + config.forcecl3 = -1; + } else { + config.forcecl3 = strcmp(cl, "1") == 0; + } + cmdp += 2; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = 1; + break; + } + } + + // validations + if (errors) return usage_hf_14a_config(); + + return hf14a_setconfig(&config); +} + int Hf14443_4aGetCardData(iso14a_card_select_t *card) { SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); @@ -1241,6 +1350,7 @@ static command_t CommandTable[] = { {"chaining", CmdHF14AChaining, IfPm3Iso14443a, "Control ISO 14443-4 input chaining"}, {"raw", CmdHF14ACmdRaw, IfPm3Iso14443a, "Send raw hex data to tag"}, {"antifuzz", CmdHF14AAntiFuzz, IfPm3Iso14443a, "Fuzzing the anticollision phase. Warning! Readers may react strange"}, + {"config", CmdHf14AConfig, IfPm3Iso14443a, "Configure 14a settings (use with caution)"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhf14a.h b/client/src/cmdhf14a.h index 83fb2c69d..925bf8bef 100644 --- a/client/src/cmdhf14a.h +++ b/client/src/cmdhf14a.h @@ -13,7 +13,7 @@ #define CMDHF14A_H__ #include "common.h" - +#include "pm3_cmd.h" //hf14a_config #include "mifare.h" // structs // structure and database for uid -> tagtype lookups @@ -26,6 +26,8 @@ int CmdHF14A(const char *Cmd); int CmdHF14ASniff(const char *Cmd); // used by hf topaz sniff int CmdHF14ASim(const char *Cmd); // used by hf mfu sim +int hf14a_getconfig(hf14a_config *config); +int hf14a_setconfig(hf14a_config *config); int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search); const char *getTagInfo(uint8_t uid); int Hf14443_4aGetCardData(iso14a_card_select_t *card); diff --git a/client/src/ui.h b/client/src/ui.h index b466b9c84..7a5b7911d 100644 --- a/client/src/ui.h +++ b/client/src/ui.h @@ -22,7 +22,6 @@ extern "C" { #define _USE_MATH_DEFINES typedef enum logLevel {NORMAL, SUCCESS, INFO, FAILED, WARNING, ERR, DEBUG, INPLACE, HINT} logLevel_t; -#define NOLF "\xff" typedef enum emojiMode {ALIAS, EMOJI, ALTTEXT, ERASE} emojiMode_t; typedef enum clientdebugLevel {cdbOFF, cdbSIMPLE, cdbFULL} clientdebugLevel_t; // typedef enum devicedebugLevel {ddbOFF, ddbERROR, ddbINFO, ddbDEBUG, ddbEXTENDED} devicedebugLevel_t; diff --git a/include/ansi.h b/include/ansi.h index 6c808d10e..27d0dc2e7 100644 --- a/include/ansi.h +++ b/include/ansi.h @@ -1,6 +1,9 @@ #ifndef __ANSI_H #define __ANSI_H +// Not ANSI but dirty trick to specify we don't want a \n +#define NOLF "\xff" + #define AEND "\x1b[0m" #define _BLUE_(s) "\x1b[34m" s AEND diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 21409bbec..5dbf3631a 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -122,6 +122,14 @@ typedef struct { bool verbose; } PACKED sample_config; +// A struct used to send hf14a-configs over USB +typedef struct { + int8_t forceanticol; // bool but also -1 if to be ignored + int8_t obeybadbcc; // bool but also -1 if to be ignored + int8_t forcecl2; // 0:auto 1:force executing CL2 -1:force skipping CL2 + int8_t forcecl3; // 0:auto 1:force executing CL3 -1:force skipping CL3 +} PACKED hf14a_config; + // Tracelog Header struct typedef struct { uint32_t timestamp; @@ -569,6 +577,11 @@ typedef struct { #define CMD_HF_FELICALITE_DUMP 0x03AA #define CMD_HF_FELICALITE_SIMULATE 0x03AB +// For 14a config +#define CMD_HF_ISO14443A_PRINT_CONFIG 0x03B0 +#define CMD_HF_ISO14443A_GET_CONFIG 0x03B1 +#define CMD_HF_ISO14443A_SET_CONFIG 0x03B2 + // For measurements of the antenna tuning #define CMD_MEASURE_ANTENNA_TUNING 0x0400 #define CMD_MEASURE_ANTENNA_TUNING_HF 0x0401