From 9f70011fee1851ec41803b9afaed0e73524f3677 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Mon, 9 Feb 2026 00:49:51 +0100 Subject: [PATCH] hf mfu setkey: supports ulc/ulaes auth and ulaes schann --- armsrc/appmain.c | 2 +- armsrc/iso14443a.c | 2 +- armsrc/mifarecmd.c | 51 +++++++++------- armsrc/mifarecmd.h | 2 +- client/src/cmdhfmfu.c | 137 ++++++++++++++++++++++++++++++------------ include/pm3_cmd.h | 9 +++ 6 files changed, 139 insertions(+), 64 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 7f91c2b94..e52325958 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1877,7 +1877,7 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_MIFAREU_SETKEY: { - MifareUSetKey(packet->oldarg[0], packet->data.asBytes); + MifareUSetKey((mful_setkey_t *)packet->data.asBytes); break; } case CMD_HF_MIFARE_READSC: { diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index da16fce61..0ece3c453 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -3441,7 +3441,7 @@ int iso14443a_fast_select_card(const uint8_t *uid_ptr, uint8_t num_cascades) { } void iso14443a_setup(uint8_t fpga_minor_mode) { - + set_session_channel(false); FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Set up the synchronous serial port FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO14443A); diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 03b9e89b4..60063a23b 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -856,15 +856,7 @@ void MifareUWriteBlockCompat(mful_writeblock_t *packet) { set_tracing(false); } -void MifareUSetKey(uint8_t arg0, uint8_t *datain) { - - uint8_t key[16] = {0x00}; - if (arg0 < 1 || arg0 > 3) { - OnError(0); - return; - } - - memcpy(key, datain, 16); +void MifareUSetKey(mful_setkey_t *packet) { LED_A_ON(); LED_B_OFF(); @@ -880,34 +872,51 @@ void MifareUSetKey(uint8_t arg0, uint8_t *datain) { return; }; + bool useCKey = (packet->keytype == 1); // UL_C + bool useAESKey = (packet->keytype == 3); // UL_AES + uint8_t start_block = 4; // just to be safe - switch (arg0) { - case 1: // UL-C - start_block = 44; - break; - case 2: // UL-AES DataProtKey + if (useCKey) { // UL-C + start_block = 44; + } else if (useAESKey) { // UL-AES + if (packet->key_index == 0) { // UL-AES DataProtKey start_block = 48; - break; - case 3: // UL-AES UIDRetrKey + } else if (packet->key_index == 1) { // UL-AES UIDRetrKey start_block = 52; - break; + } + } + + // UL-C authentication + if (useCKey && packet->has_auth_key) { + if (mifare_ultra_3des_auth(packet->auth_key, true) == 0) { + OnErrorNG(CMD_HF_MIFAREU_SETKEY, PM3_ESOFT); + return; + } + } + + // UL-AES authentication + if (useAESKey && packet->has_auth_key) { + if (mifare_ultra_aes_auth(0, packet->auth_key, packet->use_schann, true) == 0) { + OnErrorNG(CMD_HF_MIFAREU_SETKEY, PM3_ESOFT); + return; + } } for (int i = 0; i < 4; i++) { - if (mifare_ultra_writeblock(start_block + i, key + (i * 4)) != PM3_SUCCESS) { + if (mifare_ultra_writeblock(start_block + i, packet->key + (i * 4)) != PM3_SUCCESS) { if (g_dbglevel >= DBG_INFO) Dbprintf("Write block error"); - OnError(start_block + i); + OnErrorNG(CMD_HF_MIFAREU_SETKEY, PM3_ESOFT); return; }; } if (mifare_ultra_halt()) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error"); - OnError(0); + OnErrorNG(CMD_HF_MIFAREU_SETKEY, PM3_ESOFT); return; }; - reply_mix(CMD_ACK, 1, 0, 0, 0, 0); + reply_ng(CMD_HF_MIFAREU_SETKEY, PM3_SUCCESS, NULL, 0); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); set_tracing(false); diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index 31a58a566..09bbd4f08 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -67,7 +67,7 @@ void MifareG4WriteBlk(uint8_t blockno, uint8_t *pwd, uint8_t *data, uint8_t work void MifareSetMod(uint8_t *datain); void MifarePersonalizeUID(uint8_t keyType, uint8_t perso_option, uint64_t key); -void MifareUSetKey(uint8_t arg0, uint8_t *datain); +void MifareUSetKey(mful_setkey_t *packet); void OnSuccessMagic(void); void OnErrorMagic(uint8_t reason); diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index 762767678..7805bea67 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -4927,6 +4927,8 @@ static int CmdHF14AMfUSetKey(const char *Cmd) { arg_str0("k", "key", "", "New key (16 hex bytes)"), arg_int0("i", "idx", "<0..1>", "New key index (def: 0), only for UL-AES"), arg_lit0("l", NULL, "Swap entered keys' endianness"), + arg_str0(NULL, "usekey", "", "Current UL-C 3DES or UL-AES DataProt key (16 hex bytes)"), + arg_lit0(NULL, "schann", "use secure channel. Must have usekey"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -4937,6 +4939,11 @@ static int CmdHF14AMfUSetKey(const char *Cmd) { CLIGetHexWithReturn(ctx, 1, authenticationkey, &ak_len); int key_index = arg_get_int_def(ctx, 2, 0); bool swap_endian = arg_get_lit(ctx, 3); + int use_ak_len = 0; + uint8_t use_authenticationkey[16] = {0x00}; + uint8_t *use_auth_key_ptr = use_authenticationkey; + CLIGetHexWithReturn(ctx, 4, use_authenticationkey, &use_ak_len); + bool use_schann = arg_get_lit(ctx, 5); CLIParserFree(ctx); if (ak_len != 16) { @@ -4948,6 +4955,19 @@ static int CmdHF14AMfUSetKey(const char *Cmd) { return PM3_EINVARG; } + bool has_auth_key = false; + if (use_ak_len == 16) { + has_auth_key = true; + } else if (use_ak_len != 0) { + PrintAndLogEx(WARNING, "usekey must be 16 hex bytes\n"); + return PM3_EINVARG; + } + + if (use_schann && has_auth_key == false) { + PrintAndLogEx(WARNING, "Secure channel must be called with usekey"); + return PM3_EINVARG; + } + uint64_t tagtype = GetHF14AMfU_Type(); if (tagtype == MFU_TT_UL_ERROR) { return PM3_ESOFT; @@ -4968,25 +4988,49 @@ static int CmdHF14AMfUSetKey(const char *Cmd) { } } - clearCommandBuffer(); - if ((tagtype & MFU_TT_UL_C) == MFU_TT_UL_C) { - SendCommandMIX(CMD_HF_MIFAREU_SETKEY, 1, 0, 0, auth_key_ptr, sizeof(authenticationkey)); - } else if ((tagtype & MFU_TT_UL_AES) == MFU_TT_UL_AES) { - SendCommandMIX(CMD_HF_MIFAREU_SETKEY, key_index + 2, 0, 0, auth_key_ptr, sizeof(authenticationkey)); + // Swap endianness of usekey + if (swap_endian) { + if (use_ak_len == 16) { + if (((tagtype & MFU_TT_UL_C) == MFU_TT_UL_C)) { + use_auth_key_ptr = SwapEndian64(use_authenticationkey, use_ak_len, 8); + } else if (((tagtype & MFU_TT_UL_AES) == MFU_TT_UL_AES)) { + use_auth_key_ptr = SwapEndian64(use_authenticationkey, use_ak_len, 16); + } + } } - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - if ((resp.oldarg[0] & 0xff) == 1) { - PrintAndLogEx(INFO, "New key... " _GREEN_("%s"), sprint_hex_inrow(authenticationkey, sizeof(authenticationkey))); - } else { - PrintAndLogEx(WARNING, "Failed writing at block %u", (uint8_t)(resp.oldarg[1] & 0xFF)); - return PM3_ESOFT; + if (has_auth_key) { + if (((tagtype & MFU_TT_UL_C) == MFU_TT_UL_C)) { + PrintAndLogEx(INFO, "Using 3des... " _GREEN_("%s"), sprint_hex_inrow(use_authenticationkey, use_ak_len)); + } else if (((tagtype & MFU_TT_UL_AES) == MFU_TT_UL_AES)) { + PrintAndLogEx(INFO, "Using aes... " _GREEN_("%s"), sprint_hex_inrow(use_authenticationkey, use_ak_len)); } - } else { - PrintAndLogEx(WARNING, "command execution time out"); + } + + mful_setkey_t packet = { + .has_auth_key = has_auth_key, + .use_schann = use_schann, + .key_index = key_index, + .keytype = ((tagtype & MFU_TT_UL_C) == MFU_TT_UL_C) ? 1 : 3, // 1=ULC 3=ULAES + }; + if (has_auth_key) { + memcpy(packet.auth_key, use_auth_key_ptr, 16); + } + memcpy(packet.key, auth_key_ptr, 16); + + clearCommandBuffer(); + PacketResponseNG resp; + + SendCommandNG(CMD_HF_MIFAREU_SETKEY, (uint8_t *)&packet, sizeof(packet)); + if (WaitForResponseTimeout(CMD_HF_MIFAREU_SETKEY, &resp, 1500) == false) { return PM3_ETIMEOUT; } + if (resp.status == PM3_SUCCESS) { + PrintAndLogEx(INFO, "New key... " _GREEN_("%s"), sprint_hex_inrow(authenticationkey, sizeof(authenticationkey))); + } else { + PrintAndLogEx(WARNING, "Failed writing key"); + return PM3_ESOFT; + } return PM3_SUCCESS; } @@ -6770,6 +6814,13 @@ static int CmdHF14AMfuWipe(const char *Cmd) { PrintAndLogEx(INFO, "-----+-----------------------------"); + mful_setkey_t packet = { + .has_auth_key = false, + .use_schann = false, + .key_index = 0, + }; + PacketResponseNG resp; + ulc: // UL-C - set 3-DES key @@ -6780,21 +6831,21 @@ ulc: 0x21, 0x4E, 0x41, 0x43, 0x55, 0x4F, 0x59, 0x46 }; uint8_t *def_key_ptr = SwapEndian64(defaultkey, 16, 8); + packet.keytype = 1; // UL-C + memcpy(packet.key, def_key_ptr, 16); clearCommandBuffer(); - SendCommandMIX(CMD_HF_MIFAREU_SETKEY, 1, 0, 0, def_key_ptr, sizeof(defaultkey)); - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - if ((resp.oldarg[0] & 0xff) == 1) { - PrintAndLogEx(INFO, "Ultralight C new key... " _GREEN_("%s"), sprint_hex_inrow(defaultkey, sizeof(defaultkey))); - } else { - PrintAndLogEx(WARNING, "Failed writing at block %u", (uint8_t)(resp.oldarg[1] & 0xFF)); - return PM3_ESOFT; - } - } else { + SendCommandNG(CMD_HF_MIFAREU_SETKEY, (uint8_t *)&packet, sizeof(packet)); + if (WaitForResponseTimeout(CMD_HF_MIFAREU_SETKEY, &resp, 1500) == false) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } + if (resp.status == PM3_SUCCESS) { + PrintAndLogEx(INFO, "Ultralight C new key... " _GREEN_("%s"), sprint_hex_inrow(defaultkey, sizeof(defaultkey))); + } else { + PrintAndLogEx(WARNING, "Failed writing key"); + return PM3_ESOFT; + } } ulaes: @@ -6803,30 +6854,36 @@ ulaes: // Set AES keys uint8_t defaultkey[16] = { 0 }; uint8_t *def_key_ptr = SwapEndian64(defaultkey, 16, 16); + packet.keytype = 3; // UL-AES + packet.key_index = 0; + memcpy(packet.key, def_key_ptr, 16); + clearCommandBuffer(); - SendCommandMIX(CMD_HF_MIFAREU_SETKEY, 2, 0, 0, def_key_ptr, sizeof(defaultkey)); - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - if ((resp.oldarg[0] & 0xff) != 1) { - PrintAndLogEx(WARNING, "Failed writing at block %u", (uint8_t)(resp.oldarg[1] & 0xFF)); - } - } else { + SendCommandNG(CMD_HF_MIFAREU_SETKEY, (uint8_t *)&packet, sizeof(packet)); + if (WaitForResponseTimeout(CMD_HF_MIFAREU_SETKEY, &resp, 1500) == false) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } - clearCommandBuffer(); - SendCommandMIX(CMD_HF_MIFAREU_SETKEY, 3, 0, 0, def_key_ptr, sizeof(defaultkey)); - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - if ((resp.oldarg[0] & 0xff) == 1) { - PrintAndLogEx(INFO, "Ultralight AES new key... " _GREEN_("%s"), sprint_hex_inrow(defaultkey, sizeof(defaultkey))); - } else { - PrintAndLogEx(WARNING, "Failed writing at block %u", (uint8_t)(resp.oldarg[1] & 0xFF)); - return PM3_ESOFT; - } + if (resp.status == PM3_SUCCESS) { + PrintAndLogEx(INFO, "Ultralight AES new DataProtKey... " _GREEN_("%s"), sprint_hex_inrow(defaultkey, sizeof(defaultkey))); } else { + PrintAndLogEx(WARNING, "Failed writing key"); + return PM3_ESOFT; + } + + packet.key_index = 1; + clearCommandBuffer(); + SendCommandNG(CMD_HF_MIFAREU_SETKEY, (uint8_t *)&packet, sizeof(packet)); + if (WaitForResponseTimeout(CMD_HF_MIFAREU_SETKEY, &resp, 1500) == false) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } + if (resp.status == PM3_SUCCESS) { + PrintAndLogEx(INFO, "Ultralight AES new UIDRetrKey... " _GREEN_("%s"), sprint_hex_inrow(defaultkey, sizeof(defaultkey))); + } else { + PrintAndLogEx(WARNING, "Failed writing key"); + return PM3_ESOFT; + } } diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index af897b64c..587e3664d 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -414,6 +414,15 @@ typedef struct { uint8_t data[16]; } PACKED mful_writeblock_t; +typedef struct { + bool has_auth_key; + bool use_schann; + uint8_t auth_key[16]; + uint8_t keytype; + uint8_t key_index; + uint8_t key[16]; +} PACKED mful_setkey_t; + typedef struct { uint8_t status; uint8_t CSN[8];