mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-10-23 08:43:07 +00:00
Add hf mfu cchk/aeschk and options to hf mf cauth/aesauth
This commit is contained in:
1 parent
6942c41155
commit
a26892a891
14 files changed
+775
-165
No files matched your search
+4
-4
@@ -1864,12 +1864,12 @@ static void PacketReceived(PacketCommandNG *packet) {
|
||||
MifareUReadBlock((mful_readblock_t *)packet->data.asBytes);
|
||||
break;
|
||||
}
|
||||
case CMD_HF_MIFAREUC_AUTH: {
|
||||
MifareUC_Auth(packet->oldarg[0], packet->data.asBytes);
|
||||
case CMD_HF_MIFAREU3P_AUTH: {
|
||||
MifareU3PassAuth((mful_3passauth_t *)packet->data.asBytes);
|
||||
break;
|
||||
}
|
||||
case CMD_HF_MIFAREULAES_AUTH: {
|
||||
MifareUL_AES_Auth((mfulaes_keys_t *)packet->data.asBytes);
|
||||
case CMD_HF_MIFAREU3P_CHKKEY: {
|
||||
MifareU3PassChkKeys((mful_3passchk_t *)packet->data.asBytes);
|
||||
break;
|
||||
}
|
||||
case CMD_HF_MIFAREU_READCARD: {
|
||||
|
||||
+1
-1
@@ -3043,7 +3043,7 @@ static void iso14a_set_ATS_times(const uint8_t *ats) {
|
||||
}
|
||||
|
||||
|
||||
static int GetATQA(uint8_t *resp, uint16_t resp_len, uint8_t *resp_par, const iso14a_polling_parameters_t *polling_parameters) {
|
||||
int GetATQA(uint8_t *resp, uint16_t resp_len, uint8_t *resp_par, const iso14a_polling_parameters_t *polling_parameters) {
|
||||
#define RETRY_TIMEOUT 10
|
||||
|
||||
uint32_t save_iso14a_timeout = iso14a_get_timeout();
|
||||
|
||||
@@ -161,6 +161,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
|
||||
|
||||
bool GetIso14443aCommandFromReader(uint8_t *received, uint16_t received_maxlen, uint8_t *par, int *len);
|
||||
void iso14443a_antifuzz(uint32_t flags);
|
||||
int GetATQA(uint8_t *resp, uint16_t resp_len, uint8_t *resp_par, const iso14a_polling_parameters_t *polling_parameters);
|
||||
void ReaderIso14443a(PacketCommandNG *c);
|
||||
void ReaderTransmit(const uint8_t *frame, uint16_t len, uint32_t *timing);
|
||||
void ReaderTransmitBitsPar(const uint8_t *frame, uint16_t bits, uint8_t *par, uint32_t *timing);
|
||||
|
||||
+173
-44
@@ -256,68 +256,189 @@ void MifareReadSector(uint8_t sector_no, uint8_t key_type, uint8_t *key) {
|
||||
reply_old(CMD_ACK, retval == PM3_SUCCESS, 0, 0, outbuf, 16 * num_blocks);
|
||||
}
|
||||
|
||||
void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes) {
|
||||
static int MifareUFastRead0(void) {
|
||||
uint8_t resp[18] = { 0 }; // 4 pages + crc
|
||||
uint8_t resp_par[1] = {0};
|
||||
|
||||
bool turnOffField = (arg0 == 1);
|
||||
|
||||
LED_A_ON();
|
||||
LED_B_OFF();
|
||||
LED_C_OFF();
|
||||
|
||||
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
||||
|
||||
clear_trace();
|
||||
set_tracing(true);
|
||||
|
||||
if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == 0) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card");
|
||||
OnError(0);
|
||||
return;
|
||||
};
|
||||
|
||||
if (mifare_ultra_auth(keybytes) == 0) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("Authentication failed");
|
||||
OnError(1);
|
||||
return;
|
||||
if (GetATQA(resp, sizeof(resp), resp_par, NULL) == 0) {
|
||||
return 0;
|
||||
}
|
||||
uint8_t read0[4] = {0x30, 0x00, 0x00, 0x00};
|
||||
AddCrc14A(read0, 2);
|
||||
ReaderTransmit(read0, sizeof(read0), NULL);
|
||||
|
||||
if (turnOffField) {
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||
LEDsoff();
|
||||
// Receive 4 pages, 2 Byte CRC
|
||||
if (ReaderReceive(resp, sizeof(resp), resp_par) != 18) {
|
||||
return 0;
|
||||
}
|
||||
reply_mix(CMD_ACK, 1, 0, 0, 0, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void MifareUL_AES_Auth(mfulaes_keys_t *packet) {
|
||||
#define MIFAREU3P_KEY_SIZE 16
|
||||
#define MIFAREULC_KEY_INDEX 3
|
||||
|
||||
// checks one key.
|
||||
// fast select, tries 5 times to select
|
||||
//
|
||||
// return:
|
||||
// 4 = failed to select
|
||||
// 3 = failed auth
|
||||
// 2 = timeout
|
||||
// 1 = failed auth
|
||||
// 0 = correct
|
||||
|
||||
static uint8_t chkKey3Pass(uint8_t keyno, uint8_t *keybytes, uint32_t *auths, bool check_answer) {
|
||||
|
||||
uint8_t i = 0, res = 2;
|
||||
bool selected = false;
|
||||
while (i < 5) {
|
||||
if (MifareUFastRead0() == 0) {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
selected = true;
|
||||
if (g_dbglevel >= DBG_EXTENDED) Dbhexdump(MIFAREU3P_KEY_SIZE, keybytes, false);
|
||||
if (keyno == MIFAREULC_KEY_INDEX) {
|
||||
res = mifare_ultra_3des_auth(keybytes, check_answer) == 1 ? 0 : 1; // 0 = correct, 1 = failed auth
|
||||
} else {
|
||||
bool use_schann = false;
|
||||
res = mifare_ultra_aes_auth(keyno, keybytes, use_schann, check_answer) == 1 ? 0 : 1; // 0 = correct, 1 = failed auth
|
||||
}
|
||||
(*auths)++;
|
||||
break;
|
||||
}
|
||||
if (selected == false) {
|
||||
Dbprintf("chkKey: Failed at fast selecting the card!");
|
||||
res = 4;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void MifareU3PassAuth(mful_3passauth_t *packet) {
|
||||
|
||||
LED_A_ON();
|
||||
LED_B_OFF();
|
||||
LED_C_OFF();
|
||||
uint32_t auths = 0;
|
||||
int res = PM3_ESOFT;
|
||||
struct rp {
|
||||
uint32_t auths;
|
||||
uint32_t ticks;
|
||||
} PACKED rpayload;
|
||||
|
||||
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
||||
|
||||
clear_trace();
|
||||
set_tracing(true);
|
||||
|
||||
if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == 0) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card");
|
||||
OnErrorNG(CMD_HF_MIFAREULAES_AUTH, PM3_ESOFT);
|
||||
return;
|
||||
};
|
||||
uint32_t ti = GetTickCount();
|
||||
|
||||
if (mifare_ultra_aes_auth(packet->keyno, packet->key, packet->use_schann) == 0) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("Authentication failed");
|
||||
OnErrorNG(CMD_HF_MIFAREULAES_AUTH, PM3_ESOFT);
|
||||
return;
|
||||
for (uint16_t r = 0; r < 1 + packet->retries; r++) {
|
||||
WDT_HIT();
|
||||
if (chkKey3Pass(packet->keyno, packet->key, &auths, packet->check_answer) == 0) {
|
||||
res = PM3_SUCCESS;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
rpayload.auths = auths;
|
||||
rpayload.ticks = GetTickCountDelta(ti);
|
||||
if (g_dbglevel >= DBG_ERROR) {
|
||||
if (res != PM3_SUCCESS) {
|
||||
Dbprintf("Authentication failed");
|
||||
}
|
||||
}
|
||||
reply_ng(CMD_HF_MIFAREU3P_AUTH, res, (uint8_t *)&rpayload, sizeof(rpayload));
|
||||
if (packet->turn_off_field) {
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||
LEDsoff();
|
||||
}
|
||||
reply_ng(CMD_HF_MIFAREULAES_AUTH, PM3_SUCCESS, NULL, 0);
|
||||
}
|
||||
|
||||
void MifareU3PassChkKeys(mful_3passchk_t *packet) {
|
||||
static uint8_t foundkeys = 0;
|
||||
uint8_t keysize = packet->segment != 4 ? MIFAREU3P_KEY_SIZE / 4 : MIFAREU3P_KEY_SIZE;
|
||||
|
||||
int oldbg = g_dbglevel;
|
||||
int res = PM3_ESOFT;
|
||||
struct rp {
|
||||
uint32_t auths;
|
||||
uint32_t ticks;
|
||||
uint8_t key[16];
|
||||
} PACKED rpayload;
|
||||
uint32_t auths = 0;
|
||||
|
||||
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
||||
|
||||
LEDsoff();
|
||||
LED_A_ON();
|
||||
|
||||
uint32_t ti = GetTickCount();
|
||||
if (packet->firstchunk) {
|
||||
clear_trace();
|
||||
set_tracing(false);
|
||||
|
||||
if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == 0) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("ChkKeys_fast: Can't select card (ALL)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
CHK_TIMEOUT();
|
||||
}
|
||||
|
||||
// clear debug level. We are expecting lots of authentication failures...
|
||||
g_dbglevel = DBG_NONE;
|
||||
|
||||
for (uint16_t i = 0; i < packet->nkeys; ++i) {
|
||||
|
||||
// Allow button press / usb cmd to interrupt device
|
||||
if (BUTTON_PRESS() || data_available()) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
WDT_HIT();
|
||||
uint8_t fullkeybytes[MIFAREU3P_KEY_SIZE] = {0};
|
||||
memcpy(fullkeybytes, packet->ref_key, MIFAREU3P_KEY_SIZE);
|
||||
if (keysize == MIFAREU3P_KEY_SIZE) {
|
||||
if (packet->xor_ref_key) {
|
||||
for (uint8_t j = 0; j < keysize; ++j) {
|
||||
fullkeybytes[j] ^= packet->data[j + (i * keysize)];
|
||||
}
|
||||
} else {
|
||||
memcpy(fullkeybytes, packet->data + (i * keysize), keysize);
|
||||
}
|
||||
} else {
|
||||
if (packet->xor_ref_key) {
|
||||
for (uint8_t j = 0; j < keysize; ++j) {
|
||||
fullkeybytes[j + (packet->segment * keysize)] ^= packet->data[j + (i * keysize)];
|
||||
}
|
||||
} else {
|
||||
memcpy(fullkeybytes + (packet->segment * keysize), packet->data + (i * keysize), keysize);
|
||||
}
|
||||
}
|
||||
if (chkKey3Pass(packet->key_index, fullkeybytes, &auths, packet->check_answer) == 0) {
|
||||
foundkeys++;
|
||||
memcpy(rpayload.key, fullkeybytes, MIFAREU3P_KEY_SIZE);
|
||||
res = PM3_SUCCESS;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
out:
|
||||
rpayload.auths = auths;
|
||||
rpayload.ticks = GetTickCountDelta(ti);
|
||||
reply_ng(CMD_HF_MIFAREU3P_CHKKEY, res, (uint8_t *)&rpayload, sizeof(rpayload));
|
||||
LEDsoff();
|
||||
if (foundkeys || packet->lastchunk) {
|
||||
set_tracing(false);
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||
BigBuf_free();
|
||||
BigBuf_Clear_ext(false);
|
||||
}
|
||||
g_dbglevel = oldbg;
|
||||
}
|
||||
|
||||
|
||||
// Arg0 = BlockNo,
|
||||
// Arg1 = UsePwd bool
|
||||
// datain = PWD bytes,
|
||||
@@ -344,7 +465,7 @@ void MifareUReadBlock(mful_readblock_t *packet) {
|
||||
|
||||
// UL-C authentication
|
||||
if (useCKey) {
|
||||
if (mifare_ultra_auth(packet->key) == 0) {
|
||||
if (mifare_ultra_3des_auth(packet->key, true) == 0) {
|
||||
OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT);
|
||||
return;
|
||||
}
|
||||
@@ -352,7 +473,7 @@ void MifareUReadBlock(mful_readblock_t *packet) {
|
||||
|
||||
// UL-AES authentication, hardcode to use keyno 0
|
||||
if (useAESKey) {
|
||||
if (mifare_ultra_aes_auth(0, packet->key, packet->use_schann) == 0) {
|
||||
if (mifare_ultra_aes_auth(0, packet->key, packet->use_schann, true) == 0) {
|
||||
OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT);
|
||||
return;
|
||||
}
|
||||
@@ -427,7 +548,7 @@ void MifareUReadCard(mful_readblock_t *packet) {
|
||||
|
||||
// UL-C authentication
|
||||
if (useCKey) {
|
||||
if (mifare_ultra_auth(packet->key) == 0) {
|
||||
if (mifare_ultra_3des_auth(packet->key, true) == 0) {
|
||||
OnErrorNG(CMD_HF_MIFAREU_READCARD, PM3_ESOFT);
|
||||
return;
|
||||
}
|
||||
@@ -435,7 +556,7 @@ void MifareUReadCard(mful_readblock_t *packet) {
|
||||
|
||||
// UL-AES authentication
|
||||
if (useAESKey) {
|
||||
if (mifare_ultra_aes_auth(0, packet->key, schann) == 0) {
|
||||
if (mifare_ultra_aes_auth(0, packet->key, schann, true) == 0) {
|
||||
OnErrorNG(CMD_HF_MIFAREU_READCARD, PM3_ESOFT);
|
||||
return;
|
||||
}
|
||||
@@ -615,7 +736,7 @@ static void MifareUWriteBlockEx(mful_writeblock_t *packet, bool reply) {
|
||||
|
||||
// UL-C authentication
|
||||
if (useCKey) {
|
||||
if (mifare_ultra_auth(packet->key) == 0) {
|
||||
if (mifare_ultra_3des_auth(packet->key, true) == 0) {
|
||||
OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT);
|
||||
return;
|
||||
}
|
||||
@@ -623,7 +744,7 @@ static void MifareUWriteBlockEx(mful_writeblock_t *packet, bool reply) {
|
||||
|
||||
// UL-AES authentication
|
||||
if (useAESKey) {
|
||||
if (mifare_ultra_aes_auth(0, packet->key, packet->use_schann) == 0) {
|
||||
if (mifare_ultra_aes_auth(0, packet->key, packet->use_schann, true) == 0) {
|
||||
OnErrorNG(CMD_HF_MIFAREU_READBL, PM3_ESOFT);
|
||||
return;
|
||||
}
|
||||
@@ -699,7 +820,15 @@ void MifareUWriteBlockCompat(mful_writeblock_t *packet) {
|
||||
|
||||
// UL-C authentication
|
||||
if (useCKey) {
|
||||
if (mifare_ultra_auth(packet->key) == 0) {
|
||||
if (mifare_ultra_3des_auth(packet->key, true) == 0) {
|
||||
OnErrorNG(CMD_HF_MIFAREU_WRITEBL_COMPAT, PM3_ESOFT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// UL-AES authentication
|
||||
if (useAESKey) {
|
||||
if (mifare_ultra_aes_auth(0, packet->key, packet->use_schann, true) == 0) {
|
||||
OnErrorNG(CMD_HF_MIFAREU_WRITEBL_COMPAT, PM3_ESOFT);
|
||||
return;
|
||||
}
|
||||
|
||||
+3
-3
@@ -26,14 +26,14 @@ void MifareReadSector(uint8_t sector_no, uint8_t key_type, uint8_t *key);
|
||||
void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain);
|
||||
|
||||
void MifareUReadBlock(mful_readblock_t *packet);
|
||||
void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes);
|
||||
|
||||
void MifareUL_AES_Auth(mfulaes_keys_t *packet);
|
||||
void MifareUReadCard(mful_readblock_t *packet);
|
||||
|
||||
void MifareUWriteBlockCompat(mful_writeblock_t *packet);
|
||||
void MifareUWriteBlock(mful_writeblock_t *packet);
|
||||
|
||||
void MifareU3PassAuth(mful_3passauth_t *packet);
|
||||
void MifareU3PassChkKeys(mful_3passchk_t *packet);
|
||||
|
||||
void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, bool calibrate, uint8_t *key);
|
||||
void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, uint8_t *key, uint8_t forceDetectDist);
|
||||
|
||||
|
||||
+61
-49
@@ -380,12 +380,11 @@ int mifare_ul_ev1_auth(uint8_t *keybytes, uint8_t *pack) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mifare_ultra_auth(uint8_t *keybytes) {
|
||||
int mifare_ultra_3des_auth(uint8_t *keybytes, bool check_answer) {
|
||||
|
||||
/// 3des2k
|
||||
uint8_t random_a[8] = {1, 1, 1, 1, 1, 1, 1, 1};
|
||||
uint8_t random_a[8] = {1, 1, 1, 1, 1, 1, 1, 2};
|
||||
uint8_t random_b[8] = {0x00};
|
||||
uint8_t enc_random_b[8] = {0x00};
|
||||
uint8_t rnd_ab[16] = {0x00};
|
||||
uint8_t IV[8] = {0x00};
|
||||
uint8_t key[16] = {0x00};
|
||||
@@ -398,77 +397,71 @@ int mifare_ultra_auth(uint8_t *keybytes) {
|
||||
// REQUEST AUTHENTICATION
|
||||
len = mifare_sendcmd_short(NULL, CRYPT_NONE, MIFARE_ULC_AUTH_1, 0x00, resp, sizeof(resp), respPar, NULL);
|
||||
if (len != 11) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x", resp[0]);
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x - expected 11 got " _RED_("%u"), resp[0], len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// tag nonce.
|
||||
memcpy(enc_random_b, resp + 1, 8);
|
||||
|
||||
// decrypt nonce.
|
||||
tdes_nxp_receive((void *)enc_random_b, (void *)random_b, sizeof(random_b), (const void *)key, IV, 2);
|
||||
tdes_nxp_receive((void *)(resp + 1), (void *)random_b, sizeof(random_b), (const void *)key, IV, 2);
|
||||
rol(random_b, 8);
|
||||
memcpy(rnd_ab, random_a, 8);
|
||||
memcpy(rnd_ab + 8, random_b, 8);
|
||||
|
||||
if (g_dbglevel >= DBG_EXTENDED) {
|
||||
Dbprintf("enc_B: %02x %02x %02x %02x %02x %02x %02x %02x",
|
||||
enc_random_b[0], enc_random_b[1], enc_random_b[2], enc_random_b[3], enc_random_b[4], enc_random_b[5], enc_random_b[6], enc_random_b[7]);
|
||||
Dbprintf("enc_B:");
|
||||
Dbhexdump(8, resp + 1, false);
|
||||
|
||||
Dbprintf(" B: %02x %02x %02x %02x %02x %02x %02x %02x",
|
||||
random_b[0], random_b[1], random_b[2], random_b[3], random_b[4], random_b[5], random_b[6], random_b[7]);
|
||||
Dbprintf("B:");
|
||||
Dbhexdump(8, random_b, false);
|
||||
|
||||
Dbprintf("rnd_ab: %02x %02x %02x %02x %02x %02x %02x %02x",
|
||||
rnd_ab[0], rnd_ab[1], rnd_ab[2], rnd_ab[3], rnd_ab[4], rnd_ab[5], rnd_ab[6], rnd_ab[7]);
|
||||
|
||||
Dbprintf("rnd_ab: %02x %02x %02x %02x %02x %02x %02x %02x",
|
||||
rnd_ab[8], rnd_ab[9], rnd_ab[10], rnd_ab[11], rnd_ab[12], rnd_ab[13], rnd_ab[14], rnd_ab[15]);
|
||||
Dbprintf("rnd_ab:");
|
||||
Dbhexdump(16, rnd_ab, false);
|
||||
}
|
||||
|
||||
// encrypt out, in, length, key, iv
|
||||
tdes_nxp_send(rnd_ab, rnd_ab, sizeof(rnd_ab), key, enc_random_b, 2);
|
||||
// reuse rnd_ab as enc_rnd_ab
|
||||
if (check_answer) {
|
||||
tdes_nxp_send((void *)rnd_ab, (void *)rnd_ab, sizeof(rnd_ab), (const void *)key, resp + 1, 2);
|
||||
} else {
|
||||
tdes_nxp_send((void *)(rnd_ab + sizeof(random_a)), (void *)(rnd_ab + sizeof(random_a)), sizeof(random_b), (const void *)key, rnd_ab, 2);
|
||||
// tdes_nxp_send destroys IV, let's restore it
|
||||
memcpy(rnd_ab, random_a, 8);
|
||||
}
|
||||
|
||||
len = mifare_sendcmd(MIFARE_ULC_AUTH_2, rnd_ab, sizeof(rnd_ab), resp, sizeof(resp), respPar, NULL);
|
||||
if ((!check_answer) && (len == 11)) {
|
||||
return 1;
|
||||
}
|
||||
if (len != 11) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x", resp[0]);
|
||||
if (g_dbglevel >= DBG_INFO) Dbprintf("Cmd Error: %02x - expected 11 got " _RED_("%u"), resp[0], len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t enc_resp[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
uint8_t resp_random_a[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
memcpy(enc_resp, resp + 1, 8);
|
||||
|
||||
// reuse random_b as resp_random_a
|
||||
// decrypt out, in, length, key, iv
|
||||
tdes_nxp_receive(enc_resp, resp_random_a, 8, key, enc_random_b, 2);
|
||||
if (memcmp(resp_random_a, random_a, 8) != 0) {
|
||||
tdes_nxp_receive(resp + 1, random_b, 8, key, rnd_ab + 8, 2);
|
||||
if (g_dbglevel >= DBG_EXTENDED) {
|
||||
Dbprintf("e_AB:");
|
||||
Dbhexdump(16, rnd_ab, false);
|
||||
|
||||
Dbprintf("A sent:");
|
||||
Dbhexdump(8, random_a, false);
|
||||
|
||||
Dbprintf("A' recv:");
|
||||
Dbhexdump(8, random_b, false);
|
||||
}
|
||||
rol(random_a, 8);
|
||||
if (memcmp(random_b, random_a, 8) != 0) {
|
||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("failed authentication");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (g_dbglevel >= DBG_EXTENDED) {
|
||||
Dbprintf("e_AB: %02x %02x %02x %02x %02x %02x %02x %02x",
|
||||
rnd_ab[0], rnd_ab[1], rnd_ab[2], rnd_ab[3],
|
||||
rnd_ab[4], rnd_ab[5], rnd_ab[6], rnd_ab[7]);
|
||||
|
||||
Dbprintf("e_AB: %02x %02x %02x %02x %02x %02x %02x %02x",
|
||||
rnd_ab[8], rnd_ab[9], rnd_ab[10], rnd_ab[11],
|
||||
rnd_ab[12], rnd_ab[13], rnd_ab[14], rnd_ab[15]);
|
||||
|
||||
Dbprintf("a: %02x %02x %02x %02x %02x %02x %02x %02x",
|
||||
random_a[0], random_a[1], random_a[2], random_a[3],
|
||||
random_a[4], random_a[5], random_a[6], random_a[7]);
|
||||
|
||||
Dbprintf("b: %02x %02x %02x %02x %02x %02x %02x %02x",
|
||||
resp_random_a[0], resp_random_a[1], resp_random_a[2], resp_random_a[3],
|
||||
resp_random_a[4], resp_random_a[5], resp_random_a[6], resp_random_a[7]);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes, bool schann) {
|
||||
int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes, bool schann, bool check_answer) {
|
||||
|
||||
/// aes-128
|
||||
uint8_t random_a[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
|
||||
uint8_t random_a[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2};
|
||||
uint8_t random_b[16] = { 0x00 };
|
||||
uint8_t rnd_ab[32] = { 0x00 };
|
||||
uint8_t enc_rnd_ab[32] = { 0x00 };
|
||||
@@ -513,12 +506,20 @@ int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes, bool schann) {
|
||||
}
|
||||
|
||||
// encrypt reader response
|
||||
memset(IV, 0, 16);
|
||||
mbedtls_aes_setkey_enc(&actx, key, 128);
|
||||
mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_ENCRYPT, sizeof(enc_rnd_ab), IV, rnd_ab, enc_rnd_ab);
|
||||
if (check_answer) {
|
||||
memset(IV, 0, 16);
|
||||
mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_ENCRYPT, sizeof(rnd_ab), IV, rnd_ab, enc_rnd_ab);
|
||||
} else {
|
||||
memcpy(enc_rnd_ab, rnd_ab, sizeof(rnd_ab));
|
||||
mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_ENCRYPT, sizeof(random_b), rnd_ab, rnd_ab + sizeof(random_a), enc_rnd_ab + sizeof(random_a));
|
||||
}
|
||||
|
||||
// send & receive
|
||||
len = mifare_sendcmd(MIFARE_ULAES_AUTH_2, enc_rnd_ab, sizeof(enc_rnd_ab), resp, sizeof(resp), respPar, NULL);
|
||||
if ((! check_answer) && (len == 19)) {
|
||||
return 1;
|
||||
}
|
||||
if (len != 19) {
|
||||
if (g_dbglevel >= DBG_INFO) {
|
||||
Dbprintf("Cmd Error: %02x - expected 19 got " _RED_("%u"), resp[0], len);
|
||||
@@ -531,7 +532,17 @@ int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes, bool schann) {
|
||||
uint8_t rec_rnd_a[16] = {0};
|
||||
mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_DECRYPT, sizeof(rec_rnd_a), IV, resp + 1, rec_rnd_a);
|
||||
mbedtls_aes_free(&actx);
|
||||
if (g_dbglevel >= DBG_EXTENDED) {
|
||||
Dbprintf("e_AB:");
|
||||
Dbhexdump(32, rnd_ab, false);
|
||||
|
||||
Dbprintf("A sent:");
|
||||
Dbhexdump(16, random_a, false);
|
||||
|
||||
Dbprintf("A' recv:");
|
||||
Dbhexdump(16, rec_rnd_a, false);
|
||||
}
|
||||
rol(random_a, 16);
|
||||
if (memcmp(rec_rnd_a, random_a, 16) != 0) {
|
||||
if (g_dbglevel >= DBG_INFO) {
|
||||
Dbprintf("failed authentication");
|
||||
@@ -544,7 +555,7 @@ int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes, bool schann) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Session key calculation setion
|
||||
// Session key calculation section
|
||||
|
||||
// clear global session variable
|
||||
init_secure_session();
|
||||
@@ -555,7 +566,8 @@ int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes, bool schann) {
|
||||
uint8_t *ra = random_a;
|
||||
uint8_t *rb = random_b;
|
||||
|
||||
// do we need to unroll it?
|
||||
// we need to unroll randoms
|
||||
ror(ra, 16);
|
||||
ror(rb, 16);
|
||||
|
||||
uint8_t session_vec[] = {
|
||||
|
||||
+2
-2
@@ -104,8 +104,8 @@ int mifare_classic_value(struct Crypto1State *pcs, uint8_t blockNo, uint8_t *blo
|
||||
|
||||
// Ultralight/NTAG...
|
||||
int mifare_ul_ev1_auth(uint8_t *keybytes, uint8_t *pack);
|
||||
int mifare_ultra_auth(uint8_t *keybytes);
|
||||
int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes, bool schann);
|
||||
int mifare_ultra_3des_auth(uint8_t *keybytes, bool check_answer);
|
||||
int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes, bool schann, bool check_answer);
|
||||
int mifare_ultra_readblock(uint8_t blockNo, uint8_t *blockData);
|
||||
int mifare_ultra_writeblock_compat(uint8_t blockNo, uint8_t *blockData);
|
||||
int mifare_ultra_writeblock(uint8_t blockNo, uint8_t *blockData);
|
||||
|
||||
@@ -382,8 +382,10 @@ def main():
|
||||
description=main.__doc__,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter
|
||||
)
|
||||
parser.add_argument('-c', '--challenges', help='Set number of challenges to collect (default:1000)', type=int, default=1000)
|
||||
parser.add_argument('-t', '--threads', help='Set number of threads to use for key recovery (default:1)', type=int, default=1)
|
||||
parser.add_argument('-c', '--challenges', help='Set number of challenges to collect (default:1000)',
|
||||
type=int, default=1000)
|
||||
parser.add_argument('-t', '--threads', help='Set number of threads to use for key recovery (default:1)',
|
||||
type=int, default=16)
|
||||
parser.add_argument('-d', '--debug', action='store_true', help='Enable debug mode')
|
||||
parser.add_argument('-j', '--json', help='Path to JSON file to load or save collected challenges')
|
||||
parser.add_argument('-o', '--offline', action='store_true', help='Use offline mode with pre-collected challenges')
|
||||
|
||||
+434
-30
@@ -32,6 +32,7 @@
|
||||
#include "cmdmain.h"
|
||||
#include "amiibo.h" // amiiboo fcts
|
||||
#include "base64.h"
|
||||
#include "util_posix.h" // msclock
|
||||
#include "fileutils.h" // saveFile
|
||||
#include "cmdtrace.h" // trace list
|
||||
#include "preferences.h" // setDeviceDebugLevel
|
||||
@@ -60,6 +61,9 @@
|
||||
#define MAX_ST25TN512 0x3F
|
||||
#define MAX_ST25TN01K 0x3F
|
||||
|
||||
#define MIFAREU3P_KEY_SIZE 16
|
||||
#define MIFAREULC_KEY_INDEX 3
|
||||
|
||||
static int CmdHelp(const char *Cmd);
|
||||
|
||||
static const char *key_type[] = { "DataProtKey", "UIDRetrKey", "OriginalityKey" };
|
||||
@@ -517,38 +521,38 @@ Default AES key is 00-00h. Both the data and UID one.
|
||||
Data key is 00, UID is 01. Authenticity is 02h
|
||||
Auth is 1A[Key ID][CRC] - AF[RndB] - AF[RndA][RndB'] - 00[RndA']
|
||||
*/
|
||||
static int ulaes_requestAuthentication(const uint8_t *key, uint8_t keyno, bool switch_off_field, bool schann) {
|
||||
|
||||
mfulaes_keys_t payload = {
|
||||
static int ul3pass_authentication(const uint8_t *key, uint8_t keyno, bool switch_off_field, int retries, uint32_t *auths, uint32_t *ms, bool schann, bool check_answer) {
|
||||
// keyno < 3: ULAES
|
||||
// keyno = 3: ULC
|
||||
mful_3passauth_t payload = {
|
||||
.turn_off_field = switch_off_field,
|
||||
.check_answer = check_answer,
|
||||
.use_schann = schann,
|
||||
.keyno = keyno
|
||||
.keyno = keyno,
|
||||
.retries = retries,
|
||||
};
|
||||
memcpy(payload.key, key, sizeof(payload.key));
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_HF_MIFAREULAES_AUTH, (uint8_t *)&payload, sizeof(payload));
|
||||
SendCommandNG(CMD_HF_MIFAREU3P_AUTH, (uint8_t *)&payload, sizeof(payload));
|
||||
PacketResponseNG resp;
|
||||
if (WaitForResponseTimeout(CMD_HF_MIFAREULAES_AUTH, &resp, 1500) == false) {
|
||||
if (WaitForResponseTimeout(CMD_HF_MIFAREU3P_AUTH, &resp, 1500 + (retries * 15)) == false) {
|
||||
return PM3_ETIMEOUT;
|
||||
}
|
||||
struct rp {
|
||||
uint32_t auths;
|
||||
uint32_t ticks;
|
||||
} PACKED;
|
||||
struct rp *rpayload = (struct rp *) resp.data.asBytes;
|
||||
|
||||
if (auths != NULL) {
|
||||
*auths += rpayload->auths;
|
||||
}
|
||||
if (ms != NULL) {
|
||||
*ms += rpayload->ticks;
|
||||
}
|
||||
return resp.status;
|
||||
}
|
||||
|
||||
static int ulc_authentication(const uint8_t *key, bool switch_off_field) {
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandMIX(CMD_HF_MIFAREUC_AUTH, switch_off_field, 0, 0, key, 16);
|
||||
PacketResponseNG resp;
|
||||
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
|
||||
return PM3_ETIMEOUT;
|
||||
}
|
||||
if (resp.oldarg[0] == 1) {
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
static int trace_mfuc_try_key(uint8_t *key, int state, uint8_t (*authdata)[16]) {
|
||||
uint8_t iv[8] = {0};
|
||||
uint8_t RndB[8] = {0};
|
||||
@@ -629,7 +633,7 @@ static int try_default_3des_keys(bool override, uint8_t **correct_key) {
|
||||
|
||||
for (uint8_t i = 0; i < ARRAYLEN(default_3des_keys); ++i) {
|
||||
uint8_t *key = default_3des_keys[i];
|
||||
if (ulc_authentication(key, true) == PM3_SUCCESS) {
|
||||
if (ul3pass_authentication(key, MIFAREULC_KEY_INDEX, true, 0, NULL, NULL, false, true) == PM3_SUCCESS) {
|
||||
*correct_key = key;
|
||||
res = PM3_SUCCESS;
|
||||
break;
|
||||
@@ -666,7 +670,7 @@ static int try_default_aes_keys(bool override, bool use_schann) {
|
||||
|
||||
for (uint8_t keyno = 0; keyno < 3; keyno++) {
|
||||
|
||||
if (ulaes_requestAuthentication(key, keyno, true, use_schann) == PM3_SUCCESS) {
|
||||
if (ul3pass_authentication(key, keyno, true, 0, NULL, NULL, use_schann, true) == PM3_SUCCESS) {
|
||||
|
||||
char keystr[20] = {0};
|
||||
switch (keyno) {
|
||||
@@ -706,13 +710,13 @@ static int ul_auth_select(iso14a_card_select_t *card, uint64_t tagtype, bool has
|
||||
|
||||
if (hasAuthKey && (tagtype & MFU_TT_UL_C)) {
|
||||
//will select card automatically and close connection on error
|
||||
if (ulc_authentication(authkey, false) != PM3_SUCCESS) {
|
||||
if (ul3pass_authentication(authkey, MIFAREULC_KEY_INDEX, false, 0, NULL, NULL, false, true) != PM3_SUCCESS) {
|
||||
PrintAndLogEx(WARNING, "Authentication Failed UL-C");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
} else if (hasAuthKey && (tagtype & MFU_TT_UL_AES)) {
|
||||
//will select card automatically and close connection on error
|
||||
if (ulaes_requestAuthentication(authkey, 0, false, use_schann) != PM3_SUCCESS) {
|
||||
if (ul3pass_authentication(authkey, 0, false, 0, NULL, NULL, use_schann, true) != PM3_SUCCESS) {
|
||||
PrintAndLogEx(WARNING, "Authentication Failed UL-AES");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
@@ -4240,6 +4244,128 @@ static int CmdHF14AMfUSim(const char *Cmd) {
|
||||
return CmdHF14ASim(Cmd);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// Ultralight C & AES helpers
|
||||
//-------------------------------------------------------------------------------
|
||||
|
||||
static int mfu_3pass_load_keys(uint8_t **pkeyBlock, uint32_t *pkeycnt, const char *filename, int fnlen, uint8_t keysize) {
|
||||
// Handle Keys
|
||||
*pkeycnt = 0;
|
||||
*pkeyBlock = NULL;
|
||||
uint8_t *p;
|
||||
// Handle user supplied dictionary file
|
||||
if (fnlen > 0) {
|
||||
uint32_t loaded_numKeys = 0;
|
||||
uint8_t *keyBlock_tmp = NULL;
|
||||
int res = loadFileDICTIONARY_safe(filename, (void **) &keyBlock_tmp, keysize, &loaded_numKeys);
|
||||
if (res != PM3_SUCCESS || loaded_numKeys == 0 || keyBlock_tmp == NULL) {
|
||||
PrintAndLogEx(FAILED, "An error occurred while loading the dictionary!");
|
||||
free(keyBlock_tmp);
|
||||
free(*pkeyBlock);
|
||||
return PM3_EFILE;
|
||||
} else {
|
||||
p = realloc(*pkeyBlock, (*pkeycnt + loaded_numKeys) * keysize);
|
||||
if (p == NULL) {
|
||||
PrintAndLogEx(WARNING, "Failed to allocate memory");
|
||||
free(keyBlock_tmp);
|
||||
free(*pkeyBlock);
|
||||
return PM3_EMALLOC;
|
||||
}
|
||||
*pkeyBlock = p;
|
||||
memcpy(*pkeyBlock + *pkeycnt * keysize, keyBlock_tmp, loaded_numKeys * keysize);
|
||||
*pkeycnt += loaded_numKeys;
|
||||
free(keyBlock_tmp);
|
||||
}
|
||||
}
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
#define MIFAREU3P_CHKKEY_HEADER (3 + MIFAREU3P_KEY_SIZE)
|
||||
|
||||
static int mfu_3pass_check_keys(uint8_t key_index, uint8_t firstChunk, uint8_t lastChunk,
|
||||
uint32_t nkeys, int segment, uint8_t *ref_key, bool xor_ref_key, uint8_t *keyBlock,
|
||||
bool verbose, bool quiet, uint32_t *auths, uint32_t *ms, bool check_answer) {
|
||||
// send keychunk
|
||||
clearCommandBuffer();
|
||||
|
||||
mful_3passchk_t payload = {
|
||||
.key_index = key_index,
|
||||
.firstchunk = firstChunk,
|
||||
.lastchunk = lastChunk,
|
||||
.xor_ref_key = xor_ref_key,
|
||||
.segment = segment != -1 ? segment : 4,
|
||||
.check_answer = check_answer,
|
||||
.nkeys = nkeys
|
||||
};
|
||||
struct rp {
|
||||
uint32_t auths;
|
||||
uint32_t ticks;
|
||||
uint8_t key[16];
|
||||
} PACKED;
|
||||
uint8_t keysize = segment != -1 ? MIFAREU3P_KEY_SIZE / 4 : MIFAREU3P_KEY_SIZE;
|
||||
memcpy(payload.ref_key, ref_key, MIFAREU3P_KEY_SIZE);
|
||||
if (nkeys * keysize > sizeof(payload.data)) {
|
||||
PrintAndLogEx(ERR, "Key chunk size exceeds payload size");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
memcpy(payload.data, keyBlock, nkeys * keysize);
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_HF_MIFAREU3P_CHKKEY, (uint8_t *)&payload, sizeof(payload));
|
||||
|
||||
PacketResponseNG resp;
|
||||
|
||||
uint32_t timeout = 0;
|
||||
while (WaitForResponseTimeout(CMD_HF_MIFAREU3P_CHKKEY, &resp, 2000) == false) {
|
||||
|
||||
while (kbd_enter_pressed()) {
|
||||
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
|
||||
PrintAndLogEx(INFO, "aborted via keyboard!");
|
||||
return PM3_EOPABORTED;
|
||||
}
|
||||
|
||||
if (quiet == false) {
|
||||
PrintAndLogEx((timeout) ? NORMAL : INFO, "." NOLF);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
timeout++;
|
||||
|
||||
// max timeout for one chunk of 85keys, 60*3sec = 180seconds
|
||||
// s70 with 40*2 keys to check, 80*85 = 6800 auth.
|
||||
// takes about 97s, still some margin before abort
|
||||
// timeout = 180 => ~360s @ Mifare Classic 1k @ ~2300 keys in dict
|
||||
// ~2300 keys @ Mifare Classic 1k => ~620s
|
||||
if (timeout > 60 * 12) {
|
||||
PrintAndLogEx(WARNING, "\nNo response from Proxmark3. Aborting...");
|
||||
return PM3_ETIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
if (timeout && (quiet == false)) {
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
}
|
||||
// time to convert the returned data.
|
||||
struct rp *rpayload = (struct rp *) resp.data.asBytes;
|
||||
|
||||
if (auths != NULL) {
|
||||
*auths += rpayload->auths;
|
||||
}
|
||||
if (ms != NULL) {
|
||||
*ms += rpayload->ticks;
|
||||
}
|
||||
|
||||
if (resp.status == PM3_SUCCESS) {
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(SUCCESS, "Target key " _GREEN_("%1u") " -- found valid key [ " _GREEN_("%s") " ]",
|
||||
key_index,
|
||||
sprint_hex_inrow(rpayload->key, MIFAREU3P_KEY_SIZE)
|
||||
);
|
||||
}
|
||||
return resp.status;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// Ultralight C Methods
|
||||
//-------------------------------------------------------------------------------
|
||||
@@ -4260,6 +4386,8 @@ static int CmdHF14AMfUCAuth(const char *Cmd) {
|
||||
arg_str0(NULL, "key", "<hex>", "Authentication key (16 bytes in hex)"),
|
||||
arg_lit0("l", NULL, "Swap entered key's endianness"),
|
||||
arg_lit0("k", NULL, "Keep field on (only if a key is provided)"),
|
||||
arg_int0("r", "retries", "<n>", "Number of retries with provided key (def: 0)"),
|
||||
arg_lit0("n", "nocheck", "Skip checking tag answer correctness (only if a key is provided)"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
@@ -4270,12 +4398,26 @@ static int CmdHF14AMfUCAuth(const char *Cmd) {
|
||||
CLIGetHexWithReturn(ctx, 1, authenticationkey, &ak_len);
|
||||
bool swap_endian = arg_get_lit(ctx, 2);
|
||||
bool keep_field_on = arg_get_lit(ctx, 3);
|
||||
int retries = arg_get_int_def(ctx, 4, 0);
|
||||
bool check_answer = !arg_get_lit(ctx, 5);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
if (ak_len != 16 && ak_len != 0) {
|
||||
PrintAndLogEx(WARNING, "ERROR: Key is incorrect length");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
if (retries < 0 || retries > 10000) {
|
||||
PrintAndLogEx(ERR, "Invalid retries (must be 0..10000)");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
if ((retries > 0) && (ak_len == 0)) {
|
||||
PrintAndLogEx(WARNING, "ERROR: Key is required for retries");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
if ((! check_answer) && (ak_len == 0)) {
|
||||
PrintAndLogEx(WARNING, "ERROR: Key is required for nocheck");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
// Swap endianness
|
||||
if (swap_endian && ak_len) {
|
||||
@@ -4283,6 +4425,8 @@ static int CmdHF14AMfUCAuth(const char *Cmd) {
|
||||
}
|
||||
|
||||
int isok;
|
||||
uint32_t auths = 0;
|
||||
uint32_t ms = 0;
|
||||
|
||||
// If no hex key is specified, try default keys
|
||||
if (ak_len == 0) {
|
||||
@@ -4291,7 +4435,8 @@ static int CmdHF14AMfUCAuth(const char *Cmd) {
|
||||
isok = try_default_3des_keys(false, &auth_key_ptr);
|
||||
} else {
|
||||
// try user-supplied
|
||||
isok = ulc_authentication(auth_key_ptr, !keep_field_on);
|
||||
|
||||
isok = ul3pass_authentication(auth_key_ptr, MIFAREULC_KEY_INDEX, !keep_field_on, retries, &auths, &ms, false, check_answer);
|
||||
}
|
||||
|
||||
if (isok == PM3_SUCCESS) {
|
||||
@@ -4299,9 +4444,127 @@ static int CmdHF14AMfUCAuth(const char *Cmd) {
|
||||
} else {
|
||||
PrintAndLogEx(WARNING, "Authentication ( " _RED_("fail") " )");
|
||||
}
|
||||
return PM3_SUCCESS;
|
||||
if (retries > 0) {
|
||||
PrintAndLogEx(INFO, "Time spent " _YELLOW_("%.1fs"), (float)(ms / 1000.0));
|
||||
PrintAndLogEx(INFO, "Authentication attempts: %u", auths);
|
||||
PrintAndLogEx(INFO, "Speed: %.1f auths/s", (float)(auths * 1000.0 / ms));
|
||||
}
|
||||
return isok;
|
||||
}
|
||||
|
||||
static int CmdHF14AMfUCAuthChk(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf mfu cchk",
|
||||
"It checks MIFARE Ultralight C tags keys against a dictionary file with keys\n",
|
||||
"hf mfu cchk -f mfulc_default_keys.dic");
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str0("f", "file", "<fn>", "filename of dictionary"),
|
||||
arg_int0("s", "segment", "<0..3>", "Segment index (full key if not specified)"),
|
||||
arg_int0("r", "retries", "<0..255>", "Number of retries (def: 0)"),
|
||||
arg_str0("k", "key", "<hex>", "Starting key, 16 hex bytes (def: zero key), for segment check"),
|
||||
arg_lit0("x", "xor", "XOR starting key with segment candidates (def: override)"),
|
||||
arg_lit0("n", "nocheck", "Skip checking tag answer correctness"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
|
||||
int fnlen = 0;
|
||||
char filename[FILE_PATH_SIZE] = {0};
|
||||
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
|
||||
int segment = arg_get_int_def(ctx, 2, -1); // -1 means full key
|
||||
int retries = arg_get_int_def(ctx, 3, 0);
|
||||
int ref_keylen = 0;
|
||||
uint8_t ref_key[16] = {0};
|
||||
CLIGetHexWithReturn(ctx, 4, ref_key, &ref_keylen);
|
||||
bool xor_ref_key = arg_get_lit(ctx, 5);
|
||||
bool check_answer = !arg_get_lit(ctx, 6);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
if (fnlen == 0) {
|
||||
PrintAndLogEx(ERR, "No dictionary file specified");
|
||||
return PM3_EFILE;
|
||||
}
|
||||
if (segment < -1 || segment > 3) {
|
||||
PrintAndLogEx(ERR, "Invalid segment (must be 0..3)");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
if (retries < 0 || retries > 255) {
|
||||
PrintAndLogEx(ERR, "Invalid retries (must be 0..255)");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
if (ref_keylen && ref_keylen != MIFAREU3P_KEY_SIZE) {
|
||||
PrintAndLogEx(WARNING, "Key must be %i hex bytes. Got %d", MIFAREU3P_KEY_SIZE, ref_keylen);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
if (ref_keylen == 0) {
|
||||
ref_keylen = MIFAREU3P_KEY_SIZE;
|
||||
}
|
||||
|
||||
uint8_t *keyBlock = NULL;
|
||||
uint32_t keycnt = 0;
|
||||
int keysize = segment != -1 ? MIFAREU3P_KEY_SIZE / 4 : MIFAREU3P_KEY_SIZE;
|
||||
int ret = mfu_3pass_load_keys(&keyBlock, &keycnt, filename, fnlen, keysize);
|
||||
if (ret != PM3_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
if (keycnt == 0) {
|
||||
PrintAndLogEx(ERR, "Dictionary contains no keys");
|
||||
free(keyBlock);
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
uint32_t chunksize = (keycnt > (PM3_CMD_DATA_SIZE - MIFAREU3P_CHKKEY_HEADER) / keysize) ?
|
||||
((PM3_CMD_DATA_SIZE - MIFAREU3P_CHKKEY_HEADER) / keysize) : keycnt;
|
||||
bool firstChunk = true, lastChunk = false;
|
||||
|
||||
int i = 0;
|
||||
|
||||
// time
|
||||
uint32_t auths = 0;
|
||||
uint32_t ms = 0;
|
||||
|
||||
// main keychunk loop
|
||||
for (int r = 0; r < retries + 1; r++) {
|
||||
for (i = 0; i < keycnt; i += chunksize) {
|
||||
if (kbd_enter_pressed()) {
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
|
||||
SendCommandNG(CMD_FPGA_MAJOR_MODE_OFF, NULL, 0); // field is still ON if not on last chunk
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(WARNING, "\naborted via keyboard!");
|
||||
goto out;
|
||||
}
|
||||
|
||||
uint32_t nkeys = ((keycnt - i) > chunksize) ? chunksize : keycnt - i;
|
||||
|
||||
// last chunk?
|
||||
if (nkeys == keycnt - i) {
|
||||
lastChunk = true;
|
||||
}
|
||||
int res = mfu_3pass_check_keys(MIFAREULC_KEY_INDEX, firstChunk, lastChunk, nkeys, segment, ref_key, xor_ref_key, keyBlock + (i * keysize), false, true, &auths, &ms, check_answer);
|
||||
if (firstChunk)
|
||||
firstChunk = false;
|
||||
|
||||
// all keys, aborted
|
||||
if (res == PM3_SUCCESS || res == 2) {
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
goto out;
|
||||
}
|
||||
PrintAndLogEx(INPLACE, "Testing %5i/%5i ( " _YELLOW_("%02.1f %%") " )", i, keycnt, (float)i * 100 / keycnt);
|
||||
} // end chunks of keys
|
||||
}
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
out:
|
||||
PrintAndLogEx(INFO, "Time spent " _YELLOW_("%.1fs"), (float)(ms / 1000.0));
|
||||
PrintAndLogEx(INFO, "Authentication attempts: %u", auths);
|
||||
PrintAndLogEx(INFO, "Speed: %.1f auths/s", (float)(auths * 1000.0 / ms));
|
||||
|
||||
free(keyBlock);
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
//-------------------------------------------------------------------------------
|
||||
// Ultralight AES Methods
|
||||
//-------------------------------------------------------------------------------
|
||||
@@ -4328,6 +4591,8 @@ static int CmdHF14AMfUAESAuth(const char *Cmd) {
|
||||
arg_lit0("l", NULL, "Swap entered key's endianness"),
|
||||
arg_lit0("k", NULL, "Keep field on (only if a key is provided)"),
|
||||
arg_lit0(NULL, "schann", "use secure channel. Must have key"),
|
||||
arg_int0("r", "retries", "<n>", "Number of retries (def: 0)"),
|
||||
arg_lit0("n", "nocheck", "Skip checking tag answer correctness"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
@@ -4340,6 +4605,8 @@ static int CmdHF14AMfUAESAuth(const char *Cmd) {
|
||||
bool swap_endian = arg_get_lit(ctx, 3);
|
||||
bool keep_field_on = arg_get_lit(ctx, 4);
|
||||
bool use_schann = arg_get_lit(ctx, 5);
|
||||
int retries = arg_get_int_def(ctx, 6, 0);
|
||||
bool check_answer = !arg_get_lit(ctx, 7);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
if (ak_len == 0) {
|
||||
@@ -4361,7 +4628,14 @@ static int CmdHF14AMfUAESAuth(const char *Cmd) {
|
||||
auth_key_ptr = SwapEndian64(authentication_key, ak_len, 16);
|
||||
}
|
||||
|
||||
int result = ulaes_requestAuthentication(auth_key_ptr, key_index, !keep_field_on, use_schann);
|
||||
if (retries < 0 || retries > 10000) {
|
||||
PrintAndLogEx(ERR, "Invalid retries (must be 0..10000)");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
uint32_t auths = 0;
|
||||
uint32_t ms = 0;
|
||||
|
||||
int result = ul3pass_authentication(auth_key_ptr, key_index, !keep_field_on, retries, &auths, &ms, use_schann, check_answer);
|
||||
if (result == PM3_SUCCESS) {
|
||||
PrintAndLogEx(SUCCESS, "Authentication with " _YELLOW_("%s") " " _GREEN_("%s") " ( " _GREEN_("ok")" )"
|
||||
, key_type[key_index]
|
||||
@@ -4370,9 +4644,137 @@ static int CmdHF14AMfUAESAuth(const char *Cmd) {
|
||||
} else {
|
||||
PrintAndLogEx(WARNING, "Authentication with " _YELLOW_("%s") " ( " _RED_("fail") " )", key_type[key_index]);
|
||||
}
|
||||
if (retries > 0) {
|
||||
PrintAndLogEx(INFO, "Time spent " _YELLOW_("%.1fs"), (float)(ms / 1000.0));
|
||||
PrintAndLogEx(INFO, "Authentication attempts: %u", auths);
|
||||
PrintAndLogEx(INFO, "Speed: %.1f auths/s", (float)(auths * 1000.0 / ms));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int CmdHF14AMfUAESAuthChk(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf mfu aeschk",
|
||||
"It checks MIFARE Ultralight AES tags keys against a dictionary file with keys\n"
|
||||
" Key index 0... DataProtKey (default)\n"
|
||||
" Key index 1... UIDRetrKey\n"
|
||||
" Key index 2... OriginalityKey\n",
|
||||
"hf mfu aeschk -f mfulaes_default_keys.dic");
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str0("f", "file", "<fn>", "filename of dictionary"),
|
||||
arg_int0("i", "idx", "<0..2>", "Key index (def: 0)"),
|
||||
arg_int0("s", "segment", "<0..3>", "Segment index (full key if not specified)"),
|
||||
arg_int0("r", "retries", "<0..255>", "Number of retries (def: 0)"),
|
||||
arg_str0("k", "key", "<hex>", "Starting key, 16 hex bytes (def: zero key), for segment check"),
|
||||
arg_lit0("x", "xor", "XOR starting key with segment candidates (def: override)"),
|
||||
arg_lit0("n", "nocheck", "Skip checking tag answer correctness"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
|
||||
int fnlen = 0;
|
||||
char filename[FILE_PATH_SIZE] = {0};
|
||||
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
|
||||
int key_index = arg_get_int_def(ctx, 2, 0);
|
||||
int segment = arg_get_int_def(ctx, 3, -1); // -1 means full key
|
||||
int retries = arg_get_int_def(ctx, 4, 0);
|
||||
int ref_keylen = 0;
|
||||
uint8_t ref_key[16] = {0};
|
||||
CLIGetHexWithReturn(ctx, 5, ref_key, &ref_keylen);
|
||||
bool xor_ref_key = arg_get_lit(ctx, 6);
|
||||
bool check_answer = !arg_get_lit(ctx, 7);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
if (fnlen == 0) {
|
||||
PrintAndLogEx(ERR, "No dictionary file specified");
|
||||
return PM3_EFILE;
|
||||
}
|
||||
if (key_index < 0 || key_index > 2) {
|
||||
PrintAndLogEx(ERR, "Invalid key index (must be 0..2)");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
if (segment < -1 || segment > 3) {
|
||||
PrintAndLogEx(ERR, "Invalid segment (must be 0..3)");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
if (retries < 0 || retries > 255) {
|
||||
PrintAndLogEx(ERR, "Invalid retries (must be 0..255)");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
if (ref_keylen && ref_keylen != MIFAREU3P_KEY_SIZE) {
|
||||
PrintAndLogEx(WARNING, "Key must be %i hex bytes. Got %d", MIFAREU3P_KEY_SIZE, ref_keylen);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
if (ref_keylen == 0) {
|
||||
ref_keylen = MIFAREU3P_KEY_SIZE;
|
||||
}
|
||||
|
||||
uint8_t *keyBlock = NULL;
|
||||
uint32_t keycnt = 0;
|
||||
int keysize = segment != -1 ? MIFAREU3P_KEY_SIZE / 4 : MIFAREU3P_KEY_SIZE;
|
||||
int ret = mfu_3pass_load_keys(&keyBlock, &keycnt, filename, fnlen, keysize);
|
||||
if (ret != PM3_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
if (keycnt == 0) {
|
||||
PrintAndLogEx(ERR, "Dictionary contains no keys");
|
||||
free(keyBlock);
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
uint32_t chunksize = (keycnt > (PM3_CMD_DATA_SIZE - MIFAREU3P_CHKKEY_HEADER) / keysize) ?
|
||||
((PM3_CMD_DATA_SIZE - MIFAREU3P_CHKKEY_HEADER) / keysize) : keycnt;
|
||||
bool firstChunk = true, lastChunk = false;
|
||||
|
||||
int i = 0;
|
||||
|
||||
uint32_t auths = 0;
|
||||
uint32_t ms = 0;
|
||||
|
||||
// main keychunk loop
|
||||
for (int r = 0; r < retries + 1; r++) {
|
||||
for (i = 0; i < keycnt; i += chunksize) {
|
||||
if (kbd_enter_pressed()) {
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
|
||||
SendCommandNG(CMD_FPGA_MAJOR_MODE_OFF, NULL, 0); // field is still ON if not on last chunk
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(WARNING, "\naborted via keyboard!");
|
||||
goto out;
|
||||
}
|
||||
|
||||
uint32_t nkeys = ((keycnt - i) > chunksize) ? chunksize : keycnt - i;
|
||||
|
||||
// last chunk?
|
||||
if (nkeys == keycnt - i) {
|
||||
lastChunk = true;
|
||||
}
|
||||
|
||||
int res = mfu_3pass_check_keys(key_index, firstChunk, lastChunk, nkeys, segment, ref_key, xor_ref_key, keyBlock + (i * keysize), false, true, &auths, &ms, check_answer);
|
||||
if (firstChunk)
|
||||
firstChunk = false;
|
||||
|
||||
// all keys, aborted
|
||||
if (res == PM3_SUCCESS || res == 2) {
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
goto out;
|
||||
}
|
||||
PrintAndLogEx(INPLACE, "Testing %5i/%5i ( " _YELLOW_("%02.1f %%") " )", i, keycnt, (float)i * 100 / keycnt);
|
||||
} // end chunks of keys
|
||||
}
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
out:
|
||||
PrintAndLogEx(INFO, "Time spent " _YELLOW_("%.1fs"), (float)(ms / 1000.0));
|
||||
PrintAndLogEx(INFO, "Authentication attempts: %u", auths);
|
||||
PrintAndLogEx(INFO, "Speed: %.1f auths/s", (float)(auths * 1000.0 / ms));
|
||||
|
||||
free(keyBlock);
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
A test function to validate that the polarssl-function works the same
|
||||
was as the openssl-implementation.
|
||||
@@ -6519,8 +6921,10 @@ static command_t CommandTable[] = {
|
||||
{"otptear", CmdHF14AMfuOtpTearoff, IfPm3Iso14443a, "Tear-off test on OTP bits"},
|
||||
// {"tear_cnt", CmdHF14AMfuEv1CounterTearoff, IfPm3Iso14443a, "Tear-off test on Ev1/NTAG Counter bits"},
|
||||
{"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("operations") " -----------------------"},
|
||||
{"cauth", CmdHF14AMfUCAuth, IfPm3Iso14443a, "Ultralight C - Authentication"},
|
||||
{"aesauth", CmdHF14AMfUAESAuth, IfPm3Iso14443a, "Ultralight AES - Authentication"},
|
||||
{"cauth", CmdHF14AMfUCAuth, IfPm3Iso14443a, "Ultralight-C - Authentication"},
|
||||
{"cchk", CmdHF14AMfUCAuthChk, IfPm3Iso14443a, "Ultralight-C - Authentication dictionary check"},
|
||||
{"aesauth", CmdHF14AMfUAESAuth, IfPm3Iso14443a, "Ultralight-AES - Authentication"},
|
||||
{"aeschk", CmdHF14AMfUAESAuthChk, IfPm3Iso14443a, "Ultralight-AES - Authentication dictionary check"},
|
||||
{"setkey", CmdHF14AMfUSetKey, IfPm3Iso14443a, "Ultralight C/AES - Set 3DES/AES keys"},
|
||||
{"dump", CmdHF14AMfUDump, IfPm3Iso14443a, "Dump MIFARE Ultralight family tag to binary file"},
|
||||
{"incr", CmdHF14AMfUIncr, IfPm3Iso14443a, "Increments Ev1/NTAG counter"},
|
||||
|
||||
@@ -436,7 +436,9 @@ const static vocabulary_t vocabulary[] = {
|
||||
{ 1, "hf mfu pwdgen" },
|
||||
{ 0, "hf mfu otptear" },
|
||||
{ 0, "hf mfu cauth" },
|
||||
{ 0, "hf mfu cchk" },
|
||||
{ 0, "hf mfu aesauth" },
|
||||
{ 0, "hf mfu aeschk" },
|
||||
{ 0, "hf mfu setkey" },
|
||||
{ 0, "hf mfu dump" },
|
||||
{ 0, "hf mfu incr" },
|
||||
@@ -920,6 +922,10 @@ const static vocabulary_t vocabulary[] = {
|
||||
{ 0, "script run ice.py" },
|
||||
{ 0, "script run intertic.py" },
|
||||
{ 0, "script run mf_backdoor_dump.py" },
|
||||
{ 0, "script run mfulaes_mask_recovery.py" },
|
||||
{ 0, "script run mfulc_counterfeit_recovery.py" },
|
||||
{ 0, "script run ntag22x_libsuncmac.py" },
|
||||
{ 0, "script run ntag22x_suncmac_recovery.py" },
|
||||
{ 0, "script run parity.py" },
|
||||
{ 0, "script run paxton_convert.py" },
|
||||
{ 0, "script run paxton_net.py" },
|
||||
|
||||
+51
-10
@@ -1411,10 +1411,10 @@
|
||||
"-x Performs the 'reader attack', nr/ar attack against a reader",
|
||||
"--sk Fill simulator keys from found keys",
|
||||
"-v, --verbose verbose output",
|
||||
"--z1 ULC/ULAES Auth - all zero handshake part 1",
|
||||
"--z2 ULC/ULAES Auth - all zero handshake part 2"
|
||||
"--1a1 <hex> <8|16> hex bytes ULC/ULAES Auth reply step1: ek(RndB)",
|
||||
"--1a2 <hex> <8|16> hex bytes ULC/ULAES Auth reply step2: ek(RndA')"
|
||||
],
|
||||
"usage": "hf 14a sim [-hxv] -t <1-14> [-u <hex>] [-n <dec>] [--sk] [--z1] [--z2]"
|
||||
"usage": "hf 14a sim [-hxv] -t <1-14> [-u <hex>] [-n <dec>] [--sk] [--1a1 <hex>] [--1a2 <hex>]"
|
||||
},
|
||||
"hf 14a simaid": {
|
||||
"command": "hf 14a simaid",
|
||||
@@ -7183,9 +7183,30 @@
|
||||
"-i, --idx <0..2> Key index (def: 0)",
|
||||
"-l Swap entered key's endianness",
|
||||
"-k Keep field on (only if a key is provided)",
|
||||
"--schann use secure channel. Must have key"
|
||||
"--schann use secure channel. Must have key",
|
||||
"-r, --retries <n> Number of retries (def: 0)",
|
||||
"-n, --nocheck Skip checking tag answer correctness"
|
||||
],
|
||||
"usage": "hf mfu aesauth [-hlk] [--key <hex>] [-i <0..2>] [--schann]"
|
||||
"usage": "hf mfu aesauth [-hlkn] [--key <hex>] [-i <0..2>] [--schann] [-r <n>]"
|
||||
},
|
||||
"hf mfu aeschk": {
|
||||
"command": "hf mfu aeschk",
|
||||
"description": "It checks MIFARE Ultralight AES tags keys against a dictionary file with keys Key index 0... DataProtKey (default) Key index 1... UIDRetrKey Key index 2... OriginalityKey",
|
||||
"notes": [
|
||||
"hf mfu aeschk -f mfulaes_default_keys.dic"
|
||||
],
|
||||
"offline": false,
|
||||
"options": [
|
||||
"-h, --help This help",
|
||||
"-f, --file <fn> filename of dictionary",
|
||||
"-i, --idx <0..2> Key index (def: 0)",
|
||||
"-s, --segment <0..3> Segment index (full key if not specified)",
|
||||
"-r, --retries <0..255> Number of retries (def: 0)",
|
||||
"-k, --key <hex> Starting key, 16 hex bytes (def: zero key), for segment check",
|
||||
"-x, --xor XOR starting key with segment candidates (def: override)",
|
||||
"-n, --nocheck Skip checking tag answer correctness"
|
||||
],
|
||||
"usage": "hf mfu aeschk [-hxn] [-f <fn>] [-i <0..2>] [-s <0..3>] [-r <0..255>] [-k <hex>]"
|
||||
},
|
||||
"hf mfu amiibo": {
|
||||
"command": "hf mfu amiibo",
|
||||
@@ -7217,9 +7238,29 @@
|
||||
"-h, --help This help",
|
||||
"--key <hex> Authentication key (16 bytes in hex)",
|
||||
"-l Swap entered key's endianness",
|
||||
"-k Keep field on (only if a key is provided)"
|
||||
"-k Keep field on (only if a key is provided)",
|
||||
"-r, --retries <n> Number of retries with provided key (def: 0)",
|
||||
"-n, --nocheck Skip checking tag answer correctness (only if a key is provided)"
|
||||
],
|
||||
"usage": "hf mfu cauth [-hlk] [--key <hex>]"
|
||||
"usage": "hf mfu cauth [-hlkn] [--key <hex>] [-r <n>]"
|
||||
},
|
||||
"hf mfu cchk": {
|
||||
"command": "hf mfu cchk",
|
||||
"description": "It checks MIFARE Ultralight C tags keys against a dictionary file with keys",
|
||||
"notes": [
|
||||
"hf mfu cchk -f mfulc_default_keys.dic"
|
||||
],
|
||||
"offline": false,
|
||||
"options": [
|
||||
"-h, --help This help",
|
||||
"-f, --file <fn> filename of dictionary",
|
||||
"-s, --segment <0..3> Segment index (full key if not specified)",
|
||||
"-r, --retries <0..255> Number of retries (def: 0)",
|
||||
"-k, --key <hex> Starting key, 16 hex bytes (def: zero key), for segment check",
|
||||
"-x, --xor XOR starting key with segment candidates (def: override)",
|
||||
"-n, --nocheck Skip checking tag answer correctness"
|
||||
],
|
||||
"usage": "hf mfu cchk [-hxn] [-f <fn>] [-s <0..3>] [-r <0..255>] [-k <hex>]"
|
||||
},
|
||||
"hf mfu dump": {
|
||||
"command": "hf mfu dump",
|
||||
@@ -7510,10 +7551,10 @@
|
||||
"-u, --uid <hex> <4|7|10> hex bytes UID",
|
||||
"-n, --num <dec> Exit simulation after <numreads> blocks. 0 = infinite",
|
||||
"-v, --verbose Verbose output",
|
||||
"--z1 ULC/ULAES Auth - all zero handshake part 1",
|
||||
"--z2 ULC/ULAES Auth - all zero handshake part 2"
|
||||
"--1a1 <hex> <8|16> hex bytes ULC/ULAES Auth reply step1: ek(RndB)",
|
||||
"--1a2 <hex> <8|16> hex bytes ULC/ULAES Auth reply step2: ek(RndA')"
|
||||
],
|
||||
"usage": "hf mfu sim [-hv] -t <1..14> [-u <hex>] [-n <dec>] [--z1] [--z2]"
|
||||
"usage": "hf mfu sim [-hv] -t <1..14> [-u <hex>] [-n <dec>] [--1a1 <hex>] [--1a2 <hex>]"
|
||||
},
|
||||
"hf mfu tamper": {
|
||||
"command": "hf mfu tamper",
|
||||
|
||||
+4
-2
@@ -622,8 +622,10 @@ Check column "offline" for their availability.
|
||||
|`hf mfu keygen `|Y |`Generate DES/3DES/AES MIFARE diversified keys`
|
||||
|`hf mfu pwdgen `|Y |`Generate pwd from known algos`
|
||||
|`hf mfu otptear `|N |`Tear-off test on OTP bits`
|
||||
|`hf mfu cauth `|N |`Ultralight C - Authentication`
|
||||
|`hf mfu aesauth `|N |`Ultralight AES - Authentication`
|
||||
|`hf mfu cauth `|N |`Ultralight-C - Authentication`
|
||||
|`hf mfu cchk `|N |`Ultralight-C - Authentication dictionary check`
|
||||
|`hf mfu aesauth `|N |`Ultralight-AES - Authentication`
|
||||
|`hf mfu aeschk `|N |`Ultralight-AES - Authentication dictionary check`
|
||||
|`hf mfu setkey `|N |`Ultralight C/AES - Set 3DES/AES keys`
|
||||
|`hf mfu dump `|N |`Dump MIFARE Ultralight family tag to binary file`
|
||||
|`hf mfu incr `|N |`Increments Ev1/NTAG counter`
|
||||
|
||||
+27
-15
@@ -366,17 +366,30 @@ typedef struct {
|
||||
} PACKED mfc_eload_t;
|
||||
|
||||
typedef struct {
|
||||
bool use_flashmem;
|
||||
uint16_t keycount;
|
||||
uint8_t keys[];
|
||||
} PACKED mfulc_keys_t;
|
||||
|
||||
typedef struct {
|
||||
bool turn_off_field;
|
||||
bool use_schann;
|
||||
uint8_t keyno;
|
||||
uint8_t turn_off_field : 1;
|
||||
uint8_t check_answer : 1;
|
||||
uint8_t keyno : 2;
|
||||
uint8_t use_schann : 1;
|
||||
uint8_t reserved : 3;
|
||||
uint16_t retries;
|
||||
uint8_t key[16];
|
||||
} PACKED mfulaes_keys_t;
|
||||
} PACKED mful_3passauth_t;
|
||||
|
||||
enum {
|
||||
MIFAREU3P_KEY_SIZE = 16,
|
||||
MIFAREU3P_CHKKEY_HEADER = 2 + MIFAREU3P_KEY_SIZE
|
||||
};
|
||||
typedef struct {
|
||||
uint8_t key_index : 2;
|
||||
uint8_t firstchunk : 1;
|
||||
uint8_t lastchunk : 1;
|
||||
uint8_t xor_ref_key : 1;
|
||||
uint8_t segment : 3;
|
||||
uint8_t check_answer : 1;
|
||||
uint8_t nkeys : 7;
|
||||
uint8_t ref_key[MIFAREU3P_KEY_SIZE];
|
||||
uint8_t data[PM3_CMD_DATA_SIZE - MIFAREU3P_CHKKEY_HEADER];
|
||||
} PACKED mful_3passchk_t;
|
||||
|
||||
typedef struct {
|
||||
bool use_schann;
|
||||
@@ -798,11 +811,10 @@ typedef struct {
|
||||
#define CMD_HF_MIFARE_MFKEY 0x0631
|
||||
#define CMD_HF_MIFARE_PERSONALIZE_UID 0x0632
|
||||
|
||||
// ultralight-C
|
||||
#define CMD_HF_MIFAREUC_AUTH 0x0724
|
||||
// Ultralight AES
|
||||
#define CMD_HF_MIFAREULAES_AUTH 0x0725
|
||||
// 0x0726 no longer used
|
||||
// ultralight-C & AES
|
||||
#define CMD_HF_MIFAREU3P_AUTH 0x0724
|
||||
// 0x0725 is free
|
||||
#define CMD_HF_MIFAREU3P_CHKKEY 0x0726
|
||||
#define CMD_HF_MIFAREU_SETKEY 0x0727
|
||||
|
||||
// mifare desfire
|
||||
|
||||
@@ -195,9 +195,10 @@ typedef struct {
|
||||
#define CMD_HF_MIFARE_SETMOD 0x0624
|
||||
|
||||
#define CMD_HF_MIFARE_SNIFF 0x0630
|
||||
//ultralightC
|
||||
#define CMD_HF_MIFAREUC_AUTH 0x0724
|
||||
//0x0725 and 0x0726 no longer used
|
||||
// ultralight-C & AES
|
||||
#define CMD_HF_MIFAREU3P_AUTH 0x0724
|
||||
// 0x0725 is free
|
||||
#define CMD_HF_MIFAREU3P_CHKKEY 0x0726
|
||||
#define CMD_HF_MIFAREU_SETKEY 0x0727
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user