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:
NZSmartie 2020-11-02 01:22:19 +13:00
parent 634c69398d
commit c9a10631de
No known key found for this signature in database
GPG key ID: B7E1258B1896B531
5 changed files with 105 additions and 3 deletions

View file

@ -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": ""
}
]

View file

@ -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;
}

View file

@ -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
//------------------------------------

View file

@ -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

View file

@ -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;
//-----------------------------------------------------------------------------