From c9a10631de05f6d24e892eb23c827c64dc30c5f9 Mon Sep 17 00:00:00 2001 From: NZSmartie Date: Mon, 2 Nov 2020 01:22:19 +1300 Subject: [PATCH] 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 Key Derivation Function (KDF) (0=None, 1=AN10922, 2=Gallagher) -i, --kdfi KDF input (HEX 1-31 bytes) ``` And for `hf mfdes chk`: ``` -f, --kdf Key Derivation Function (KDF) (0=None, 1=AN10922, Gallagher) -i, --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. --- client/resources/aid_desfire.json | 16 +++++++++++ client/src/cmdhfmfdes.c | 48 +++++++++++++++++++++++++++++-- common/generator.c | 41 ++++++++++++++++++++++++++ common/generator.h | 2 ++ include/mifare.h | 1 + 5 files changed, 105 insertions(+), 3 deletions(-) diff --git a/client/resources/aid_desfire.json b/client/resources/aid_desfire.json index 4dddc2a52..9bedab608 100644 --- a/client/resources/aid_desfire.json +++ b/client/resources/aid_desfire.json @@ -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": "" } ] diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index dc9c364b8..ff2656c33 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -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 used for authentification (HEX 3 bytes)"), arg_int0("n", "keyno", "", "Key number used for authentification"), arg_str0("k", "key", "", "Key for checking (HEX 8-24 bytes)"), - arg_int0("d", "kdf", "", "Key Derivation Function (KDF) (0=None, 1=AN10922)"), + arg_int0("d", "kdf", "", "Key Derivation Function (KDF) (0=None, 1=AN10922, 2=Gallagher)"), arg_str0("i", "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", "", "Start key (2-byte HEX) for 2-byte search (use with `--pattern2b`)"), arg_str0("j", "json", "", "Json file to save keys"), arg_lit0("v", "verbose", "Verbose mode."), + arg_int0("f", "kdf", "", "Key Derivation Function (KDF) (0=None, 1=AN10922, 2=Gallagher)"), + arg_str0("i", "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; } diff --git a/common/generator.c b/common/generator.c index a6324d24f..f40070346 100644 --- a/common/generator.c +++ b/common/generator.c @@ -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 //------------------------------------ diff --git a/common/generator.h b/common/generator.h index b1c3c82d0..40dd51e78 100644 --- a/common/generator.h +++ b/common/generator.h @@ -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 diff --git a/include/mifare.h b/include/mifare.h index 50dbdc3b0..804d6bd78 100644 --- a/include/mifare.h +++ b/include/mifare.h @@ -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; //-----------------------------------------------------------------------------