diff --git a/CHANGELOG.md b/CHANGELOG.md index f6e9c9811..4121234df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +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 `hf mf gdmgetblk` - Support Gen4 GDM read configuration block (@iceman1001) + - Added `hf mf gdmsetcfg` - Supprt Gen4 GDM write configuration block (@iceman1001) + - Added `hf mf gdmcfg` - Support Gen4 GDM read configuration block (@iceman1001) - Changed magic note to include a section about GDM tags (@iceman1001) - Added `hf mf gdmsetblk` - Support Gen4 GDM write block (@iceman1001) - Changed `hf 14a info` - detect Gen GDM magic tags (@iceman1001) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 969513d39..6901973f2 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1743,6 +1743,14 @@ static void PacketReceived(PacketCommandNG *packet) { MifareReadConfigBlockGDM(payload->key); break; } + case CMD_HF_MIFARE_G4_GDM_WRCFG: { + struct p { + uint8_t data[16]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + MifareWriteConfigBlockGDM(payload->data); + break; + } case CMD_HF_MIFARE_G4_GDM_WRBL: { struct p { uint8_t blockno; diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 0d14ffb92..7eaad3e23 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -253,7 +253,7 @@ void MifareReadConfigBlockGDM(uint8_t *key) { goto OUT; }; - if (mifare_classic_readblock_ex(pcs, cuid, 0, outbuf, MIFARE_MAGIC_GDM_READBLOCK)) { + if (mifare_classic_readblock_ex(pcs, cuid, 0, outbuf, MIFARE_MAGIC_GDM_READ_CFG)) { retval = PM3_ESOFT; goto OUT; }; @@ -574,6 +574,73 @@ OUT: BigBuf_free(); } +void MifareWriteConfigBlockGDM(uint8_t *datain) { + + int retval = PM3_SUCCESS; + + // check args + if (datain == NULL) { + retval = PM3_EINVARG; + goto OUT; + } + + uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); + if (par == NULL) { + retval = PM3_EMALLOC; + goto OUT; + } + + uint8_t *uid = BigBuf_malloc(10); + if (uid == NULL) { + retval = PM3_EMALLOC; + goto OUT; + } + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + + // variables + uint32_t cuid = 0; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + if (iso14443a_select_card(uid, NULL, &cuid, true, 0, true) == false) { + retval = PM3_ESOFT; + goto OUT; + } + + uint64_t key = 0; + if (mifare_classic_authex_2(pcs, cuid, 0, 0, key, AUTH_FIRST, NULL, NULL, true)) { + retval = PM3_ESOFT; + goto OUT; + }; + + int res = mifare_classic_write_cfg_block_gdm(pcs, cuid, datain); + if (res == PM3_ETEAROFF) { + retval = PM3_ETEAROFF; + goto OUT; + } else if (res) { + retval = PM3_ESOFT; + goto OUT; + } + + if (mifare_classic_halt(pcs, cuid)) { + retval = PM3_ESOFT; + goto OUT; + }; + +OUT: + crypto1_deinit(pcs); + + reply_ng(CMD_HF_MIFARE_G4_GDM_WRCFG, retval, NULL, 0); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); + BigBuf_free(); +} + void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t *datain) { // params @@ -2487,7 +2554,7 @@ void MifareCIdent(bool is_mfc) { uint8_t rats[4] = { ISO14443A_CMD_RATS, 0x80, 0x31, 0x73 }; uint8_t rdblf0[4] = { ISO14443A_CMD_READBLOCK, 0xF0, 0x8D, 0x5f}; uint8_t rdbl00[4] = { ISO14443A_CMD_READBLOCK, 0x00, 0x02, 0xa8}; - uint8_t gen4gmd[4] = { MIFARE_MAGIC_GDM_AUTH_KEYA, 0x00, 0x6C, 0x92}; + uint8_t gen4gmd[4] = { MIFARE_MAGIC_GDM_AUTH_KEY, 0x00, 0x6C, 0x92}; uint8_t gen4GetConf[8] = { GEN_4GTU_CMD, 0x00, 0x00, 0x00, 0x00, GEN_4GTU_GETCNF, 0, 0}; uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); uint8_t *buf = BigBuf_malloc(PM3_CMD_DATA_SIZE); diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index 6363423fd..415e0333d 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -60,6 +60,7 @@ void MifareGen3Freez(void); // Gen 3 magic card lock further UID changes // MFC GEN4 GDM void MifareReadConfigBlockGDM(uint8_t *key); +void MifareWriteConfigBlockGDM(uint8_t *datain); void MifareWriteBlockGDM(uint8_t blockno, uint8_t keytype, uint8_t *key, uint8_t *datain); // MFC GEN4 GTU diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index fd9260e23..b11625339 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -142,24 +142,21 @@ int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockN return mifare_classic_authex_2(pcs, uid, blockNo, keyType, ui64Key, isNested, NULL, NULL, false); } int mifare_classic_authex_2(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *timing, bool is_gdm) { - int len; - uint32_t pos, nt, ntpp; // Supplied tag nonce - uint8_t par[1] = {0x00}; + + // "random" reader nonce: uint8_t nr[4]; - uint8_t mf_nr_ar[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + num_to_bytes(prng_successor(GetTickCount(), 32), 4, nr); + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; - // "random" reader nonce: - num_to_bytes(prng_successor(GetTickCount(), 32), 4, nr); - // Transmit MIFARE_CLASSIC_AUTH 0x60, 0x61 or GDM 0x80 - uint8_t cmdbyte = (is_gdm) ? MIFARE_MAGIC_GDM_AUTH_KEYA + (keyType & 0x01) : MIFARE_AUTH_KEYA + (keyType & 0x01); - len = mifare_sendcmd_short(pcs, isNested, cmdbyte, blockNo, receivedAnswer, receivedAnswerPar, timing); + uint8_t cmdbyte = (is_gdm) ? MIFARE_MAGIC_GDM_AUTH_KEY : MIFARE_AUTH_KEYA + (keyType & 0x01); + int len = mifare_sendcmd_short(pcs, isNested, cmdbyte, blockNo, receivedAnswer, receivedAnswerPar, timing); if (len != 4) return 1; // Save the tag nonce (nt) - nt = bytes_to_num(receivedAnswer, 4); + uint32_t nt = bytes_to_num(receivedAnswer, 4); // ----------------------------- crypto1 create if (isNested) @@ -185,7 +182,9 @@ int mifare_classic_authex_2(struct Crypto1State *pcs, uint32_t uid, uint8_t bloc *ntptr = nt; // Generate (encrypted) nr+parity by loading it into the cipher (Nr) - par[0] = 0; + uint32_t pos; + uint8_t par[1] = {0x00}; + uint8_t mf_nr_ar[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; for (pos = 0; pos < 4; pos++) { mf_nr_ar[pos] = crypto1_byte(pcs, nr[pos], 0) ^ nr[pos]; par[0] |= (((filter(pcs->odd) ^ oddparity8(nr[pos])) & 0x01) << (7 - pos)); @@ -221,8 +220,8 @@ int mifare_classic_authex_2(struct Crypto1State *pcs, uint32_t uid, uint8_t bloc return 2; } - ntpp = prng_successor(nt, 32) ^ crypto1_word(pcs, 0, 0); - + // Supplied tag nonce + uint32_t ntpp = prng_successor(nt, 32) ^ crypto1_word(pcs, 0, 0); if (ntpp != bytes_to_num(receivedAnswer, 4)) { if (g_dbglevel >= DBG_EXTENDED) Dbprintf("Authentication failed. Error card response"); return 3; @@ -473,6 +472,52 @@ int mifare_classic_writeblock_ex(struct Crypto1State *pcs, uint32_t uid, uint8_t return 0; } +int mifare_classic_write_cfg_block_gdm(struct Crypto1State *pcs, uint32_t uid, uint8_t *blockData) { + + // variables + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; + + uint16_t len = mifare_sendcmd_short(pcs, 1, MIFARE_MAGIC_GDM_WRITE_CFG, 0, receivedAnswer, receivedAnswerPar, NULL); + if ((len != 1) || (receivedAnswer[0] != 0x0A)) { + return 1; + } + + uint8_t d_block[18], d_block_enc[18]; + memcpy(d_block, blockData, 16); + AddCrc14A(d_block, 16); + + // enough for 18 Bytes to send + uint8_t par[3] = {0x00, 0x00, 0x00}; + // crypto + for (uint32_t pos = 0; pos < 18; pos++) { + d_block_enc[pos] = crypto1_byte(pcs, 0x00, 0) ^ d_block[pos]; + par[pos >> 3] |= (((filter(pcs->odd) ^ oddparity8(d_block[pos])) & 0x01) << (7 - (pos & 0x0007))); + } + + ReaderTransmitPar(d_block_enc, sizeof(d_block_enc), par, NULL); + + // tearoff occurred + if (tearoff_hook() == PM3_ETEAROFF) { + return PM3_ETEAROFF; + } else { + // Receive the response + len = ReaderReceive(receivedAnswer, receivedAnswerPar); + + uint8_t res = 0; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 0)) << 0; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 1)) << 1; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 2)) << 2; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 3)) << 3; + + if ((len != 1) || (res != 0x0A)) { + return 2; + } + } + return 0; +} + + int mifare_classic_value(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, uint8_t action) { // variables diff --git a/armsrc/mifareutil.h b/armsrc/mifareutil.h index bcc813d00..e5731ce36 100644 --- a/armsrc/mifareutil.h +++ b/armsrc/mifareutil.h @@ -82,6 +82,7 @@ int mifare_classic_halt_ex(struct Crypto1State *pcs); int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData); int mifare_classic_writeblock_ex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, bool is_gdm); int mifare_classic_value(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, uint8_t action); +int mifare_classic_write_cfg_block_gdm(struct Crypto1State *pcs, uint32_t uid, uint8_t *blockData); // Ultralight/NTAG... int mifare_ul_ev1_auth(uint8_t *keybytes, uint8_t *pack); diff --git a/client/src/cmdhflist.c b/client/src/cmdhflist.c index 263a4baa1..7207e50f9 100644 --- a/client/src/cmdhflist.c +++ b/client/src/cmdhflist.c @@ -318,18 +318,13 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i snprintf(exp, size, "AUTH-B(" _MAGENTA_("%d") ")", cmd[1]); break; } - case MIFARE_MAGIC_GDM_AUTH_KEYA: { + case MIFARE_MAGIC_GDM_AUTH_KEY: { if (cmdsize > 3) { - snprintf(exp, size, "MAGIC AUTH-A(" _MAGENTA_("%d") ")", cmd[1]); + snprintf(exp, size, "MAGIC AUTH (" _MAGENTA_("%d") ")", cmd[1]); MifareAuthState = masNt; } break; } - case MIFARE_MAGIC_GDM_AUTH_KEYB: { - MifareAuthState = masNt; - snprintf(exp, size, "MAGIC AUTH-B(" _MAGENTA_("%d") ")", cmd[1]); - break; - } case MIFARE_MAGIC_GDM_WRITEBLOCK: { snprintf(exp, size, "MAGIC WRITEBLOCK(" _MAGENTA_("%d") ")", cmd[1]); break; diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 8093c818d..d5d0e913b 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -7574,11 +7574,11 @@ static int CmdHF14AGen4Save(const char *Cmd) { return PM3_SUCCESS; } -static int CmdHF14AGen4_GDM_ConfigBlk(const char *Cmd) { +static int CmdHF14AGen4_GDM_Cfg(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "hf mf gdmconfig", + CLIParserInit(&ctx, "hf mf gdmcfg", "Get configuration data from magic gen4 GDM card.", - "hf mf gdmconfig\n" + "hf mf gdmcfg\n" ); void *argtable[] = { arg_param_begin, @@ -7620,6 +7620,52 @@ static int CmdHF14AGen4_GDM_ConfigBlk(const char *Cmd) { return resp.status; } +static int CmdHF14AGen4_GDM_SetCfg(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf gdmsetcfg", + "Set configuration data on a magic gen4 GDM card", + "hf mf gdmsetcfg -d 850000000000000000005A5A00000008" + ); + void *argtable[] = { + arg_param_begin, + arg_str1("d", "data", "", "bytes to write, 16 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + uint8_t block[MFBLOCK_SIZE] = {0x00}; + int blen = 0; + CLIGetHexWithReturn(ctx, 1, block, &blen); + CLIParserFree(ctx); + + if (blen != MFBLOCK_SIZE) { + PrintAndLogEx(WARNING, "expected %u HEX bytes. got %i", MFBLOCK_SIZE, blen); + return PM3_EINVARG; + } + + struct p { + uint8_t data[MFBLOCK_SIZE]; + } PACKED payload; + + memcpy(payload.data, block, sizeof(payload.data)); + + clearCommandBuffer(); + SendCommandNG(CMD_HF_MIFARE_G4_GDM_WRCFG, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_MIFARE_G4_GDM_WRCFG, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "command execute timeout"); + return PM3_ETIMEOUT; + } + + if (resp.status == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); + PrintAndLogEx(HINT, "try `" _YELLOW_("hf mf gdmcfg") "` to verify"); + } else { + PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )"); + } + return PM3_SUCCESS; +} + static int CmdHF14AGen4_GDM_SetBlk(const char *Cmd) { CLIParserContext *ctx; @@ -7995,8 +8041,9 @@ static command_t CommandTable[] = { {"gsetblk", CmdHF14AGen4SetBlk, IfPm3Iso14443a, "Write block to card"}, {"gview", CmdHF14AGen4View, IfPm3Iso14443a, "View card"}, {"-----------", CmdHelp, IfPm3Iso14443a, "-------------------- " _CYAN_("magic gen4 GDM") " --------------------------"}, - {"gdmconfig", CmdHF14AGen4_GDM_ConfigBlk, IfPm3Iso14443a, "Read config block from card"}, - {"gdmsetblk", CmdHF14AGen4_GDM_SetBlk, IfPm3Iso14443a, "Write block to card"}, + {"gdmcfg", CmdHF14AGen4_GDM_Cfg, IfPm3Iso14443a, "Read config block from card"}, + {"gdmsetcfg", CmdHF14AGen4_GDM_SetCfg, IfPm3Iso14443a, "Write config block to card"}, + {"gdmsetblk", CmdHF14AGen4_GDM_SetBlk, IfPm3Iso14443a, "Write block to card"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("ndef") " -----------------------"}, // {"ice", CmdHF14AMfice, IfPm3Iso14443a, "collect MIFARE Classic nonces to file"}, {"ndefformat", CmdHFMFNDEFFormat, IfPm3Iso14443a, "Format MIFARE Classic Tag as NFC Tag"}, diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index b99bb8430..7a19e6dbe 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -690,6 +690,7 @@ typedef struct { #define CMD_HF_MIFARE_G4_GDM_RDBL 0x0870 #define CMD_HF_MIFARE_G4_GDM_WRBL 0x0871 #define CMD_HF_MIFARE_G4_GDM_CONFIG 0x0872 +#define CMD_HF_MIFARE_G4_GDM_WRCFG 0x0873 #define CMD_UNKNOWN 0xFFFF diff --git a/include/protocols.h b/include/protocols.h index 5900b377c..7b318a9f5 100644 --- a/include/protocols.h +++ b/include/protocols.h @@ -190,11 +190,10 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define MIFARE_CMD_RESTORE 0xC2 #define MIFARE_CMD_TRANSFER 0xB0 -#define MIFARE_MAGIC_GDM_AUTH_KEYA 0x80 -#define MIFARE_MAGIC_GDM_AUTH_KEYB 0x81 +#define MIFARE_MAGIC_GDM_AUTH_KEY 0x80 #define MIFARE_MAGIC_GDM_WRITEBLOCK 0xA8 -#define MIFARE_MAGIC_GDM_READBLOCK 0xE0 -#define MIFARE_MAGIC_GDM_READBLOCK_1 0xE1 +#define MIFARE_MAGIC_GDM_READ_CFG 0xE0 +#define MIFARE_MAGIC_GDM_WRITE_CFG 0xE1 #define MIFARE_EV1_PERSONAL_UID 0x40 #define MIFARE_EV1_SETMODE 0x43