mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2024-09-20 15:26:13 +08:00
Gallagher key checking is now supported on MIFARE Desfire
Both `hf mfdes auth` and `hf mfdes chk` now support Key Diversification for AN10922 and as special treat, Gallagher issued cards. For `hf mfdes auth`: ``` -d, --kdf <kdf> Key Derivation Function (KDF) (0=None, 1=AN10922, 2=Gallagher) -i, --kdfi <kdfi> KDF input (HEX 1-31 bytes) ``` And for `hf mfdes chk`: ``` -f, --kdf <kdf> Key Derivation Function (KDF) (0=None, 1=AN10922, Gallagher) -i, --kdfi <kdfi> KDF input (HEX 1-31 bytes) ``` Examples: - `hf mfdes auth -a 2081f4 -m 3 -t 4 -d 2 -n 2 -k 00112233445566778899aabbccddeeff` Will diversify the key for key `2` on AID `2081F4` for Gallagher issued cards - `hf mfdes chk -f 1 -i 00112233 -d mfdes_default_keys` Will read in all the default keys from the dictionary, and diversify them using AN10922 with the input data `00112233` - `hf mfdes chk -f 2 -d mfdes_default_keys` Will read in all the default keys from the dictionary, and diversify them using AN10922 but with input data generated from the card's UID, AID and key number.
This commit is contained in:
parent
634c69398d
commit
c9a10631de
|
@ -336,5 +336,21 @@ FFFFFF General Issuer Information (FIDs 00: MAD Version; 01: Card Holder; 02: Ca
|
|||
"Name": "MemberCard",
|
||||
"Description": "CAR2GO - Member Card",
|
||||
"Type": "carsharing"
|
||||
},
|
||||
{
|
||||
"AID": "2F81F4",
|
||||
"Vendor": "Gallagher",
|
||||
"Country": "NZ",
|
||||
"Name": "Access control",
|
||||
"Description": "Card Application Directory (CAD)",
|
||||
"Type": ""
|
||||
},
|
||||
{
|
||||
"AID": "2081F4",
|
||||
"Vendor": "Gallagher",
|
||||
"Country": "NZ",
|
||||
"Name": "Access control",
|
||||
"Description": "Cardax Card Data Application",
|
||||
"Type": ""
|
||||
}
|
||||
]
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "mifare/mifaredefault.h" // default keys
|
||||
#include "mifare/ndef.h" // NDEF
|
||||
#include "mifare/mad.h"
|
||||
#include "generator.h"
|
||||
|
||||
#define MAX_KEY_LEN 24
|
||||
#define MAX_KEYS_LIST_LEN 1024
|
||||
|
@ -757,6 +758,20 @@ static int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rp
|
|||
if (g_debugMode) {
|
||||
PrintAndLogEx(INFO, " Derrived key: " _GREEN_("%s"), sprint_hex(key->data, key_block_size(key)));
|
||||
}
|
||||
} else if (payload->kdfAlgo == MFDES_KDF_ALGO_GALLAGHER) {
|
||||
// We will overrite any provided KDF input since a gallagher specific KDF was requested.
|
||||
payload->kdfInputLen = 11;
|
||||
|
||||
if (mfdes_kdf_input_gallagher(tag->info.uid, tag->info.uidlen, payload->keyno, tag->selected_application, payload->kdfInput, &payload->kdfInputLen) != PM3_SUCCESS) {
|
||||
PrintAndLogEx(FAILED, "Could not generate Gallagher KDF input");
|
||||
}
|
||||
|
||||
mifare_kdf_an10922(key, payload->kdfInput, payload->kdfInputLen);
|
||||
|
||||
if (g_debugMode) {
|
||||
PrintAndLogEx(INFO, " KDF Input: " _YELLOW_("%s"), sprint_hex(payload->kdfInput, payload->kdfInputLen));
|
||||
PrintAndLogEx(INFO, " Derrived key: " _GREEN_("%s"), sprint_hex(key->data, key_block_size(key)));
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subcommand = MFDES_AUTHENTICATE;
|
||||
|
@ -2100,6 +2115,13 @@ static int desfire_authenticate(int cmdAuthMode, int cmdAuthAlgo, uint8_t *aid,
|
|||
PrintAndLogEx(FAILED, "KDF AN10922 algo requires an input of length 1-31 bytes.");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
case MFDES_KDF_ALGO_GALLAGHER:
|
||||
// TODO: 2TDEA and 3TDEA keys use an input length of 1-15 bytes
|
||||
if (cmdAuthAlgo != MFDES_ALGO_AES) {
|
||||
PrintAndLogEx(FAILED, "Crypto algo not valid for the KDF AN10922 algo.");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
// KDF input arg is ignored as it'll be generated.
|
||||
case MFDES_KDF_ALGO_NONE:
|
||||
break;
|
||||
default:
|
||||
|
@ -3924,7 +3946,7 @@ static int CmdHF14ADesAuth(const char *Cmd) {
|
|||
arg_strx0("a", "aid", "<aid>", "AID used for authentification (HEX 3 bytes)"),
|
||||
arg_int0("n", "keyno", "<keyno>", "Key number used for authentification"),
|
||||
arg_str0("k", "key", "<Key>", "Key for checking (HEX 8-24 bytes)"),
|
||||
arg_int0("d", "kdf", "<kdf>", "Key Derivation Function (KDF) (0=None, 1=AN10922)"),
|
||||
arg_int0("d", "kdf", "<kdf>", "Key Derivation Function (KDF) (0=None, 1=AN10922, 2=Gallagher)"),
|
||||
arg_str0("i", "kdfi", "<kdfi>", "KDF input (HEX 1-31 bytes)"),
|
||||
arg_param_end
|
||||
};
|
||||
|
@ -4052,6 +4074,7 @@ static int AuthCheckDesfire(uint8_t *aid,
|
|||
uint8_t deskeyList[MAX_KEYS_LIST_LEN][8], uint32_t deskeyListLen,
|
||||
uint8_t aeskeyList[MAX_KEYS_LIST_LEN][16], uint32_t aeskeyListLen,
|
||||
uint8_t k3kkeyList[MAX_KEYS_LIST_LEN][24], uint32_t k3kkeyListLen,
|
||||
uint8_t cmdKdfAlgo, uint8_t kdfInputLen, uint8_t *kdfInput,
|
||||
uint8_t foundKeys[4][0xE][24 + 1], bool *result) {
|
||||
|
||||
uint32_t curaid = (aid[0] & 0xFF) + ((aid[1] & 0xFF) << 8) + ((aid[2] & 0xFF) << 16);
|
||||
|
@ -4213,7 +4236,7 @@ static int AuthCheckDesfire(uint8_t *aid,
|
|||
if (usedkeys[keyno] == 1 && foundKeys[2][keyno][0] == 0) {
|
||||
for (uint32_t curkey = 0; curkey < aeskeyListLen; curkey++) {
|
||||
mfdes_auth_res_t rpayload;
|
||||
error = desfire_authenticate(MFDES_AUTH_AES, MFDES_ALGO_AES, aid, aeskeyList[curkey], keyno, 0, 0, NULL, &rpayload);
|
||||
error = desfire_authenticate(MFDES_AUTH_AES, MFDES_ALGO_AES, aid, aeskeyList[curkey], keyno, cmdKdfAlgo, kdfInputLen, kdfInput, &rpayload);
|
||||
if (error == PM3_SUCCESS) {
|
||||
PrintAndLogEx(SUCCESS, "AID 0x%06X, Found AES Key %u : " _GREEN_("%s"), curaid, keyno, sprint_hex(aeskeyList[curkey], 16));
|
||||
foundKeys[2][keyno][0] = 0x01;
|
||||
|
@ -4302,6 +4325,8 @@ static int CmdHF14aDesChk(const char *Cmd) {
|
|||
arg_str0(NULL, "startp2b", "<Pattern>", "Start key (2-byte HEX) for 2-byte search (use with `--pattern2b`)"),
|
||||
arg_str0("j", "json", "<file>", "Json file to save keys"),
|
||||
arg_lit0("v", "verbose", "Verbose mode."),
|
||||
arg_int0("f", "kdf", "<kdf>", "Key Derivation Function (KDF) (0=None, 1=AN10922, 2=Gallagher)"),
|
||||
arg_str0("i", "kdfi", "<kdfi>", "KDF input (HEX 1-31 bytes)"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||
|
@ -4381,6 +4406,12 @@ static int CmdHF14aDesChk(const char *Cmd) {
|
|||
|
||||
bool verbose = arg_get_lit(ctx, 8);
|
||||
|
||||
// Get KDF input
|
||||
uint8_t kdfInput[31] = {0};
|
||||
int kdfInputLen = 0;
|
||||
uint8_t cmdKDFAlgo = arg_get_int_def(ctx, 9, 0);
|
||||
CLIGetHexWithReturn(ctx, 10, kdfInput, &kdfInputLen);
|
||||
|
||||
CLIParserFree(ctx);
|
||||
|
||||
// 1-byte pattern search mode
|
||||
|
@ -4446,6 +4477,17 @@ static int CmdHF14aDesChk(const char *Cmd) {
|
|||
uint8_t app_ids[78] = {0};
|
||||
uint32_t app_ids_len = 0;
|
||||
|
||||
clearCommandBuffer();
|
||||
|
||||
mfdes_info_res_t info = {0};
|
||||
res = mfdes_get_info(&info);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
// TODO: Store this UID someowhere not global
|
||||
memcpy(tag->info.uid, info.uid, info.uidlen);
|
||||
tag->info.uidlen = info.uidlen;
|
||||
|
||||
if (handler_desfire_appids(app_ids, &app_ids_len) != PM3_SUCCESS) {
|
||||
PrintAndLogEx(ERR, "Can't get list of applications on tag");
|
||||
DropField();
|
||||
|
@ -4462,7 +4504,7 @@ static int CmdHF14aDesChk(const char *Cmd) {
|
|||
uint32_t curaid = (app_ids[x * 3] & 0xFF) + ((app_ids[(x * 3) + 1] & 0xFF) << 8) + ((app_ids[(x * 3) + 2] & 0xFF) << 16);
|
||||
PrintAndLogEx(ERR, "Checking aid 0x%06X...", curaid);
|
||||
|
||||
res = AuthCheckDesfire(&app_ids[x * 3], deskeyList, deskeyListLen, aeskeyList, aeskeyListLen, k3kkeyList, k3kkeyListLen, foundKeys, &result);
|
||||
res = AuthCheckDesfire(&app_ids[x * 3], deskeyList, deskeyListLen, aeskeyList, aeskeyListLen, k3kkeyList, k3kkeyListLen, cmdKDFAlgo, kdfInputLen, kdfInput, foundKeys, &result);
|
||||
if (res == PM3_EOPABORTED) {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -423,6 +423,47 @@ uint32_t lf_t55xx_white_pwdgen(uint32_t id) {
|
|||
return pwd;
|
||||
}
|
||||
|
||||
// Gallagher Desfire Key Diversification Input for Cardax Card Data Application
|
||||
int mfdes_kdf_input_gallagher(uint8_t *uid, uint8_t uidLen, uint8_t keyNo, uint32_t aid, uint8_t *kdfInputOut, uint8_t *kdfInputLen) {
|
||||
if (uid == NULL || (uidLen != 4 && uidLen != 7) || keyNo > 2 || kdfInputOut == NULL || kdfInputLen == NULL) {
|
||||
if (g_debugMode) {
|
||||
PrintAndLogEx(WARNING, "Invalid arguments");
|
||||
}
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
// Verify the AppID is a valid Gallagher AppID
|
||||
if ((aid & 0xF0FFFF) != 0x2081F4) {
|
||||
if (g_debugMode) {
|
||||
PrintAndLogEx(WARNING, "Invalid Gallagher AID %06X", aid);
|
||||
}
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
int len = 0;
|
||||
// If the keyNo == 1, then omit the UID.
|
||||
if (keyNo != 1) {
|
||||
if (*kdfInputLen < (4 + uidLen)) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
memcpy(kdfInputOut, uid, uidLen);
|
||||
len += uidLen;
|
||||
} else if (*kdfInputLen < 4) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
kdfInputOut[len++] = keyNo;
|
||||
|
||||
kdfInputOut[len++] = aid & 0xff;
|
||||
kdfInputOut[len++] = (aid >> 8) & 0xff;
|
||||
kdfInputOut[len++] = (aid >> 16) & 0xff;
|
||||
|
||||
*kdfInputLen = len;
|
||||
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
// Self tests
|
||||
//------------------------------------
|
||||
|
|
|
@ -43,5 +43,7 @@ int mfc_algo_sky_all(uint8_t *uid, uint8_t *keys);
|
|||
|
||||
uint32_t lf_t55xx_white_pwdgen(uint32_t id);
|
||||
|
||||
int mfdes_kdf_input_gallagher(uint8_t *uid, uint8_t uidLen, uint8_t keyNo, uint32_t aid, uint8_t *kdfInputOut, uint8_t *kdfInputLen);
|
||||
|
||||
int generator_selftest(void);
|
||||
#endif
|
||||
|
|
|
@ -97,6 +97,7 @@ typedef enum {
|
|||
typedef enum {
|
||||
MFDES_KDF_ALGO_NONE = 0,
|
||||
MFDES_KDF_ALGO_AN10922 = 1,
|
||||
MFDES_KDF_ALGO_GALLAGHER = 2,
|
||||
} mifare_des_kdf_algo_t;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
Loading…
Reference in a new issue