From 4b33a21d09042585625807e3a2564a4f60f121b6 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 1 Nov 2020 00:14:47 +0100 Subject: [PATCH 1/9] exclude 0x6d 00 --- client/src/cmdhfmfp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index fd0f6c93a..5ef2ac826 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -377,7 +377,7 @@ static int CmdHFMFPInfo(const char *Cmd) { // DESFire answers 0x1C or 67 00 // Plus answers 0x0B, 0x09, 0x06 // Which tag answers 6D 00 ?? - if (data[0] != 0x0b && data[0] != 0x09 && data[0] != 0x1C && data[0] != 0x67) { + if (data[0] != 0x0b && data[0] != 0x09 && data[0] != 0x1C && data[0] != 0x67 && data[0] != 0x6d) { PrintAndLogEx(INFO, _RED_("Send copy to iceman of this command output!")); PrintAndLogEx(INFO, "data: %s", sprint_hex(data, datalen)); } From 8d35226943333ebb759aeaf8b7c8e7416f7f9281 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Sun, 1 Nov 2020 01:05:42 +0100 Subject: [PATCH 2/9] Changelog --- CHANGELOG.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7bdfe891..44faeba78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,30 @@ 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] + - Change many commands to cliparser (@iceman1001, @tcprst, @mwalker33,...) + - ... + - Add dictionaries with common words of proper size (@will-caruana) + - Add `hf mf supercard` (@iceman1001) + - Add initial suport for MIFARE Key Diversification, cf AN10922 (@NZSmartie) + - Change MIFARE detection improved (@VortixDev) + - Change `hf 14b sriread` to `hf 14b rdbl` and `hf 14b dump` (@iceman1001) + - Add continuous mode to `hf 14a reader` (@doegox and @iceman1001) + - Add `lf em 4x05_sniff` to allow extracting commands and passwords used be cloners. (@mwalker33) - Removed 'hf iclass replay' - use the 'hf iclass dump' or 'hf iclass rdbl' with option "n" instead (@iceman1001). Concept taken from official repo (@pwpiwi) + - Add Destron FDX-A support (@doegox and @iceman1001) + - Add `lf em 4x05_chk` (@iceman1001) + - Add `lf em 4x05_unlock` tear-off (@doegox and @iceman1001) + - Added customizable 3DES key to hf mfu cauth (@socram8888) + - Add generic `hw tearoff` and hooks in various write commands (@doegox and @iceman1001) + - Add protect support for EM4x05 and fix various EM4x69/EM4x05 aspects (@doegox and @iceman1001) + - Add incognito option to client to avoid mangling history & logs (@doegox) + - Add option to hide/show plot sliders (@mwalker33) + - Add "" key bindings to realign demod plot on samples (@doegox) + - Add "T" key binding to trim plot (@doegox) + - Add units options to `data timescale` (@doegox) + - Add mouse scrolling to pan & zoom to plot (@doegox) + - Add hf_14b_mobib Lua script (@iceman1001) + - Add ASK CTx detection to hf 14b reader (@iceman1001 and @doegox) - Add low level support for 14b' aka Innovatron (@doegox) - Add doc/cliparser.md (@mwalker33) - Add `hf 14b apdu` - send APDU over ISO14443B (@iceman1001) From 5f411bdc3e897fd395407942f862ec22157548c6 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 1 Nov 2020 01:10:54 +0100 Subject: [PATCH 3/9] miller decoding, in odd cases should return --- armsrc/iso14443a.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 8f19b0be9..94954222b 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -109,7 +109,8 @@ static uint32_t LastProxToAirDuration; // Sequence E: 00001111 modulation with subcarrier during second half // Sequence F: 00000000 no modulation with subcarrier // Sequence COLL: 11111111 load modulation over the full bitlength. -// Tricks the reader to think that multiple cards answer (at least one card with 1 and at least one card with 0). +// Tricks the reader to think that multiple cards answer. +// (at least one card with 1 and at least one card with 0) // READER TO CARD - miller // Sequence X: 00001100 drop after half a period // Sequence Y: 00000000 no drop @@ -380,6 +381,7 @@ RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) { return true; // we are finished with decoding the raw data sequence } else { Uart14aReset(); // Nothing received - start over + return false; } } if (Uart.state == STATE_14A_START_OF_COMMUNICATION) { // error - must not follow directly after SOC @@ -2047,11 +2049,6 @@ int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen) { while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)); b = AT91C_BASE_SSC->SSC_RHR; (void) b; - /* - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY)); - b = AT91C_BASE_SSC->SSC_THR; - (void) b; - */ // wait for the FPGA to signal fdt_indicator == 1 (the FPGA is ready to queue new data in its delay line) for (uint8_t j = 0; j < 5; j++) { // allow timeout - better late than never @@ -2070,13 +2067,6 @@ int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen) { AT91C_BASE_SSC->SSC_THR = resp[i++]; FpgaSendQueueDelay = (uint8_t)AT91C_BASE_SSC->SSC_RHR; } - - /* - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - b = (uint16_t)(AT91C_BASE_SSC->SSC_RHR); - (void)b; - } - */ } // Ensure that the FPGA Delay Queue is empty before we switch to TAGSIM_LISTEN again: @@ -2340,7 +2330,9 @@ void iso14443a_antifuzz(uint32_t flags) { uint8_t *received = BigBuf_malloc(MAX_FRAME_SIZE); uint8_t *receivedPar = BigBuf_malloc(MAX_PARITY_SIZE); uint8_t *resp = BigBuf_malloc(20); - + + memset(received, 0x00, MAX_FRAME_SIZE); + memset(received, 0x00, MAX_PARITY_SIZE); memset(resp, 0xFF, 20); LED_A_ON(); @@ -2379,6 +2371,7 @@ void iso14443a_antifuzz(uint32_t flags) { colpos = 8; } + // trigger a faulty/collision response EmSendCmdEx(resp, 5, true); if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("ANTICOLL or SELECT %x", received[1]); LED_D_INV(); @@ -2499,16 +2492,19 @@ int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32 sel_uid[0] = sel_all[0] = 0x93 + cascade_level * 2; if (anticollision) { + // SELECT_ALL ReaderTransmit(sel_all, sizeof(sel_all), NULL); if (!ReaderReceive(resp, resp_par)) { Dbprintf("Card didn't answer to CL%i select all", cascade_level + 1); return 0; } + if (Demod.collisionPos) { // we had a collision and need to construct the UID bit by bit memset(uid_resp, 0, 5); uint16_t uid_resp_bits = 0; uint16_t collision_answer_offset = 0; + // anti-collision-loop: while (Demod.collisionPos) { Dbprintf("Multiple tags detected. Collision after Bit %d", Demod.collisionPos); @@ -2527,6 +2523,7 @@ int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32 ReaderTransmitBits(sel_uid, 16 + uid_resp_bits, NULL); if (!ReaderReceiveOffset(resp, collision_answer_offset, resp_par)) return 0; } + // finally, add the last bits and BCC of the UID for (uint16_t i = collision_answer_offset; i < (Demod.len - 1) * 8; i++, uid_resp_bits++) { uint16_t UIDbit = (resp[i / 8] >> (i % 8)) & 0x01; @@ -2762,7 +2759,7 @@ b5,b6 = 00 - DESELECT */ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, bool send_chaining, void *data, uint8_t *res) { uint8_t parity[MAX_PARITY_SIZE] = {0x00}; - uint8_t real_cmd[cmd_len + 4]; + uint8_t real_cmd[cmd_len + 4] = {0x00}; if (cmd_len) { // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02 From 5a3502920c4ac21465bd7feddf2c7eaf3bfd0d7b Mon Sep 17 00:00:00 2001 From: NZSmartie Date: Sun, 1 Nov 2020 13:40:46 +1300 Subject: [PATCH 4/9] Improve hf mfdes chk key checking and fix specifying single AID --- client/src/cmdhfmfdes.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 2d71b41dd..7a5999614 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -4055,6 +4055,14 @@ static int AuthCheckDesfire(uint8_t *aid, bool aes = false; bool k3kdes = false; + uint8_t num_keys = 0; + uint8_t key_setting = 0; + res = handler_desfire_getkeysettings(&key_setting, &num_keys); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Could not get key settings"); + return res; + } + if (memcmp(aid, "\x00\x00\x00", 3) != 0) { uint8_t file_ids[33] = {0}; uint32_t file_ids_len = 0; @@ -4086,7 +4094,7 @@ static int AuthCheckDesfire(uint8_t *aid, usedkeys[read_access] = 1; if (res == PM3_SUCCESS) { - switch (fileset_len >> 6) { + switch (num_keys >> 6) { case 0: des = true; tdes = true; @@ -4401,24 +4409,20 @@ static int CmdHF14aDesChk(const char *Cmd) { endFilePosition = 0; } - if (aeskeyListLen == 0) { - PrintAndLogEx(ERR, "Aes key list is empty. Nothing to check."); + if (aeskeyListLen == 0 && deskeyListLen == 0 && k3kkeyListLen == 0) { + PrintAndLogEx(ERR, "No keys provided. Nothing to check."); return PM3_EINVARG; - } else { + } + + if (aeskeyListLen != 0) { PrintAndLogEx(INFO, "Loaded " _YELLOW_("%"PRIu32) " aes keys", aeskeyListLen); } - if (deskeyListLen == 0) { - PrintAndLogEx(ERR, "Des key list is empty. Nothing to check."); - return PM3_EINVARG; - } else { + if (deskeyListLen != 0) { PrintAndLogEx(INFO, "Loaded " _YELLOW_("%"PRIu32) " des keys", deskeyListLen); } - if (k3kkeyListLen == 0) { - PrintAndLogEx(ERR, "K3k key list is empty. Nothing to check."); - return PM3_EINVARG; - } else { + if (k3kkeyListLen != 0) { PrintAndLogEx(INFO, "Loaded " _YELLOW_("%"PRIu32) " k3kdes keys", k3kkeyListLen); } @@ -4437,7 +4441,7 @@ static int CmdHF14aDesChk(const char *Cmd) { if (aidlength != 0) { memcpy(&app_ids[0], aid, 3); - app_ids_len = 1; + app_ids_len = 3; } for (uint32_t x = 0; x < app_ids_len / 3; x++) { From 1902c3639425ed1765730657c522780ff0c0d99f Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 1 Nov 2020 01:42:23 +0100 Subject: [PATCH 5/9] fix --- armsrc/iso14443a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 94954222b..1edaad5a6 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -2759,7 +2759,7 @@ b5,b6 = 00 - DESELECT */ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, bool send_chaining, void *data, uint8_t *res) { uint8_t parity[MAX_PARITY_SIZE] = {0x00}; - uint8_t real_cmd[cmd_len + 4] = {0x00}; + uint8_t real_cmd[cmd_len + 4]; if (cmd_len) { // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02 From 634c69398d2945de4bb06fc7994f2c54e7e324a8 Mon Sep 17 00:00:00 2001 From: NZSmartie Date: Mon, 2 Nov 2020 01:09:48 +1300 Subject: [PATCH 6/9] Split out mfdes_get_info from CmdHF14ADesInfo Change needed so that UID can be read for later --- client/src/cmdhfmfdes.c | 117 ++++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 52 deletions(-) diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 7a5999614..dc9c364b8 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -83,6 +83,16 @@ typedef struct mfdes_data { uint8_t *data; } PACKED mfdes_data_t; +typedef struct { + uint8_t isOK; + uint8_t uid[7]; + uint8_t uidlen; + uint8_t versionHW[7]; + uint8_t versionSW[7]; + uint8_t details[14]; +} PACKED mfdes_info_res_t; + + typedef struct mfdes_value { uint8_t fileno; //01 uint8_t value[16]; @@ -664,6 +674,38 @@ static nxp_cardtype_t getCardType(uint8_t major, uint8_t minor) { return DESFIRE_UNKNOWN; } +static int mfdes_get_info(mfdes_info_res_t *info) { + SendCommandNG(CMD_HF_DESFIRE_INFO, NULL, 0); + PacketResponseNG resp; + + if (WaitForResponseTimeout(CMD_HF_DESFIRE_INFO, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "Command execute timeout"); + DropField(); + return PM3_ETIMEOUT; + } + + memcpy(info, resp.data.asBytes, sizeof(mfdes_info_res_t)); + + if (resp.status != PM3_SUCCESS) { + switch (info->isOK) { + case 1: + PrintAndLogEx(WARNING, "Can't select card"); + break; + case 2: + PrintAndLogEx(WARNING, "Card is most likely not DESFire. Wrong size UID"); + break; + case 3: + default: + PrintAndLogEx(WARNING, _RED_("Command unsuccessful")); + break; + } + return PM3_ESOFT; + } + + return PM3_SUCCESS; +} + + static int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rpayload) { // 3 different way to authenticate AUTH (CRC16) , AUTH_ISO (CRC32) , AUTH_AES (CRC32) // 4 different crypto arg1 DES, 3DES, 3K3DES, AES @@ -3265,43 +3307,14 @@ static int CmdHF14ADesFormatPICC(const char *Cmd) { static int CmdHF14ADesInfo(const char *Cmd) { (void)Cmd; // Cmd is not used so far DropField(); - SendCommandNG(CMD_HF_DESFIRE_INFO, NULL, 0); - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_DESFIRE_INFO, &resp, 1500) == false) { - PrintAndLogEx(WARNING, "Command execute timeout"); - DropField(); - return PM3_ETIMEOUT; + mfdes_info_res_t info; + int res = mfdes_get_info(&info); + if (res != PM3_SUCCESS) { + return res; } - struct p { - uint8_t isOK; - uint8_t uid[7]; - uint8_t uidlen; - uint8_t versionHW[7]; - uint8_t versionSW[7]; - uint8_t details[14]; - } PACKED; - - struct p *package = (struct p *) resp.data.asBytes; - - if (resp.status != PM3_SUCCESS) { - switch (package->isOK) { - case 1: - PrintAndLogEx(WARNING, "Can't select card"); - break; - case 2: - PrintAndLogEx(WARNING, "Card is most likely not DESFire. Wrong size UID"); - break; - case 3: - default: - PrintAndLogEx(WARNING, _RED_("Command unsuccessful")); - break; - } - return PM3_ESOFT; - } - - nxp_cardtype_t cardtype = getCardType(package->versionHW[3], package->versionHW[4]); + nxp_cardtype_t cardtype = getCardType(info.versionHW[3], info.versionHW[4]); if (cardtype == PLUS_EV1) { PrintAndLogEx(INFO, "Card seems to be MIFARE Plus EV1. Try " _YELLOW_("`hf mfp info`")); return PM3_SUCCESS; @@ -3310,30 +3323,30 @@ static int CmdHF14ADesInfo(const char *Cmd) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); PrintAndLogEx(INFO, "-------------------------------------------------------------"); - PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(package->uid, package->uidlen)); - PrintAndLogEx(SUCCESS, " Batch number: " _GREEN_("%s"), sprint_hex(package->details + 7, 5)); - PrintAndLogEx(SUCCESS, " Production date: week " _GREEN_("%02x") " / " _GREEN_("20%02x"), package->details[12], package->details[13]); + PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(info.uid, info.uidlen)); + PrintAndLogEx(SUCCESS, " Batch number: " _GREEN_("%s"), sprint_hex(info.details + 7, 5)); + PrintAndLogEx(SUCCESS, " Production date: week " _GREEN_("%02x") " / " _GREEN_("20%02x"), info.details[12], info.details[13]); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Hardware Information")); - PrintAndLogEx(INFO, " Vendor Id: " _YELLOW_("%s"), getTagInfo(package->versionHW[0])); - PrintAndLogEx(INFO, " Type: " _YELLOW_("0x%02X"), package->versionHW[1]); - PrintAndLogEx(INFO, " Subtype: " _YELLOW_("0x%02X"), package->versionHW[2]); - PrintAndLogEx(INFO, " Version: %s", getVersionStr(package->versionHW[3], package->versionHW[4])); - PrintAndLogEx(INFO, " Storage size: %s", getCardSizeStr(package->versionHW[5])); - PrintAndLogEx(INFO, " Protocol: %s", getProtocolStr(package->versionHW[6], true)); + PrintAndLogEx(INFO, " Vendor Id: " _YELLOW_("%s"), getTagInfo(info.versionHW[0])); + PrintAndLogEx(INFO, " Type: " _YELLOW_("0x%02X"), info.versionHW[1]); + PrintAndLogEx(INFO, " Subtype: " _YELLOW_("0x%02X"), info.versionHW[2]); + PrintAndLogEx(INFO, " Version: %s", getVersionStr(info.versionHW[3], info.versionHW[4])); + PrintAndLogEx(INFO, " Storage size: %s", getCardSizeStr(info.versionHW[5])); + PrintAndLogEx(INFO, " Protocol: %s", getProtocolStr(info.versionHW[6], true)); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Software Information")); - PrintAndLogEx(INFO, " Vendor Id: " _YELLOW_("%s"), getTagInfo(package->versionSW[0])); - PrintAndLogEx(INFO, " Type: " _YELLOW_("0x%02X"), package->versionSW[1]); - PrintAndLogEx(INFO, " Subtype: " _YELLOW_("0x%02X"), package->versionSW[2]); - PrintAndLogEx(INFO, " Version: " _YELLOW_("%d.%d"), package->versionSW[3], package->versionSW[4]); - PrintAndLogEx(INFO, " Storage size: %s", getCardSizeStr(package->versionSW[5])); - PrintAndLogEx(INFO, " Protocol: %s", getProtocolStr(package->versionSW[6], false)); + PrintAndLogEx(INFO, " Vendor Id: " _YELLOW_("%s"), getTagInfo(info.versionSW[0])); + PrintAndLogEx(INFO, " Type: " _YELLOW_("0x%02X"), info.versionSW[1]); + PrintAndLogEx(INFO, " Subtype: " _YELLOW_("0x%02X"), info.versionSW[2]); + PrintAndLogEx(INFO, " Version: " _YELLOW_("%d.%d"), info.versionSW[3], info.versionSW[4]); + PrintAndLogEx(INFO, " Storage size: %s", getCardSizeStr(info.versionSW[5])); + PrintAndLogEx(INFO, " Protocol: %s", getProtocolStr(info.versionSW[6], false)); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Card capabilities")); - uint8_t major = package->versionSW[3]; - uint8_t minor = package->versionSW[4]; + uint8_t major = info.versionSW[3]; + uint8_t minor = info.versionSW[4]; if (major == 0 && minor == 4) PrintAndLogEx(INFO, "\t0.4 - DESFire MF3ICD40, No support for APDU (only native commands)"); if (major == 0 && minor == 5) @@ -3363,7 +3376,7 @@ static int CmdHF14ADesInfo(const char *Cmd) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature")); if (handler_desfire_signature(signature, &signature_len) == PM3_SUCCESS) { - desfire_print_signature(package->uid, package->uidlen, signature, signature_len, cardtype); + desfire_print_signature(info.uid, info.uidlen, signature, signature_len, cardtype); } else { PrintAndLogEx(WARNING, "--- Card doesn't support GetSignature cmd"); } From c9a10631de05f6d24e892eb23c827c64dc30c5f9 Mon Sep 17 00:00:00 2001 From: NZSmartie Date: Mon, 2 Nov 2020 01:22:19 +1300 Subject: [PATCH 7/9] Gallagher key checking is now supported on MIFARE Desfire Both `hf mfdes auth` and `hf mfdes chk` now support Key Diversification for AN10922 and as special treat, Gallagher issued cards. For `hf mfdes auth`: ``` -d, --kdf Key Derivation Function (KDF) (0=None, 1=AN10922, 2=Gallagher) -i, --kdfi KDF input (HEX 1-31 bytes) ``` And for `hf mfdes chk`: ``` -f, --kdf Key Derivation Function (KDF) (0=None, 1=AN10922, Gallagher) -i, --kdfi KDF input (HEX 1-31 bytes) ``` Examples: - `hf mfdes auth -a 2081f4 -m 3 -t 4 -d 2 -n 2 -k 00112233445566778899aabbccddeeff` Will diversify the key for key `2` on AID `2081F4` for Gallagher issued cards - `hf mfdes chk -f 1 -i 00112233 -d mfdes_default_keys` Will read in all the default keys from the dictionary, and diversify them using AN10922 with the input data `00112233` - `hf mfdes chk -f 2 -d mfdes_default_keys` Will read in all the default keys from the dictionary, and diversify them using AN10922 but with input data generated from the card's UID, AID and key number. --- client/resources/aid_desfire.json | 16 +++++++++++ client/src/cmdhfmfdes.c | 48 +++++++++++++++++++++++++++++-- common/generator.c | 41 ++++++++++++++++++++++++++ common/generator.h | 2 ++ include/mifare.h | 1 + 5 files changed, 105 insertions(+), 3 deletions(-) diff --git a/client/resources/aid_desfire.json b/client/resources/aid_desfire.json index 4dddc2a52..9bedab608 100644 --- a/client/resources/aid_desfire.json +++ b/client/resources/aid_desfire.json @@ -336,5 +336,21 @@ FFFFFF General Issuer Information (FIDs 00: MAD Version; 01: Card Holder; 02: Ca "Name": "MemberCard", "Description": "CAR2GO - Member Card", "Type": "carsharing" + }, + { + "AID": "2F81F4", + "Vendor": "Gallagher", + "Country": "NZ", + "Name": "Access control", + "Description": "Card Application Directory (CAD)", + "Type": "" + }, + { + "AID": "2081F4", + "Vendor": "Gallagher", + "Country": "NZ", + "Name": "Access control", + "Description": "Cardax Card Data Application", + "Type": "" } ] diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index dc9c364b8..ff2656c33 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -33,6 +33,7 @@ #include "mifare/mifaredefault.h" // default keys #include "mifare/ndef.h" // NDEF #include "mifare/mad.h" +#include "generator.h" #define MAX_KEY_LEN 24 #define MAX_KEYS_LIST_LEN 1024 @@ -757,6 +758,20 @@ static int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rp if (g_debugMode) { PrintAndLogEx(INFO, " Derrived key: " _GREEN_("%s"), sprint_hex(key->data, key_block_size(key))); } + } else if (payload->kdfAlgo == MFDES_KDF_ALGO_GALLAGHER) { + // We will overrite any provided KDF input since a gallagher specific KDF was requested. + payload->kdfInputLen = 11; + + if (mfdes_kdf_input_gallagher(tag->info.uid, tag->info.uidlen, payload->keyno, tag->selected_application, payload->kdfInput, &payload->kdfInputLen) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Could not generate Gallagher KDF input"); + } + + mifare_kdf_an10922(key, payload->kdfInput, payload->kdfInputLen); + + if (g_debugMode) { + PrintAndLogEx(INFO, " KDF Input: " _YELLOW_("%s"), sprint_hex(payload->kdfInput, payload->kdfInputLen)); + PrintAndLogEx(INFO, " Derrived key: " _GREEN_("%s"), sprint_hex(key->data, key_block_size(key))); + } } uint8_t subcommand = MFDES_AUTHENTICATE; @@ -2100,6 +2115,13 @@ static int desfire_authenticate(int cmdAuthMode, int cmdAuthAlgo, uint8_t *aid, PrintAndLogEx(FAILED, "KDF AN10922 algo requires an input of length 1-31 bytes."); return PM3_EINVARG; } + case MFDES_KDF_ALGO_GALLAGHER: + // TODO: 2TDEA and 3TDEA keys use an input length of 1-15 bytes + if (cmdAuthAlgo != MFDES_ALGO_AES) { + PrintAndLogEx(FAILED, "Crypto algo not valid for the KDF AN10922 algo."); + return PM3_EINVARG; + } + // KDF input arg is ignored as it'll be generated. case MFDES_KDF_ALGO_NONE: break; default: @@ -3924,7 +3946,7 @@ static int CmdHF14ADesAuth(const char *Cmd) { arg_strx0("a", "aid", "", "AID used for authentification (HEX 3 bytes)"), arg_int0("n", "keyno", "", "Key number used for authentification"), arg_str0("k", "key", "", "Key for checking (HEX 8-24 bytes)"), - arg_int0("d", "kdf", "", "Key Derivation Function (KDF) (0=None, 1=AN10922)"), + arg_int0("d", "kdf", "", "Key Derivation Function (KDF) (0=None, 1=AN10922, 2=Gallagher)"), arg_str0("i", "kdfi", "", "KDF input (HEX 1-31 bytes)"), arg_param_end }; @@ -4052,6 +4074,7 @@ static int AuthCheckDesfire(uint8_t *aid, uint8_t deskeyList[MAX_KEYS_LIST_LEN][8], uint32_t deskeyListLen, uint8_t aeskeyList[MAX_KEYS_LIST_LEN][16], uint32_t aeskeyListLen, uint8_t k3kkeyList[MAX_KEYS_LIST_LEN][24], uint32_t k3kkeyListLen, + uint8_t cmdKdfAlgo, uint8_t kdfInputLen, uint8_t *kdfInput, uint8_t foundKeys[4][0xE][24 + 1], bool *result) { uint32_t curaid = (aid[0] & 0xFF) + ((aid[1] & 0xFF) << 8) + ((aid[2] & 0xFF) << 16); @@ -4213,7 +4236,7 @@ static int AuthCheckDesfire(uint8_t *aid, if (usedkeys[keyno] == 1 && foundKeys[2][keyno][0] == 0) { for (uint32_t curkey = 0; curkey < aeskeyListLen; curkey++) { mfdes_auth_res_t rpayload; - error = desfire_authenticate(MFDES_AUTH_AES, MFDES_ALGO_AES, aid, aeskeyList[curkey], keyno, 0, 0, NULL, &rpayload); + error = desfire_authenticate(MFDES_AUTH_AES, MFDES_ALGO_AES, aid, aeskeyList[curkey], keyno, cmdKdfAlgo, kdfInputLen, kdfInput, &rpayload); if (error == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "AID 0x%06X, Found AES Key %u : " _GREEN_("%s"), curaid, keyno, sprint_hex(aeskeyList[curkey], 16)); foundKeys[2][keyno][0] = 0x01; @@ -4302,6 +4325,8 @@ static int CmdHF14aDesChk(const char *Cmd) { arg_str0(NULL, "startp2b", "", "Start key (2-byte HEX) for 2-byte search (use with `--pattern2b`)"), arg_str0("j", "json", "", "Json file to save keys"), arg_lit0("v", "verbose", "Verbose mode."), + arg_int0("f", "kdf", "", "Key Derivation Function (KDF) (0=None, 1=AN10922, 2=Gallagher)"), + arg_str0("i", "kdfi", "", "KDF input (HEX 1-31 bytes)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -4381,6 +4406,12 @@ static int CmdHF14aDesChk(const char *Cmd) { bool verbose = arg_get_lit(ctx, 8); + // Get KDF input + uint8_t kdfInput[31] = {0}; + int kdfInputLen = 0; + uint8_t cmdKDFAlgo = arg_get_int_def(ctx, 9, 0); + CLIGetHexWithReturn(ctx, 10, kdfInput, &kdfInputLen); + CLIParserFree(ctx); // 1-byte pattern search mode @@ -4446,6 +4477,17 @@ static int CmdHF14aDesChk(const char *Cmd) { uint8_t app_ids[78] = {0}; uint32_t app_ids_len = 0; + clearCommandBuffer(); + + mfdes_info_res_t info = {0}; + res = mfdes_get_info(&info); + if (res != PM3_SUCCESS) { + return res; + } + // TODO: Store this UID someowhere not global + memcpy(tag->info.uid, info.uid, info.uidlen); + tag->info.uidlen = info.uidlen; + if (handler_desfire_appids(app_ids, &app_ids_len) != PM3_SUCCESS) { PrintAndLogEx(ERR, "Can't get list of applications on tag"); DropField(); @@ -4462,7 +4504,7 @@ static int CmdHF14aDesChk(const char *Cmd) { uint32_t curaid = (app_ids[x * 3] & 0xFF) + ((app_ids[(x * 3) + 1] & 0xFF) << 8) + ((app_ids[(x * 3) + 2] & 0xFF) << 16); PrintAndLogEx(ERR, "Checking aid 0x%06X...", curaid); - res = AuthCheckDesfire(&app_ids[x * 3], deskeyList, deskeyListLen, aeskeyList, aeskeyListLen, k3kkeyList, k3kkeyListLen, foundKeys, &result); + res = AuthCheckDesfire(&app_ids[x * 3], deskeyList, deskeyListLen, aeskeyList, aeskeyListLen, k3kkeyList, k3kkeyListLen, cmdKDFAlgo, kdfInputLen, kdfInput, foundKeys, &result); if (res == PM3_EOPABORTED) { break; } diff --git a/common/generator.c b/common/generator.c index a6324d24f..f40070346 100644 --- a/common/generator.c +++ b/common/generator.c @@ -423,6 +423,47 @@ uint32_t lf_t55xx_white_pwdgen(uint32_t id) { return pwd; } +// Gallagher Desfire Key Diversification Input for Cardax Card Data Application +int mfdes_kdf_input_gallagher(uint8_t *uid, uint8_t uidLen, uint8_t keyNo, uint32_t aid, uint8_t *kdfInputOut, uint8_t *kdfInputLen) { + if (uid == NULL || (uidLen != 4 && uidLen != 7) || keyNo > 2 || kdfInputOut == NULL || kdfInputLen == NULL) { + if (g_debugMode) { + PrintAndLogEx(WARNING, "Invalid arguments"); + } + return PM3_EINVARG; + } + + // Verify the AppID is a valid Gallagher AppID + if ((aid & 0xF0FFFF) != 0x2081F4) { + if (g_debugMode) { + PrintAndLogEx(WARNING, "Invalid Gallagher AID %06X", aid); + } + return PM3_EINVARG; + } + + int len = 0; + // If the keyNo == 1, then omit the UID. + if (keyNo != 1) { + if (*kdfInputLen < (4 + uidLen)) { + return PM3_EINVARG; + } + + memcpy(kdfInputOut, uid, uidLen); + len += uidLen; + } else if (*kdfInputLen < 4) { + return PM3_EINVARG; + } + + kdfInputOut[len++] = keyNo; + + kdfInputOut[len++] = aid & 0xff; + kdfInputOut[len++] = (aid >> 8) & 0xff; + kdfInputOut[len++] = (aid >> 16) & 0xff; + + *kdfInputLen = len; + + return PM3_SUCCESS; +} + //------------------------------------ // Self tests //------------------------------------ diff --git a/common/generator.h b/common/generator.h index b1c3c82d0..40dd51e78 100644 --- a/common/generator.h +++ b/common/generator.h @@ -43,5 +43,7 @@ int mfc_algo_sky_all(uint8_t *uid, uint8_t *keys); uint32_t lf_t55xx_white_pwdgen(uint32_t id); +int mfdes_kdf_input_gallagher(uint8_t *uid, uint8_t uidLen, uint8_t keyNo, uint32_t aid, uint8_t *kdfInputOut, uint8_t *kdfInputLen); + int generator_selftest(void); #endif diff --git a/include/mifare.h b/include/mifare.h index 50dbdc3b0..804d6bd78 100644 --- a/include/mifare.h +++ b/include/mifare.h @@ -97,6 +97,7 @@ typedef enum { typedef enum { MFDES_KDF_ALGO_NONE = 0, MFDES_KDF_ALGO_AN10922 = 1, + MFDES_KDF_ALGO_GALLAGHER = 2, } mifare_des_kdf_algo_t; //----------------------------------------------------------------------------- From 13e6baec310f2d9a149e11d7a84b259bef006815 Mon Sep 17 00:00:00 2001 From: NZSmartie Date: Mon, 2 Nov 2020 01:47:19 +1300 Subject: [PATCH 8/9] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44faeba78..73d4d6651 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac ## [unreleased][unreleased] - Change many commands to cliparser (@iceman1001, @tcprst, @mwalker33,...) - ... + - Add Gallagher key checking/KDF on MIFARE Desfire (@NZSmartie) - Add dictionaries with common words of proper size (@will-caruana) - Add `hf mf supercard` (@iceman1001) - Add initial suport for MIFARE Key Diversification, cf AN10922 (@NZSmartie) From ddae148613fbeb82fb26ff14898f5eeac8f76536 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Sun, 1 Nov 2020 22:51:07 +0100 Subject: [PATCH 9/9] Makefile: don't rebuild client if not needed --- client/Makefile | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/client/Makefile b/client/Makefile index b4393ad36..5eff657e0 100644 --- a/client/Makefile +++ b/client/Makefile @@ -583,7 +583,7 @@ all: $(BINS) all-static: LDLIBS:=-static $(LDLIBS) all-static: $(BINS) -proxmark3: $(OBJS) amiibo cliparser jansson hardnested lua mbedtls reveng tinycbor whereami lualibs/pm3_cmd.lua lualibs/mfc_default_keys.lua +proxmark3: $(OBJS) $(AMIIBOLIB) $(CLIPARSERLIB) $(JANSSONLIB) $(HARDNESTEDLIB) $(LUALIB) $(MBEDTLSLIB) $(REVENGLIB) $(TINYCBORLIB) $(WHEREAMILIB) lualibs/pm3_cmd.lua lualibs/mfc_default_keys.lua $(info [=] LD $@) $(Q)$(LD) $(PM3LDFLAGS) $(OBJS) $(LDLIBS) -o $@ @@ -649,43 +649,43 @@ tarbin: $(BINS) # local libraries targets # ########################### -amiibo: +$(AMIIBOLIB): $(info [*] MAKE $@) $(Q)$(MAKE) --no-print-directory -C $(AMIIBOLIBPATH) all -cliparser: +$(CLIPARSERLIB): $(info [*] MAKE $@) $(Q)$(MAKE) --no-print-directory -C $(CLIPARSERLIBPATH) all -hardnested: +$(HARDNESTEDLIB): $(info [*] MAKE $@) $(Q)$(MAKE) --no-print-directory -C $(HARDNESTEDLIBPATH) all -jansson: +$(JANSSONLIB): ifneq ($(JANSSON_FOUND),1) $(info [*] MAKE $@) $(Q)$(MAKE) --no-print-directory -C $(JANSSONLIBPATH) all endif -lua: +$(LUALIB): ifneq ($(LUA_FOUND),1) $(info [*] MAKE $@ for $(LUAPLATFORM)) $(Q)$(MAKE) --no-print-directory -C $(LUALIBPATH) $(LUAPLATFORM) endif -mbedtls: +$(MBEDTLSLIB): $(info [*] MAKE $@) $(Q)$(MAKE) --no-print-directory -C $(MBEDTLSLIBPATH) OBJDIR=$(ROOT_DIR)$(OBJDIR) BINDIR=$(ROOT_DIR)$(OBJDIR) all -reveng: +$(REVENGLIB): $(info [*] MAKE $@) $(Q)$(MAKE) --no-print-directory -C $(REVENGLIBPATH) all -tinycbor: +$(TINYCBORLIB): $(info [*] MAKE $@) $(Q)$(MAKE) --no-print-directory -C $(TINYCBORLIBPATH) all -whereami: +$(WHEREAMILIB): ifneq ($(WHEREAMI_FOUND),1) $(info [*] MAKE $@) $(Q)$(MAKE) --no-print-directory -C $(WHEREAMILIBPATH) all @@ -695,7 +695,7 @@ endif # misc # ######## -.PHONY: all clean install uninstall tarbin amiibo cliparser hardnested jansson lua mbedtls reveng tinycbor whereami +.PHONY: all clean install uninstall tarbin # version.c should be remade on every compilation src/version.c: default_version.c