mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2024-09-21 07:46:12 +08:00
HF MFDES dump + chk commands added
This commit is contained in:
parent
69142baa7f
commit
4b3a78538c
37
client/dictionaries/mfdes_default_keys.dic
Normal file
37
client/dictionaries/mfdes_default_keys.dic
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
0000000000000000
|
||||||
|
00000000000000000000000000000000
|
||||||
|
000000000000000000000000000000000000000000000000
|
||||||
|
404142434445464748494a4b4c4d4e4f
|
||||||
|
00112233445566778899aabbccddeeff
|
||||||
|
2b7e151628aed2a6abf7158809cf4f3c
|
||||||
|
fbeed618357133667c85e08f7236a8de
|
||||||
|
f7ddac306ae266ccf90bc11ee46d513b
|
||||||
|
54686973206973206D79206B65792020
|
||||||
|
ffffffffffffffffffffffffffffffff
|
||||||
|
a0a1a2a3a4a5a6a7a0a1a2a3a4a5a6a7
|
||||||
|
b0b1b2b3b4b5b6b7b0b1b2b3b4b5b6b7
|
||||||
|
d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7
|
||||||
|
11111111111111111111111111111111
|
||||||
|
22222222222222222222222222222222
|
||||||
|
33333333333333333333333333333333
|
||||||
|
44444444444444444444444444444444
|
||||||
|
55555555555555555555555555555555
|
||||||
|
66666666666666666666666666666666
|
||||||
|
77777777777777777777777777777777
|
||||||
|
88888888888888888888888888888888
|
||||||
|
99999999999999999999999999999999
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
|
||||||
|
cccccccccccccccccccccccccccccccc
|
||||||
|
dddddddddddddddddddddddddddddddd
|
||||||
|
eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
|
||||||
|
000102030405060708090a0b0c0d0e0f
|
||||||
|
0102030405060708090a0b0c0d0e0f10
|
||||||
|
00010203040506070809101112131415
|
||||||
|
01020304050607080910111213141516
|
||||||
|
16151413121110090807060504030201
|
||||||
|
15141312111009080706050403020100
|
||||||
|
0f0e0d0c0b0a09080706050403020100
|
||||||
|
100f0e0d0c0b0a090807060504030201
|
||||||
|
404142434445464748494a4b4c4d4e4f
|
||||||
|
303132333435363738393a3b3c3d3e3f
|
|
@ -16,21 +16,21 @@
|
||||||
#include "cmdparser.h" // command_t
|
#include "cmdparser.h" // command_t
|
||||||
#include "comms.h"
|
#include "comms.h"
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
#include "cmdhw.h"
|
|
||||||
#include "cmdhf14a.h"
|
#include "cmdhf14a.h"
|
||||||
#include "mbedtls/des.h"
|
|
||||||
#include "mbedtls/aes.h"
|
#include "mbedtls/aes.h"
|
||||||
#include "../crypto/libpcrypto.h"
|
#include "../crypto/libpcrypto.h"
|
||||||
#include "protocols.h"
|
#include "protocols.h"
|
||||||
#include "mifare.h" // desfire raw command options
|
|
||||||
#include "cmdtrace.h"
|
#include "cmdtrace.h"
|
||||||
#include "cliparser/cliparser.h"
|
#include "cliparser/cliparser.h"
|
||||||
#include "emv/apduinfo.h" // APDU manipulation / errorcodes
|
#include "emv/apduinfo.h" // APDU manipulation / errorcodes
|
||||||
#include "emv/emvcore.h" // APDU logging
|
#include "emv/emvcore.h" // APDU logging
|
||||||
#include "util_posix.h" // msleep
|
#include "util_posix.h" // msleep
|
||||||
#include "mifare/mifare4.h" // MIFARE Authenticate / MAC
|
|
||||||
#include "mifare/desfire_crypto.h"
|
#include "mifare/desfire_crypto.h"
|
||||||
#include "crapto1/crapto1.h"
|
#include "crapto1/crapto1.h"
|
||||||
|
#include "fileutils.h"
|
||||||
|
|
||||||
|
#define MAX_KEY_LEN 24
|
||||||
|
#define MAX_KEYS_LIST_LEN 1024
|
||||||
|
|
||||||
struct desfire_key defaultkey = {0};
|
struct desfire_key defaultkey = {0};
|
||||||
static desfirekey_t sessionkey = &defaultkey;
|
static desfirekey_t sessionkey = &defaultkey;
|
||||||
|
@ -175,17 +175,11 @@ static char *cluster_to_text(uint8_t cluster) {
|
||||||
case CL_ADMIN:
|
case CL_ADMIN:
|
||||||
return "card administration";
|
return "card administration";
|
||||||
case CL_MISC1:
|
case CL_MISC1:
|
||||||
return "miscellaneous applications";
|
|
||||||
case CL_MISC2:
|
case CL_MISC2:
|
||||||
return "miscellaneous applications";
|
|
||||||
case CL_MISC3:
|
case CL_MISC3:
|
||||||
return "miscellaneous applications";
|
|
||||||
case CL_MISC4:
|
case CL_MISC4:
|
||||||
return "miscellaneous applications";
|
|
||||||
case CL_MISC5:
|
case CL_MISC5:
|
||||||
return "miscellaneous applications";
|
|
||||||
case CL_MISC6:
|
case CL_MISC6:
|
||||||
return "miscellaneous applications";
|
|
||||||
case CL_MISC7:
|
case CL_MISC7:
|
||||||
return "miscellaneous applications";
|
return "miscellaneous applications";
|
||||||
case CL_AIRLINES:
|
case CL_AIRLINES:
|
||||||
|
@ -219,7 +213,6 @@ static char *cluster_to_text(uint8_t cluster) {
|
||||||
case CL_CITYCARD:
|
case CL_CITYCARD:
|
||||||
return "city card services";
|
return "city card services";
|
||||||
case CL_ACCESS_CONTROL_1:
|
case CL_ACCESS_CONTROL_1:
|
||||||
return "access control & security";
|
|
||||||
case CL_ACCESS_CONTROL_2:
|
case CL_ACCESS_CONTROL_2:
|
||||||
return "access control & security";
|
return "access control & security";
|
||||||
case CL_VIGIK:
|
case CL_VIGIK:
|
||||||
|
@ -289,19 +282,12 @@ static char *cluster_to_text(uint8_t cluster) {
|
||||||
case CL_MAIL:
|
case CL_MAIL:
|
||||||
return "mail";
|
return "mail";
|
||||||
case CL_AMISC:
|
case CL_AMISC:
|
||||||
return "miscellaneous applications";
|
|
||||||
case CL_AMISC1:
|
case CL_AMISC1:
|
||||||
return "miscellaneous applications";
|
|
||||||
case CL_AMISC2:
|
case CL_AMISC2:
|
||||||
return "miscellaneous applications";
|
|
||||||
case CL_AMISC3:
|
case CL_AMISC3:
|
||||||
return "miscellaneous applications";
|
|
||||||
case CL_AMISC4:
|
case CL_AMISC4:
|
||||||
return "miscellaneous applications";
|
|
||||||
case CL_AMISC5:
|
case CL_AMISC5:
|
||||||
return "miscellaneous applications";
|
|
||||||
case CL_AMISC6:
|
case CL_AMISC6:
|
||||||
return "miscellaneous applications";
|
|
||||||
case CL_AMISC7:
|
case CL_AMISC7:
|
||||||
return "miscellaneous applications";
|
return "miscellaneous applications";
|
||||||
default:
|
default:
|
||||||
|
@ -311,7 +297,7 @@ static char *cluster_to_text(uint8_t cluster) {
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
UNKNOWN = 0,
|
DESFIRE_UNKNOWN = 0,
|
||||||
DESFIRE_MF3ICD40,
|
DESFIRE_MF3ICD40,
|
||||||
DESFIRE_EV1,
|
DESFIRE_EV1,
|
||||||
DESFIRE_EV2,
|
DESFIRE_EV2,
|
||||||
|
@ -736,8 +722,7 @@ int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rpayload,
|
||||||
sAPDU apdu = {0x90, subcommand, 0x00, 0x00, 0x01, data};
|
sAPDU apdu = {0x90, subcommand, 0x00, 0x00, 0x01, data};
|
||||||
int res = send_desfire_cmd(&apdu, false, recv_data, &recv_len, &sw, 0, false);
|
int res = send_desfire_cmd(&apdu, false, recv_data, &recv_len, &sw, 0, false);
|
||||||
if (res != PM3_SUCCESS) {
|
if (res != PM3_SUCCESS) {
|
||||||
PrintAndLogEx(SUCCESS, "Sending auth command %02X " _RED_("failed"), subcommand);
|
return 1;
|
||||||
return PM3_ESOFT;
|
|
||||||
}
|
}
|
||||||
} else if (payload->mode == MFDES_AUTH_PICC) {
|
} else if (payload->mode == MFDES_AUTH_PICC) {
|
||||||
/*cmd[0] = AUTHENTICATE;
|
/*cmd[0] = AUTHENTICATE;
|
||||||
|
@ -747,13 +732,11 @@ int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rpayload,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!recv_len) {
|
if (!recv_len) {
|
||||||
PrintAndLogEx(ERR, "Authentication failed. Card timeout.");
|
return 2;
|
||||||
return PM3_ESOFT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sw != status(MFDES_ADDITIONAL_FRAME)) {
|
if (sw != status(MFDES_ADDITIONAL_FRAME)) {
|
||||||
PrintAndLogEx(ERR, "Authentication failed. Invalid key number.");
|
return 3;
|
||||||
return PM3_ESOFT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int expectedlen = 8;
|
int expectedlen = 8;
|
||||||
|
@ -762,8 +745,7 @@ int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rpayload,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recv_len != expectedlen) {
|
if (recv_len != expectedlen) {
|
||||||
PrintAndLogEx(ERR, "Authentication failed. Length of answer %d doesn't match algo length %d.", recv_len, expectedlen);
|
return 4;
|
||||||
return PM3_ESOFT;
|
|
||||||
}
|
}
|
||||||
int rndlen = recv_len;
|
int rndlen = recv_len;
|
||||||
|
|
||||||
|
@ -778,8 +760,7 @@ int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rpayload,
|
||||||
// Part 3
|
// Part 3
|
||||||
if (payload->algo == MFDES_ALGO_AES) {
|
if (payload->algo == MFDES_ALGO_AES) {
|
||||||
if (mbedtls_aes_setkey_dec(&ctx, key->data, 128) != 0) {
|
if (mbedtls_aes_setkey_dec(&ctx, key->data, 128) != 0) {
|
||||||
PrintAndLogEx(ERR, "mbedtls_aes_setkey_dec failed");
|
return 5;
|
||||||
return PM3_ESOFT;
|
|
||||||
}
|
}
|
||||||
mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_DECRYPT, rndlen, IV, encRndB, RndB);
|
mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_DECRYPT, rndlen, IV, encRndB, RndB);
|
||||||
} else if (payload->algo == MFDES_ALGO_DES)
|
} else if (payload->algo == MFDES_ALGO_DES)
|
||||||
|
@ -848,8 +829,7 @@ int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rpayload,
|
||||||
}
|
}
|
||||||
if (payload->algo == MFDES_ALGO_AES) {
|
if (payload->algo == MFDES_ALGO_AES) {
|
||||||
if (mbedtls_aes_setkey_enc(&ctx, key->data, 128) != 0) {
|
if (mbedtls_aes_setkey_enc(&ctx, key->data, 128) != 0) {
|
||||||
PrintAndLogEx(ERR, "mbedtls_aes_setkey_enc failed");
|
return 6;
|
||||||
return PM3_ESOFT;
|
|
||||||
}
|
}
|
||||||
mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_ENCRYPT, 32, IV, tmp, both);
|
mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_ENCRYPT, 32, IV, tmp, both);
|
||||||
if (g_debugMode > 1) {
|
if (g_debugMode > 1) {
|
||||||
|
@ -866,8 +846,7 @@ int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rpayload,
|
||||||
sAPDU apdu = {0x90, MFDES_ADDITIONAL_FRAME, 0x00, 0x00, bothlen, both};
|
sAPDU apdu = {0x90, MFDES_ADDITIONAL_FRAME, 0x00, 0x00, bothlen, both};
|
||||||
int res = send_desfire_cmd(&apdu, false, recv_data, &recv_len, &sw, 0, false);
|
int res = send_desfire_cmd(&apdu, false, recv_data, &recv_len, &sw, 0, false);
|
||||||
if (res != PM3_SUCCESS) {
|
if (res != PM3_SUCCESS) {
|
||||||
PrintAndLogEx(SUCCESS, "Sending auth command %02X " _RED_("failed"), subcommand);
|
return 7;
|
||||||
return PM3_ESOFT;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/*cmd[0] = ADDITIONAL_FRAME;
|
/*cmd[0] = ADDITIONAL_FRAME;
|
||||||
|
@ -881,14 +860,12 @@ int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rpayload,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!recv_len) {
|
if (!recv_len) {
|
||||||
PrintAndLogEx(ERR, "Authentication failed. Card timeout.");
|
return 8;
|
||||||
return PM3_ESOFT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload->mode != MFDES_AUTH_PICC) {
|
if (payload->mode != MFDES_AUTH_PICC) {
|
||||||
if (sw != status(MFDES_S_OPERATION_OK)) {
|
if (sw != status(MFDES_S_OPERATION_OK)) {
|
||||||
PrintAndLogEx(ERR, "Authentication failed.");
|
return 9;
|
||||||
return PM3_ESOFT;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/*if (resp[1] != 0x00) {
|
/*if (resp[1] != 0x00) {
|
||||||
|
@ -915,8 +892,7 @@ int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rpayload,
|
||||||
tdes_nxp_receive(encRndA, encRndA, rndlen, key->data, IV, 3);
|
tdes_nxp_receive(encRndA, encRndA, rndlen, key->data, IV, 3);
|
||||||
} else if (payload->mode == MFDES_AUTH_AES) {
|
} else if (payload->mode == MFDES_AUTH_AES) {
|
||||||
if (mbedtls_aes_setkey_dec(&ctx, key->data, 128) != 0) {
|
if (mbedtls_aes_setkey_dec(&ctx, key->data, 128) != 0) {
|
||||||
PrintAndLogEx(ERR, "mbedtls_aes_setkey_dec failed");
|
return 10;
|
||||||
return PM3_ESOFT;
|
|
||||||
}
|
}
|
||||||
mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_DECRYPT, rndlen, IV, encRndA, encRndA);
|
mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_DECRYPT, rndlen, IV, encRndA, encRndA);
|
||||||
}
|
}
|
||||||
|
@ -924,12 +900,11 @@ int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rpayload,
|
||||||
rol(RndA, rndlen);
|
rol(RndA, rndlen);
|
||||||
for (int x = 0; x < rndlen; x++) {
|
for (int x = 0; x < rndlen; x++) {
|
||||||
if (RndA[x] != encRndA[x]) {
|
if (RndA[x] != encRndA[x]) {
|
||||||
PrintAndLogEx(ERR, "Authentication failed. Cannot verify Session Key.");
|
|
||||||
if (g_debugMode > 1) {
|
if (g_debugMode > 1) {
|
||||||
PrintAndLogEx(INFO, "Expected_RndA : %s", sprint_hex(RndA, rndlen));
|
PrintAndLogEx(INFO, "Expected_RndA : %s", sprint_hex(RndA, rndlen));
|
||||||
PrintAndLogEx(INFO, "Generated_RndA : %s", sprint_hex(encRndA, rndlen));
|
PrintAndLogEx(INFO, "Generated_RndA : %s", sprint_hex(encRndA, rndlen));
|
||||||
}
|
}
|
||||||
return PM3_ESOFT;
|
return 11;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -938,6 +913,45 @@ int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rpayload,
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AuthToError(int error) {
|
||||||
|
switch (error) {
|
||||||
|
case 1:
|
||||||
|
PrintAndLogEx(SUCCESS, "Sending auth command failed");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
PrintAndLogEx(ERR, "Authentication failed. No data received");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
PrintAndLogEx(ERR, "Authentication failed. Invalid key number.");
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
PrintAndLogEx(ERR, "Authentication failed. Length of answer %d doesn't match algo length %d.");
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
PrintAndLogEx(ERR, "mbedtls_aes_setkey_dec failed");
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
PrintAndLogEx(ERR, "mbedtls_aes_setkey_enc failed");
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
PrintAndLogEx(SUCCESS, "Sending auth command failed");
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
PrintAndLogEx(ERR, "Authentication failed. Card timeout.");
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
PrintAndLogEx(ERR, "Authentication failed.");
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
PrintAndLogEx(ERR, "mbedtls_aes_setkey_dec failed");
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
PrintAndLogEx(ERR, "Authentication failed. Cannot verify Session Key.");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
// -- test if card supports 0x0A
|
// -- test if card supports 0x0A
|
||||||
static int test_desfire_authenticate() {
|
static int test_desfire_authenticate() {
|
||||||
uint8_t data[] = {0x00};
|
uint8_t data[] = {0x00};
|
||||||
|
@ -3051,6 +3065,149 @@ static int DecodeFileSettings(uint8_t *src, int src_len, int maclen) {
|
||||||
return PM3_ESOFT;
|
return PM3_ESOFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int CmdHF14ADesDump(const char *Cmd) {
|
||||||
|
(void)Cmd; // Cmd is not used so far
|
||||||
|
DropField();
|
||||||
|
uint8_t aid[3] = {0};
|
||||||
|
uint8_t app_ids[78] = {0};
|
||||||
|
uint8_t app_ids_len = 0;
|
||||||
|
|
||||||
|
uint8_t file_ids[33] = {0};
|
||||||
|
uint8_t file_ids_len = 0;
|
||||||
|
|
||||||
|
dfname_t dfnames[255];
|
||||||
|
uint8_t dfname_count = 0;
|
||||||
|
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
|
if (handler_desfire_appids(app_ids, &app_ids_len) != PM3_SUCCESS) {
|
||||||
|
PrintAndLogEx(ERR, "Can't get list of applications on tag");
|
||||||
|
DropField();
|
||||||
|
return PM3_ESOFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handler_desfire_dfnames(dfnames, &dfname_count) != PM3_SUCCESS) {
|
||||||
|
PrintAndLogEx(WARNING, _RED_("Can't get DF Names"));
|
||||||
|
DropField();
|
||||||
|
return PM3_ESOFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintAndLogEx(NORMAL, "");
|
||||||
|
PrintAndLogEx(INFO, "-- Mifare DESFire Dump ----------------------");
|
||||||
|
PrintAndLogEx(INFO, "-------------------------------------------------------------");
|
||||||
|
|
||||||
|
for (int i = 0; i < app_ids_len; i += 3) {
|
||||||
|
|
||||||
|
aid[0] = app_ids[i];
|
||||||
|
aid[1] = app_ids[i + 1];
|
||||||
|
aid[2] = app_ids[i + 2];
|
||||||
|
|
||||||
|
PrintAndLogEx(SUCCESS, " AID : " _GREEN_("%02X%02X%02X"), aid[2], aid[1], aid[0]);
|
||||||
|
PrintAndLogEx(SUCCESS, " AID Function Cluster 0x%02X: " _YELLOW_("%s"), aid[2], cluster_to_text(aid[2]));
|
||||||
|
|
||||||
|
for (int m = 0; m < dfname_count; m++) {
|
||||||
|
if (dfnames[m].aid[0] == aid[0] && dfnames[m].aid[1] == aid[1] && dfnames[m].aid[2] == aid[2]) {
|
||||||
|
PrintAndLogEx(SUCCESS, " - DF " _YELLOW_("%02X%02X") " Name : " _YELLOW_("%s"), dfnames[m].fid[1], dfnames[m].fid[0], dfnames[m].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t num_keys = 0;
|
||||||
|
uint8_t key_setting = 0;
|
||||||
|
res = handler_desfire_keysettings(&key_setting, &num_keys);
|
||||||
|
if (res != PM3_SUCCESS) return res;
|
||||||
|
|
||||||
|
res = handler_desfire_select_application(aid);
|
||||||
|
|
||||||
|
if (handler_desfire_fileids(file_ids, &file_ids_len) == PM3_SUCCESS) {
|
||||||
|
for (int j = file_ids_len - 1; j >= 0; j--) {
|
||||||
|
PrintAndLogEx(SUCCESS, "\n\n Fileid %d (0x%02x)", file_ids[j], file_ids[j]);
|
||||||
|
|
||||||
|
uint8_t filesettings[20] = {0};
|
||||||
|
int fileset_len = 0;
|
||||||
|
int res = handler_desfire_filesettings(file_ids[j], filesettings, &fileset_len);
|
||||||
|
int maclen = 0; // To be implemented
|
||||||
|
if (res == PM3_SUCCESS) {
|
||||||
|
//if (DecodeFileSettings(filesettings, fileset_len, maclen) != PM3_SUCCESS) {
|
||||||
|
if (fileset_len == 1 + 1 + 2 + 3 + maclen) {
|
||||||
|
int filesize = (filesettings[6] << 16) + (filesettings[5] << 8) + filesettings[4];
|
||||||
|
mfdes_data_t fdata;
|
||||||
|
fdata.fileno = file_ids[j];
|
||||||
|
memset(fdata.offset, 0, 3);
|
||||||
|
//memcpy(fdata.length,&filesettings[4],3);
|
||||||
|
memset(fdata.length, 0, 3);
|
||||||
|
uint8_t *data = (uint8_t *)malloc(filesize);
|
||||||
|
fdata.data = data;
|
||||||
|
if (data) {
|
||||||
|
res = handler_desfire_readdata(&fdata, MFDES_DATA_FILE);
|
||||||
|
if (res == PM3_SUCCESS) {
|
||||||
|
PrintAndLogEx(NORMAL, "\nOffset | Data | Ascii");
|
||||||
|
PrintAndLogEx(NORMAL, "----------------------------------------------------------------------------");
|
||||||
|
int len = le24toh(fdata.length);
|
||||||
|
for (int i = 0; i < len; i += 16) {
|
||||||
|
PrintAndLogEx(NORMAL, "%02d/0x%02X | %s| %s", i, i, sprint_hex(&fdata.data[i], len > 16 ? 16 : len), sprint_ascii(&fdata.data[i], len > 16 ? 16 : len));
|
||||||
|
}
|
||||||
|
free(data);
|
||||||
|
} else {
|
||||||
|
PrintAndLogEx(ERR, "Couldn't read value. Error %d", res);
|
||||||
|
res = handler_desfire_select_application(aid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (fileset_len == 1 + 1 + 2 + 4 + 4 + 4 + 1 + maclen) {
|
||||||
|
PrintAndLogEx(NORMAL, "\n\nValue file: 0x%0x", file_ids[j]);
|
||||||
|
mfdes_value_t value;
|
||||||
|
value.fileno = file_ids[j];
|
||||||
|
int len = 0;
|
||||||
|
res = handler_desfire_getvalue(&value, &len);
|
||||||
|
if (res == PM3_SUCCESS) {
|
||||||
|
PrintAndLogEx(NORMAL, "\nOffset | Value | Ascii");
|
||||||
|
PrintAndLogEx(NORMAL, "----------------------------------------------------------------------------");
|
||||||
|
for (int i = 0; i < len; i += 16) {
|
||||||
|
PrintAndLogEx(NORMAL, "%02d/0x%02X | %s| %s", i, i, sprint_hex(&value.value[i], len > 16 ? 16 : len), sprint_ascii(&value.value[i], len > 16 ? 16 : len));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PrintAndLogEx(ERR, "Couldn't read value. Error %d", res);
|
||||||
|
res = handler_desfire_select_application(aid);
|
||||||
|
}
|
||||||
|
} else if (fileset_len == 1 + 1 + 2 + 3 + 3 + 3 + maclen) {
|
||||||
|
int maxrecords = (filesettings[9] << 16) + (filesettings[8] << 8) + filesettings[7];
|
||||||
|
int filesize = (filesettings[6] << 16) + (filesettings[5] << 8) + filesettings[4];
|
||||||
|
mfdes_data_t fdata;
|
||||||
|
fdata.fileno = file_ids[j];
|
||||||
|
memset(fdata.length, 0, 3);
|
||||||
|
//memcpy(fdata.length,&filesettings[4],3);
|
||||||
|
uint8_t *data = (uint8_t *)malloc(filesize);
|
||||||
|
fdata.data = data;
|
||||||
|
if (data) {
|
||||||
|
for (int offset = 0; offset < maxrecords; offset++) {
|
||||||
|
PrintAndLogEx(NORMAL, "\n\nRecord offset: %024x", offset);
|
||||||
|
memset(data, 0, filesize);
|
||||||
|
fdata.offset[0] = offset & 0xFF;
|
||||||
|
fdata.offset[1] = (offset >> 8) & 0xFF;
|
||||||
|
fdata.offset[2] = (offset >> 16) & 0xFF;
|
||||||
|
res = handler_desfire_readdata(&fdata, MFDES_RECORD_FILE);
|
||||||
|
if (res == PM3_SUCCESS) {
|
||||||
|
PrintAndLogEx(NORMAL, "\nOffset | Data | Ascii");
|
||||||
|
PrintAndLogEx(NORMAL, "----------------------------------------------------------------------------");
|
||||||
|
int len = le24toh(fdata.length);
|
||||||
|
for (int i = 0; i < len; i += 16) {
|
||||||
|
PrintAndLogEx(NORMAL, "%02d/0x%02X | %s| %s", i, i, sprint_hex(&fdata.data[i], len > 16 ? 16 : len), sprint_ascii(&fdata.data[i], len > 16 ? 16 : len));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res = handler_desfire_select_application(aid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PrintAndLogEx(INFO, "-------------------------------------------------------------");
|
||||||
|
DropField();
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
static int CmdHF14ADesEnumApplications(const char *Cmd) {
|
static int CmdHF14ADesEnumApplications(const char *Cmd) {
|
||||||
(void)Cmd; // Cmd is not used so far
|
(void)Cmd; // Cmd is not used so far
|
||||||
DropField();
|
DropField();
|
||||||
|
@ -3305,17 +3462,523 @@ static int CmdHF14ADesAuth(const char *Cmd) {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
mfdes_auth_res_t rpayload;
|
mfdes_auth_res_t rpayload;
|
||||||
if (handler_desfire_auth(&payload, &rpayload, usedefaultkey) == PM3_SUCCESS) {
|
int error = handler_desfire_auth(&payload, &rpayload, usedefaultkey);
|
||||||
|
if (error == PM3_SUCCESS) {
|
||||||
PrintAndLogEx(SUCCESS, " Key : " _GREEN_("%s"), sprint_hex(key, keylength));
|
PrintAndLogEx(SUCCESS, " Key : " _GREEN_("%s"), sprint_hex(key, keylength));
|
||||||
PrintAndLogEx(SUCCESS, " SESSION : " _GREEN_("%s"), sprint_hex(rpayload.sessionkey, keylength));
|
PrintAndLogEx(SUCCESS, " SESSION : " _GREEN_("%s"), sprint_hex(rpayload.sessionkey, keylength));
|
||||||
PrintAndLogEx(INFO, "-------------------------------------------------------------");
|
PrintAndLogEx(INFO, "-------------------------------------------------------------");
|
||||||
} else {
|
} else {
|
||||||
|
AuthToError(error);
|
||||||
return PM3_ESOFT;
|
return PM3_ESOFT;
|
||||||
}
|
}
|
||||||
PrintAndLogEx(INFO, "-------------------------------------------------------------");
|
PrintAndLogEx(INFO, "-------------------------------------------------------------");
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DesFill2bPattern(uint8_t deskeyList[MAX_KEYS_LIST_LEN][8], size_t *deskeyListLen, uint8_t aeskeyList[MAX_KEYS_LIST_LEN][16], size_t *aeskeyListLen, uint8_t k3kkeyList[MAX_KEYS_LIST_LEN][24], size_t *k3kkeyListLen, uint32_t *startPattern) {
|
||||||
|
for (uint32_t pt = *startPattern; pt < 0x10000; pt++) {
|
||||||
|
if (*deskeyListLen != MAX_KEYS_LIST_LEN) {
|
||||||
|
deskeyList[*deskeyListLen][0] = (pt >> 8) & 0xff;
|
||||||
|
deskeyList[*deskeyListLen][1] = pt & 0xff;
|
||||||
|
memcpy(&deskeyList[*deskeyListLen][2], &deskeyList[*deskeyListLen][0], 2);
|
||||||
|
memcpy(&deskeyList[*deskeyListLen][4], &deskeyList[*deskeyListLen][0], 4);
|
||||||
|
(*deskeyListLen)++;
|
||||||
|
}
|
||||||
|
if (*aeskeyListLen != MAX_KEYS_LIST_LEN) {
|
||||||
|
aeskeyList[*aeskeyListLen][0] = (pt >> 8) & 0xff;
|
||||||
|
aeskeyList[*aeskeyListLen][1] = pt & 0xff;
|
||||||
|
memcpy(&aeskeyList[*aeskeyListLen][2], &aeskeyList[*aeskeyListLen][0], 2);
|
||||||
|
memcpy(&aeskeyList[*aeskeyListLen][4], &aeskeyList[*aeskeyListLen][0], 4);
|
||||||
|
memcpy(&aeskeyList[*aeskeyListLen][8], &aeskeyList[*aeskeyListLen][0], 8);
|
||||||
|
(*aeskeyListLen)++;
|
||||||
|
}
|
||||||
|
if (*k3kkeyListLen != MAX_KEYS_LIST_LEN) {
|
||||||
|
k3kkeyList[*k3kkeyListLen][0] = (pt >> 8) & 0xff;
|
||||||
|
k3kkeyList[*k3kkeyListLen][1] = pt & 0xff;
|
||||||
|
memcpy(&k3kkeyList[*k3kkeyListLen][2], &k3kkeyList[*k3kkeyListLen][0], 2);
|
||||||
|
memcpy(&k3kkeyList[*k3kkeyListLen][4], &k3kkeyList[*k3kkeyListLen][0], 4);
|
||||||
|
memcpy(&k3kkeyList[*k3kkeyListLen][8], &k3kkeyList[*k3kkeyListLen][0], 8);
|
||||||
|
memcpy(&k3kkeyList[*k3kkeyListLen][16], &k3kkeyList[*k3kkeyListLen][0], 4);
|
||||||
|
(*k3kkeyListLen)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
*startPattern = pt;
|
||||||
|
if ((*deskeyListLen == MAX_KEYS_LIST_LEN) && (*aeskeyListLen == MAX_KEYS_LIST_LEN) && (*k3kkeyListLen == MAX_KEYS_LIST_LEN))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(*startPattern)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int AuthCheckDesfire(uint8_t *aid, uint8_t deskeyList[MAX_KEYS_LIST_LEN][8], size_t deskeyListLen, uint8_t aeskeyList[MAX_KEYS_LIST_LEN][16], size_t aeskeyListLen, uint8_t k3kkeyList[MAX_KEYS_LIST_LEN][24], size_t k3kkeyListLen, uint8_t foundKeys[4][0xE][24 + 1], bool *result) {
|
||||||
|
int res = handler_desfire_select_application(aid);
|
||||||
|
if (res != PM3_SUCCESS) {
|
||||||
|
PrintAndLogEx(ERR, "AID %X does not exist.");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int usedkeys[0xF] = {0};
|
||||||
|
bool des = false;
|
||||||
|
bool tdes = false;
|
||||||
|
bool aes = false;
|
||||||
|
bool k3kdes = false;
|
||||||
|
|
||||||
|
if (memcmp(aid, "\x00\x00\x00", 3) != 0) {
|
||||||
|
uint8_t file_ids[33] = {0};
|
||||||
|
uint8_t file_ids_len = 0;
|
||||||
|
// Get File IDs
|
||||||
|
if (handler_desfire_fileids(file_ids, &file_ids_len) == PM3_SUCCESS) {
|
||||||
|
for (int j = file_ids_len - 1; j >= 0; j--) {
|
||||||
|
uint8_t filesettings[20] = {0};
|
||||||
|
int fileset_len = 0;
|
||||||
|
res = handler_desfire_filesettings(file_ids[j], filesettings, &fileset_len);
|
||||||
|
if (res == PM3_SUCCESS) {
|
||||||
|
uint16_t accrights = (filesettings[3] << 8) + filesettings[2];
|
||||||
|
int change_access_rights = accrights & 0xF;
|
||||||
|
int read_write_access = (accrights >> 4) & 0xF;
|
||||||
|
int write_access = (accrights >> 8) & 0xF;
|
||||||
|
int read_access = (accrights >> 12) & 0xF;
|
||||||
|
if (change_access_rights == 0xE) change_access_rights = 0x0;
|
||||||
|
if (read_write_access == 0xE) read_write_access = 0x0;
|
||||||
|
if (write_access == 0xE) write_access = 0x0;
|
||||||
|
if (read_access == 0xE) read_access = 0x0;
|
||||||
|
usedkeys[change_access_rights] = 1;
|
||||||
|
usedkeys[read_write_access] = 1;
|
||||||
|
usedkeys[write_access] = 1;
|
||||||
|
usedkeys[read_access] = 1;
|
||||||
|
if (res == PM3_SUCCESS) {
|
||||||
|
switch (fileset_len >> 6) {
|
||||||
|
case 0:
|
||||||
|
des = true;
|
||||||
|
tdes = true;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
k3kdes = true;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
aes = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (file_ids_len == 0) {
|
||||||
|
for (int z = 0; z < 0xE; z++) {
|
||||||
|
usedkeys[z] = 1;
|
||||||
|
des = true;
|
||||||
|
tdes = true;
|
||||||
|
aes = true;
|
||||||
|
k3kdes = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else des = true;
|
||||||
|
int error = PM3_SUCCESS;
|
||||||
|
bool badlen = false;
|
||||||
|
mfdes_authinput_t payload;
|
||||||
|
uint32_t curaid = (aid[0] & 0xFF) + ((aid[1] & 0xFF) << 8) + ((aid[2] & 0xFF) << 16);
|
||||||
|
if (des) {
|
||||||
|
for (int keyno = 0; keyno < 0xE; keyno++)
|
||||||
|
if (usedkeys[keyno] == 1 && foundKeys[0][keyno][0] == 0) {
|
||||||
|
for (int curkey = 0; curkey < deskeyListLen; curkey++) {
|
||||||
|
payload.keylen = 8;
|
||||||
|
memcpy(payload.key, deskeyList[curkey], 8);
|
||||||
|
payload.mode = MFDES_AUTH_DES;
|
||||||
|
payload.algo = MFDES_ALGO_DES;
|
||||||
|
payload.keyno = keyno;
|
||||||
|
mfdes_auth_res_t rpayload;
|
||||||
|
error = handler_desfire_auth(&payload, &rpayload, false);
|
||||||
|
if (error == PM3_SUCCESS) {
|
||||||
|
PrintAndLogEx(SUCCESS, "AID 0x%06X, Found DES Key %d : " _GREEN_("%s"), curaid, keyno, sprint_hex(deskeyList[curkey], 8));
|
||||||
|
foundKeys[0][keyno][0] = 0x01;
|
||||||
|
*result = true;
|
||||||
|
memcpy(&foundKeys[0][keyno][1], deskeyList[curkey], 8);
|
||||||
|
break;
|
||||||
|
} else if (error < 7) {
|
||||||
|
badlen = true;
|
||||||
|
DropField();
|
||||||
|
res = handler_desfire_select_application(aid);
|
||||||
|
if (res != PM3_SUCCESS) {
|
||||||
|
PrintAndLogEx(ERR, "AID %X does not exist.");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (badlen == true) {
|
||||||
|
badlen = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tdes) {
|
||||||
|
for (int keyno = 0; keyno < 0xE; keyno++)
|
||||||
|
if (usedkeys[keyno] == 1 && foundKeys[1][keyno][0] == 0) {
|
||||||
|
for (int curkey = 0; curkey < aeskeyListLen; curkey++) {
|
||||||
|
payload.keylen = 16;
|
||||||
|
memcpy(payload.key, aeskeyList[curkey], 16);
|
||||||
|
payload.mode = MFDES_AUTH_DES;
|
||||||
|
payload.algo = MFDES_ALGO_3DES;
|
||||||
|
payload.keyno = keyno;
|
||||||
|
mfdes_auth_res_t rpayload;
|
||||||
|
error = handler_desfire_auth(&payload, &rpayload, false);
|
||||||
|
if (error == PM3_SUCCESS) {
|
||||||
|
PrintAndLogEx(SUCCESS, "AID 0x%06X, Found 3DES Key %d : " _GREEN_("%s"), curaid, keyno, sprint_hex(aeskeyList[curkey], 16));
|
||||||
|
foundKeys[1][keyno][0] = 0x01;
|
||||||
|
*result = true;
|
||||||
|
memcpy(&foundKeys[1][keyno][1], aeskeyList[curkey], 16);
|
||||||
|
break;
|
||||||
|
} else if (error < 7) {
|
||||||
|
badlen = true;
|
||||||
|
DropField();
|
||||||
|
res = handler_desfire_select_application(aid);
|
||||||
|
if (res != PM3_SUCCESS) {
|
||||||
|
PrintAndLogEx(ERR, "AID %X does not exist.");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (badlen == true) {
|
||||||
|
badlen = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aes) {
|
||||||
|
for (int keyno = 0; keyno < 0xE; keyno++)
|
||||||
|
if (usedkeys[keyno] == 1 && foundKeys[2][keyno][0] == 0) {
|
||||||
|
for (int curkey = 0; curkey < aeskeyListLen; curkey++) {
|
||||||
|
payload.keylen = 16;
|
||||||
|
memcpy(payload.key, aeskeyList[curkey], 16);
|
||||||
|
payload.mode = MFDES_AUTH_AES;
|
||||||
|
payload.algo = MFDES_ALGO_AES;
|
||||||
|
payload.keyno = keyno;
|
||||||
|
mfdes_auth_res_t rpayload;
|
||||||
|
error = handler_desfire_auth(&payload, &rpayload, false);
|
||||||
|
if (error == PM3_SUCCESS) {
|
||||||
|
PrintAndLogEx(SUCCESS, "AID 0x%06X, Found AES Key %d : " _GREEN_("%s"), curaid, keyno, sprint_hex(aeskeyList[curkey], 16));
|
||||||
|
foundKeys[2][keyno][0] = 0x01;
|
||||||
|
*result = true;
|
||||||
|
memcpy(&foundKeys[2][keyno][1], aeskeyList[curkey], 16);
|
||||||
|
break;
|
||||||
|
} else if (error < 7) {
|
||||||
|
badlen = true;
|
||||||
|
DropField();
|
||||||
|
res = handler_desfire_select_application(aid);
|
||||||
|
if (res != PM3_SUCCESS) {
|
||||||
|
PrintAndLogEx(ERR, "AID %X does not exist.");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (badlen == true) {
|
||||||
|
badlen = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (k3kdes) {
|
||||||
|
for (int keyno = 0; keyno < 0xE; keyno++)
|
||||||
|
if (usedkeys[keyno] == 1 && foundKeys[3][keyno][0] == 0) {
|
||||||
|
for (int curkey = 0; curkey < k3kkeyListLen; curkey++) {
|
||||||
|
payload.keylen = 24;
|
||||||
|
memcpy(payload.key, k3kkeyList[curkey], 24);
|
||||||
|
payload.mode = MFDES_AUTH_ISO;
|
||||||
|
payload.algo = MFDES_ALGO_3K3DES;
|
||||||
|
payload.keyno = keyno;
|
||||||
|
mfdes_auth_res_t rpayload;
|
||||||
|
error = handler_desfire_auth(&payload, &rpayload, false);
|
||||||
|
if (error == PM3_SUCCESS) {
|
||||||
|
PrintAndLogEx(SUCCESS, "AID 0x%06X, Found 3K3 Key %d : " _GREEN_("%s"), curaid, keyno, sprint_hex(k3kkeyList[curkey], 24));
|
||||||
|
foundKeys[3][keyno][0] = 0x01;
|
||||||
|
*result = true;
|
||||||
|
memcpy(&foundKeys[3][keyno][1], k3kkeyList[curkey], 16);
|
||||||
|
break;
|
||||||
|
} else if (error < 7) {
|
||||||
|
badlen = true;
|
||||||
|
DropField();
|
||||||
|
res = handler_desfire_select_application(aid);
|
||||||
|
if (res != PM3_SUCCESS) {
|
||||||
|
PrintAndLogEx(ERR, "AID %X does not exist.");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (badlen == true) {
|
||||||
|
badlen = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DropField();
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CmdHF14aDesChk(const char *Cmd) {
|
||||||
|
int res;
|
||||||
|
uint8_t deskeyList[MAX_KEYS_LIST_LEN][8] = {{0}};
|
||||||
|
uint8_t aeskeyList[MAX_KEYS_LIST_LEN][16] = {{0}};
|
||||||
|
uint8_t k3kkeyList[MAX_KEYS_LIST_LEN][MAX_KEY_LEN] = {{0}};
|
||||||
|
size_t deskeyListLen = 0;
|
||||||
|
size_t aeskeyListLen = 0;
|
||||||
|
size_t k3kkeyListLen = 0;
|
||||||
|
uint8_t foundKeys[4][0xE][24 + 1] = {{{0}}};
|
||||||
|
|
||||||
|
CLIParserInit("hf mfdes chk",
|
||||||
|
"Checks keys with Mifare Desfire card.",
|
||||||
|
"Usage:\n"
|
||||||
|
" hf mfdes chk -a 123456 -k 000102030405060708090a0b0c0d0e0f -> check key on aid 0x123456\n"
|
||||||
|
" hf mfdes chk -d mfdes_default_keys -> check keys from dictionary against all existing aid on card\n"
|
||||||
|
" hf mfdes chk -d mfdes_default_keys -a 123456 -> check keys from dictionary against aid 0x123456\n"
|
||||||
|
" hf mfdes chk -a 123456 --pattern1b -j keys -> check all 1-byte keys pattern on aid 0x123456 and save found keys to json\n"
|
||||||
|
" hf mfdes chk -a 123456 --pattern2b --startp2b FA00 -> check all 2-byte keys pattern on aid 0x123456. Start from key FA00FA00...FA00\n");
|
||||||
|
|
||||||
|
void *argtable[] = {
|
||||||
|
arg_param_begin,
|
||||||
|
arg_strx0("aA", "aid", "<aid>", "Use specific AID (3 hex bytes, big endian)"),
|
||||||
|
arg_str0("kK", "key", "<Key>", "Key for checking (HEX 16 bytes)"),
|
||||||
|
arg_str0("dD", "dict", "<file>", "File with keys dictionary"),
|
||||||
|
arg_lit0(NULL, "pattern1b", "Check all 1-byte combinations of key (0000...0000, 0101...0101, 0202...0202, ...)"),
|
||||||
|
arg_lit0(NULL, "pattern2b", "Check all 2-byte combinations of key (0000...0000, 0001...0001, 0002...0002, ...)"),
|
||||||
|
arg_str0(NULL, "startp2b", "<Pattern>", "Start key (2-byte HEX) for 2-byte search (use with `--pattern2b`)"),
|
||||||
|
arg_str0("jJ", "json", "<file>", "Json file to save keys"),
|
||||||
|
arg_lit0("vV", "verbose", "Verbose mode."),
|
||||||
|
arg_param_end
|
||||||
|
};
|
||||||
|
CLIExecWithReturn(Cmd, argtable, false);
|
||||||
|
|
||||||
|
int aidlength = 0;
|
||||||
|
uint8_t aid[3] = {0};
|
||||||
|
CLIGetHexWithReturn(1, aid, &aidlength);
|
||||||
|
swap24(aid);
|
||||||
|
uint8_t vkey[16] = {0};
|
||||||
|
int vkeylen = 0;
|
||||||
|
CLIGetHexWithReturn(2, vkey, &vkeylen);
|
||||||
|
|
||||||
|
if (vkeylen > 0) {
|
||||||
|
if (vkeylen == 8) {
|
||||||
|
memcpy(&deskeyList[deskeyListLen], vkey, 8);
|
||||||
|
deskeyListLen++;
|
||||||
|
} else if (vkeylen == 16) {
|
||||||
|
memcpy(&aeskeyList[aeskeyListLen], vkey, 16);
|
||||||
|
aeskeyListLen++;
|
||||||
|
} else if (vkeylen == 24) {
|
||||||
|
memcpy(&k3kkeyList[k3kkeyListLen], vkey, 16);
|
||||||
|
k3kkeyListLen++;
|
||||||
|
} else {
|
||||||
|
PrintAndLogEx(ERR, "Specified key must have 8, 16 or 24 bytes length.");
|
||||||
|
CLIParserFree();
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t dict_filename[FILE_PATH_SIZE + 2] = {0};
|
||||||
|
int dict_filenamelen = 0;
|
||||||
|
if (CLIParamStrToBuf(arg_get_str(3), dict_filename, FILE_PATH_SIZE, &dict_filenamelen)) {
|
||||||
|
PrintAndLogEx(FAILED, "File name too long or invalid.");
|
||||||
|
CLIParserFree();
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pattern1b = arg_get_lit(4);
|
||||||
|
bool pattern2b = arg_get_lit(5);
|
||||||
|
|
||||||
|
if (pattern1b && pattern2b) {
|
||||||
|
PrintAndLogEx(ERR, "Pattern search mode must be 2-byte or 1-byte only.");
|
||||||
|
CLIParserFree();
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dict_filenamelen && (pattern1b || pattern2b)) {
|
||||||
|
PrintAndLogEx(ERR, "Pattern search mode and dictionary mode can't be used in one command.");
|
||||||
|
CLIParserFree();
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t startPattern = 0x0000;
|
||||||
|
uint8_t vpattern[2];
|
||||||
|
int vpatternlen = 0;
|
||||||
|
CLIGetHexWithReturn(6, vpattern, &vpatternlen);
|
||||||
|
if (vpatternlen > 0) {
|
||||||
|
if (vpatternlen > 0 && vpatternlen <= 2) {
|
||||||
|
startPattern = (vpattern[0] << 8) + vpattern[1];
|
||||||
|
} else {
|
||||||
|
PrintAndLogEx(ERR, "Pattern must be 2-byte length.");
|
||||||
|
CLIParserFree();
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
if (!pattern2b)
|
||||||
|
PrintAndLogEx(WARNING, "Pattern entered, but search mode not is 2-byte search.");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t jsonname[250] = {0};
|
||||||
|
int jsonnamelen = 0;
|
||||||
|
if (CLIParamStrToBuf(arg_get_str(7), jsonname, sizeof(jsonname), &jsonnamelen)) {
|
||||||
|
PrintAndLogEx(ERR, "Invalid json name.");
|
||||||
|
CLIParserFree();
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
jsonname[jsonnamelen] = 0;
|
||||||
|
|
||||||
|
bool verbose = arg_get_lit(8);
|
||||||
|
|
||||||
|
CLIParserFree();
|
||||||
|
|
||||||
|
// 1-byte pattern search mode
|
||||||
|
if (pattern1b) {
|
||||||
|
for (int i = 0; i < 0x100; i++)
|
||||||
|
memset(aeskeyList[i], i, 16);
|
||||||
|
for (int i = 0; i < 0x100; i++)
|
||||||
|
memset(deskeyList[i], i, 8);
|
||||||
|
for (int i = 0; i < 0x100; i++)
|
||||||
|
memset(k3kkeyList[i], i, 24);
|
||||||
|
aeskeyListLen = 0x100;
|
||||||
|
deskeyListLen = 0x100;
|
||||||
|
k3kkeyListLen = 0x100;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2-byte pattern search mode
|
||||||
|
if (pattern2b) {
|
||||||
|
DesFill2bPattern(deskeyList, &deskeyListLen, aeskeyList, &aeskeyListLen, k3kkeyList, &k3kkeyListLen, &startPattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
// dictionary mode
|
||||||
|
size_t endFilePosition = 0;
|
||||||
|
if (dict_filenamelen) {
|
||||||
|
uint16_t keycnt = 0;
|
||||||
|
res = loadFileDICTIONARYEx((char *)dict_filename, deskeyList, sizeof(deskeyList), NULL, 8, &keycnt, 0, &endFilePosition, true);
|
||||||
|
deskeyListLen = keycnt;
|
||||||
|
if (endFilePosition)
|
||||||
|
PrintAndLogEx(SUCCESS, "First part of des dictionary successfully loaded.");
|
||||||
|
endFilePosition = 0;
|
||||||
|
res = loadFileDICTIONARYEx((char *)dict_filename, aeskeyList, sizeof(aeskeyList), NULL, 16, &keycnt, 0, &endFilePosition, true);
|
||||||
|
aeskeyListLen = keycnt;
|
||||||
|
if (endFilePosition)
|
||||||
|
PrintAndLogEx(SUCCESS, "First part of aes dictionary successfully loaded.");
|
||||||
|
endFilePosition = 0;
|
||||||
|
res = loadFileDICTIONARYEx((char *)dict_filename, k3kkeyList, sizeof(k3kkeyList), NULL, 24, &keycnt, 0, &endFilePosition, true);
|
||||||
|
k3kkeyListLen = keycnt;
|
||||||
|
if (endFilePosition)
|
||||||
|
PrintAndLogEx(SUCCESS, "First part of k3kdes dictionary successfully loaded.");
|
||||||
|
endFilePosition = 0;
|
||||||
|
|
||||||
|
if (endFilePosition)
|
||||||
|
PrintAndLogEx(SUCCESS, "First part of dictionary successfully loaded.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aeskeyListLen == 0) {
|
||||||
|
PrintAndLogEx(ERR, "Aes key list is empty. Nothing to check.");
|
||||||
|
return PM3_EINVARG;
|
||||||
|
} else {
|
||||||
|
PrintAndLogEx(INFO, "Loaded " _YELLOW_("%zu") "aes keys", aeskeyListLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deskeyListLen == 0) {
|
||||||
|
PrintAndLogEx(ERR, "Des key list is empty. Nothing to check.");
|
||||||
|
return PM3_EINVARG;
|
||||||
|
} else {
|
||||||
|
PrintAndLogEx(INFO, "Loaded " _YELLOW_("%zu") "des keys", deskeyListLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (k3kkeyListLen == 0) {
|
||||||
|
PrintAndLogEx(ERR, "K3k key list is empty. Nothing to check.");
|
||||||
|
return PM3_EINVARG;
|
||||||
|
} else {
|
||||||
|
PrintAndLogEx(INFO, "Loaded " _YELLOW_("%zu") "k3kdes keys", k3kkeyListLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!verbose)
|
||||||
|
printf("Search keys:\n");
|
||||||
|
|
||||||
|
bool result = false;
|
||||||
|
uint8_t app_ids[78] = {0};
|
||||||
|
uint8_t app_ids_len = 0;
|
||||||
|
|
||||||
|
if (handler_desfire_appids(app_ids, &app_ids_len) != PM3_SUCCESS) {
|
||||||
|
PrintAndLogEx(ERR, "Can't get list of applications on tag");
|
||||||
|
DropField();
|
||||||
|
return PM3_ESOFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aidlength != 0) {
|
||||||
|
memcpy(&app_ids[0], aid, 3);
|
||||||
|
app_ids_len = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int x = 0; x < app_ids_len / 3; x++) {
|
||||||
|
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);
|
||||||
|
if (res == PM3_EOPABORTED) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pattern2b && startPattern < 0x10000) {
|
||||||
|
if (!verbose)
|
||||||
|
printf("p");
|
||||||
|
aeskeyListLen = 0;
|
||||||
|
deskeyListLen = 0;
|
||||||
|
k3kkeyListLen = 0;
|
||||||
|
DesFill2bPattern(deskeyList, &deskeyListLen, aeskeyList, &aeskeyListLen, k3kkeyList, &k3kkeyListLen, &startPattern);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (dict_filenamelen && endFilePosition) {
|
||||||
|
if (!verbose)
|
||||||
|
printf("d");
|
||||||
|
uint16_t keycnt = 0;
|
||||||
|
res = loadFileDICTIONARYEx((char *)dict_filename, deskeyList, sizeof(deskeyList), NULL, 16, &keycnt, endFilePosition, &endFilePosition, false);
|
||||||
|
deskeyListLen = keycnt;
|
||||||
|
keycnt = 0;
|
||||||
|
res = loadFileDICTIONARYEx((char *)dict_filename, aeskeyList, sizeof(aeskeyList), NULL, 16, &keycnt, endFilePosition, &endFilePosition, false);
|
||||||
|
aeskeyListLen = keycnt;
|
||||||
|
keycnt = 0;
|
||||||
|
res = loadFileDICTIONARYEx((char *)dict_filename, k3kkeyList, sizeof(k3kkeyList), NULL, 16, &keycnt, endFilePosition, &endFilePosition, false);
|
||||||
|
k3kkeyListLen = keycnt;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!verbose)
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
// save keys to json
|
||||||
|
if ((jsonnamelen > 0) && result) {
|
||||||
|
// Mifare Desfire info
|
||||||
|
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
|
||||||
|
|
||||||
|
PacketResponseNG resp;
|
||||||
|
WaitForResponse(CMD_ACK, &resp);
|
||||||
|
|
||||||
|
iso14a_card_select_t card;
|
||||||
|
memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t));
|
||||||
|
|
||||||
|
uint64_t select_status = resp.oldarg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision
|
||||||
|
|
||||||
|
uint8_t data[10 + 1 + 2 + 1 + 256 + (4 * 0xE * (24 + 1))] = {0};
|
||||||
|
uint8_t atslen = 0;
|
||||||
|
if (select_status == 1 || select_status == 2) {
|
||||||
|
memcpy(data, card.uid, card.uidlen);
|
||||||
|
data[10] = card.sak;
|
||||||
|
data[11] = card.atqa[1];
|
||||||
|
data[12] = card.atqa[0];
|
||||||
|
atslen = card.ats_len;
|
||||||
|
data[13] = atslen;
|
||||||
|
memcpy(&data[14], card.ats, atslen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// length: UID(10b)+SAK(1b)+ATQA(2b)+ATSlen(1b)+ATS(atslen)+foundKeys[2][64][AES_KEY_LEN + 1]
|
||||||
|
memcpy(&data[14 + atslen], foundKeys, 4 * 0xE * (24 + 1));
|
||||||
|
saveFileJSON((char *)jsonname, jsfMfDesfireKeys, data, 0xE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
static int CmdHF14ADesList(const char *Cmd) {
|
static int CmdHF14ADesList(const char *Cmd) {
|
||||||
(void)Cmd; // Cmd is not used so far
|
(void)Cmd; // Cmd is not used so far
|
||||||
return CmdTraceList("des");
|
return CmdTraceList("des");
|
||||||
|
@ -3364,6 +4027,8 @@ static command_t CommandTable[] = {
|
||||||
{"getvalue", CmdHF14ADesGetValueData, IfPm3Iso14443a, "Get value of file"},
|
{"getvalue", CmdHF14ADesGetValueData, IfPm3Iso14443a, "Get value of file"},
|
||||||
{"changevalue", CmdHF14ADesChangeValue, IfPm3Iso14443a, "Write value of a value file (credit/debit/clear)"},
|
{"changevalue", CmdHF14ADesChangeValue, IfPm3Iso14443a, "Write value of a value file (credit/debit/clear)"},
|
||||||
{"formatpicc", CmdHF14ADesFormatPICC, IfPm3Iso14443a, "Format PICC"},
|
{"formatpicc", CmdHF14ADesFormatPICC, IfPm3Iso14443a, "Format PICC"},
|
||||||
|
{"dump", CmdHF14ADesDump, IfPm3Iso14443a, "Dump all files"},
|
||||||
|
{"chk", CmdHF14aDesChk, IfPm3Iso14443a, "Check keys"},
|
||||||
/*
|
/*
|
||||||
ToDo:
|
ToDo:
|
||||||
|
|
||||||
|
|
|
@ -426,6 +426,44 @@ int saveFileJSON(const char *preferredName, JSONFileType ftype, uint8_t *data, s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case jsfMfDesfireKeys:
|
||||||
|
JsonSaveStr(root, "FileType", "mfdes");
|
||||||
|
JsonSaveBufAsHexCompact(root, "$.Card.UID", &data[0], 7);
|
||||||
|
JsonSaveBufAsHexCompact(root, "$.Card.SAK", &data[10], 1);
|
||||||
|
JsonSaveBufAsHexCompact(root, "$.Card.ATQA", &data[11], 2);
|
||||||
|
uint8_t datslen = data[13];
|
||||||
|
if (datslen > 0)
|
||||||
|
JsonSaveBufAsHexCompact(root, "$.Card.ATS", &data[14], datslen);
|
||||||
|
|
||||||
|
uint8_t dvdata[4][0xE][24 + 1] = {{{0}}};
|
||||||
|
memcpy(dvdata, &data[14 + datslen], 4 * 0xE * (24 + 1));
|
||||||
|
|
||||||
|
for (int i = 0; i < (int)datalen; i++) {
|
||||||
|
char path[PATH_MAX_LENGTH] = {0};
|
||||||
|
|
||||||
|
if (dvdata[0][i][0]) {
|
||||||
|
memset(path, 0x00, sizeof(path));
|
||||||
|
sprintf(path, "$.DES.%d.Key", i);
|
||||||
|
JsonSaveBufAsHexCompact(root, path, &dvdata[0][i][1], 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dvdata[1][i][0]) {
|
||||||
|
memset(path, 0x00, sizeof(path));
|
||||||
|
sprintf(path, "$.3DES.%d.Key", i);
|
||||||
|
JsonSaveBufAsHexCompact(root, path, &dvdata[1][i][1], 16);
|
||||||
|
}
|
||||||
|
if (dvdata[2][i][0]) {
|
||||||
|
memset(path, 0x00, sizeof(path));
|
||||||
|
sprintf(path, "$.AES.%d.Key", i);
|
||||||
|
JsonSaveBufAsHexCompact(root, path, &dvdata[2][i][1], 16);
|
||||||
|
}
|
||||||
|
if (dvdata[3][i][0]) {
|
||||||
|
memset(path, 0x00, sizeof(path));
|
||||||
|
sprintf(path, "$.K3KDES.%d.Key", i);
|
||||||
|
JsonSaveBufAsHexCompact(root, path, &dvdata[3][i][1], 24);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
case jsfSettings:
|
case jsfSettings:
|
||||||
preferences_save_callback(root);
|
preferences_save_callback(root);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -63,6 +63,7 @@ typedef enum {
|
||||||
jsfT5555,
|
jsfT5555,
|
||||||
jsfMfPlusKeys,
|
jsfMfPlusKeys,
|
||||||
jsfSettings,
|
jsfSettings,
|
||||||
|
jsfMfDesfireKeys,
|
||||||
} JSONFileType;
|
} JSONFileType;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
Loading…
Reference in a new issue