Reorder function definitions

Order is roughly:
- exported helpers
- helpers
- read -> create -> delete
- exported commands
This commit is contained in:
Matt Moran 2022-01-06 00:11:30 +13:00
commit 82cd44e99f

View file

@ -32,42 +32,6 @@ static const uint8_t DEFAULT_SITE_KEY[] = {
0x34, 0x30, 0x2E, 0xB2, 0x99, 0xAA, 0xB4, 0x56,
};
/**
* @brief Reverses the bytes in AID. Used when parsing CLI args
* (because Proxmark displays AIDs in reverse byte order).
*/
static void reverse_aid(uint8_t *aid) {
uint8_t tmp = aid[0];
aid[0] = aid[2];
aid[2] = tmp;
}
/**
* @brief Converts a Card Application Directory format application ID to an integer.
* Note that the CAD stores AIDs in reverse order, so this function is different to DesfireAIDByteToUint().
*/
static uint32_t cad_aid_byte_to_uint(uint8_t *data) {
return data[2] + (data[1] << 8) + (data[0] << 16);
}
/**
* @brief Converts an integer application ID to Card Application Directory format.
* Note that the CAD stores AIDs in reverse order, so this function is different to DesfireAIDUintToByte().
*/
static void cad_aid_uint_to_byte(uint32_t aid, uint8_t *data) {
data[2] = aid & 0xff;
data[1] = (aid >> 8) & 0xff;
data[0] = (aid >> 16) & 0xff;
}
/**
* @brief Returns true if the Card Application Directory entry
* is for the specified region & facility, false otherwise.
*/
static bool cad_facility_match(uint8_t *entry, uint8_t region_code, uint16_t facility_code) {
return entry[0] == region_code && (entry[1] << 8) + entry[2] == facility_code;
}
/**
* @brief Create Gallagher Application Master Key by diversifying
* the MIFARE Site Key with card UID, key number, and application ID.
@ -104,6 +68,42 @@ int hfgal_diversify_key(uint8_t *site_key, uint8_t *uid, uint8_t uid_len,
return PM3_SUCCESS;
}
/**
* @brief Reverses the bytes in AID. Used when parsing CLI args
* (because Proxmark displays AIDs in reverse byte order).
*/
static void reverse_aid(uint8_t *aid) {
uint8_t tmp = aid[0];
aid[0] = aid[2];
aid[2] = tmp;
}
/**
* @brief Converts a Card Application Directory format application ID to an integer.
* Note that the CAD stores AIDs in reverse order, so this function is different to DesfireAIDByteToUint().
*/
static uint32_t cad_aid_byte_to_uint(uint8_t *data) {
return data[2] + (data[1] << 8) + (data[0] << 16);
}
/**
* @brief Converts an integer application ID to Card Application Directory format.
* Note that the CAD stores AIDs in reverse order, so this function is different to DesfireAIDUintToByte().
*/
static void cad_aid_uint_to_byte(uint32_t aid, uint8_t *data) {
data[2] = aid & 0xff;
data[1] = (aid >> 8) & 0xff;
data[0] = (aid >> 16) & 0xff;
}
/**
* @brief Returns true if the Card Application Directory entry
* is for the specified region & facility, false otherwise.
*/
static bool cad_facility_match(uint8_t *entry, uint8_t region_code, uint16_t facility_code) {
return entry[0] == region_code && (entry[1] << 8) + entry[2] == facility_code;
}
/**
* @brief Select application ID.
*/
@ -195,51 +195,25 @@ static uint32_t find_available_gallagher_aid(DesfireContext_t *ctx, bool verbose
}
/**
* @brief Read Gallagher Card Application Directory from card.
* @brief Delete the CAD or an application that contains cardholder credentials.
*
* @param dest_buf Buffer to copy Card Application Directory into.
* @param dest_buf_len Size of dest_buf. Must be at least 108 bytes.
* @param num_entries Will be set to the number of entries in the Card Application Directory.
* @param site_key MIFARE site key.
* @param aid Application ID to remove.
*/
static int hfgal_read_cad(DesfireContext_t *ctx, uint8_t *dest_buf,
uint8_t dest_buf_len, uint8_t *num_entries, bool verbose) {
if (dest_buf_len < 3 * 36) {
PrintAndLogEx(ERR, "hfgal_read_cad destination buffer is incorrectly sized. "
"Received length %d, must be at least %d", dest_buf_len, 3 * 36);
return PM3_EINVARG;
}
static int hfgal_delete_app(DesfireContext_t *ctx, uint8_t *site_key,
uint32_t aid, bool verbose) {
// Select application & authenticate
DesfireSetKeyNoClear(ctx, 0, T_AES, site_key);
DesfireSetKdf(ctx, MFDES_KDF_ALGO_GALLAGHER, NULL, 0);
int res = select_aid_and_authenticate(ctx, aid, verbose);
HFGAL_RET_IF_ERR(res);
// Get card AIDs from Card Application Directory (which contains 1 to 3 files)
int res = select_aid(ctx, CAD_AID, verbose);
HFGAL_RET_IF_ERR_WITH_MSG(res, "Failed selecting Card Application Directory, does AID %06X exist?", CAD_AID);
// Read up to 3 files with 6x 6-byte entries each
for (uint8_t i = 0; i < 3; i++) {
size_t read_len;
res = DesfireReadFile(ctx, i, 0, 36, &dest_buf[i * 36], &read_len);
if (res != PM3_SUCCESS && res != PM3_EAPDU_FAIL)
HFGAL_RET_ERR(res, "Failed reading file %d in Card Application Directory (AID %06X)", i, CAD_AID);
// end if the last entry is NULL
if (memcmp(&dest_buf[36 * i + 30], "\0\0\0\0\0\0", 6) == 0) break;
}
// Count number of entries (i.e. count until we hit a NULL entry)
*num_entries = 0;
for (uint8_t i = 0; i < dest_buf_len; i += 6) {
if (memcmp(&dest_buf[i], "\0\0\0\0\0\0", 6) == 0) break;
*num_entries += 1;
}
if (verbose) {
// Print what we found
PrintAndLogEx(SUCCESS, "Card Application Directory contains:" NOLF);
for (int i = 0; i < *num_entries; i++)
PrintAndLogEx(NORMAL, "%s %06X" NOLF, (i == 0) ? "" : ",",
cad_aid_byte_to_uint(&dest_buf[i * 6 + 3]));
PrintAndLogEx(NORMAL, "");
}
// Delete application
DesfireSetCommMode(ctx, DCMMACed);
res = DesfireDeleteApplication(ctx, aid);
HFGAL_RET_IF_ERR_WITH_MSG(res, "Failed deleting AID %06X", aid);
PrintAndLogEx(INFO, "Successfully deleted AID %06X", aid);
return PM3_SUCCESS;
}
@ -250,7 +224,7 @@ static int hfgal_read_cad(DesfireContext_t *ctx, uint8_t *dest_buf,
* @param site_key MIFARE site key.
* @param creds Decoded credentials will be stored in this structure.
*/
static int hfgal_read_app_creds(DesfireContext_t *ctx, uint32_t aid, uint8_t *site_key,
static int hfgal_read_creds_app(DesfireContext_t *ctx, uint32_t aid, uint8_t *site_key,
GallagherCredentials_t *creds, bool verbose) {
// Check that card UID has been set
if (ctx->uidlen == 0)
@ -287,137 +261,6 @@ static int hfgal_read_app_creds(DesfireContext_t *ctx, uint32_t aid, uint8_t *si
return PM3_SUCCESS;
}
/**
* @brief Read credentials from a Gallagher card.
*
* @param aid Application ID to read. If 0, then the Card Application Directory will be queried and all entries will be read.
* @param site_key MIFARE site key.
* @param quiet Suppress error messages. Used when in continuous reader mode.
*/
static int hfgal_read_card(uint32_t aid, uint8_t *site_key, bool verbose, bool quiet) {
DropField();
clearCommandBuffer();
// Set up context
DesfireContext_t dctx = {0};
DesfireClearContext(&dctx);
// Get card UID (for key diversification)
int res = DesfireGetCardUID(&dctx);
HFGAL_RET_IF_ERR_MAYBE_MSG(res, !quiet, "Failed retrieving card UID");
// Find AIDs to process (from CLI args or the Card Application Directory)
uint8_t cad[36 * 3] = {0};
uint8_t num_entries = 0;
if (aid != 0) {
cad_aid_uint_to_byte(aid, &cad[3]);
num_entries = 1;
} else {
res = hfgal_read_cad(&dctx, cad, ARRAYLEN(cad), &num_entries, verbose);
HFGAL_RET_IF_ERR_MAYBE_MSG(res, !quiet, "Failed reading Card Application Directory");
}
// Loop through each application in the CAD
for (uint8_t i = 0; i < num_entries * 6; i += 6) {
uint16_t region_code = cad[i + 0];
uint16_t facility_code = (cad[i + 1] << 8) + cad[i + 2];
uint32_t current_aid = cad_aid_byte_to_uint(&cad[i + 3]);
if (verbose) {
if (region_code > 0 || facility_code > 0)
PrintAndLogEx(INFO, "Reading AID: %06X, region: %u, facility: %u", current_aid, region_code, facility_code);
else
PrintAndLogEx(INFO, "Reading AID: %06X", current_aid);
}
// Read & decode credentials
GallagherCredentials_t creds = {0};
res = hfgal_read_app_creds(&dctx, current_aid, site_key, &creds, verbose);
HFGAL_RET_IF_ERR_MAYBE_MSG(res, !quiet, "Failed reading card application credentials");
PrintAndLogEx(SUCCESS, "GALLAGHER (AID %06X) - Region: " _GREEN_("%u") ", Facility: " _GREEN_("%u")
", Card No.: " _GREEN_("%u") ", Issue Level: " _GREEN_("%u"), current_aid,
creds.region_code, creds.facility_code, creds.card_number, creds.issue_level);
}
return PM3_SUCCESS;
}
static int CmdGallagherReader(const char *cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf gallagher reader",
"Read a GALLAGHER tag",
"hf gallagher reader --aid 2081f4 --sitekey 00112233445566778899aabbccddeeff"
" -> act as a reader that skips reading the Card Application Directory and uses a non-default site key\n"
"hf gallagher reader -@ -> continuous reader mode"
);
void *argtable[] = {
arg_param_begin,
arg_str0(NULL, "aid", "<hex>", "Application ID to read (3 bytes)"),
arg_str0("k", "sitekey", "<hex>", "Master site key to compute diversified keys (16 bytes) [default=3112B738D8862CCD34302EB299AAB456]"),
arg_lit0(NULL, "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose mode"),
arg_lit0("@", "continuous", "Continuous reader mode"),
arg_param_end
};
CLIExecWithReturn(ctx, cmd, argtable, true);
int aid_len = 0;
uint8_t aid_buf[3] = {0};
CLIGetHexWithReturn(ctx, 1, aid_buf, &aid_len);
if (aid_len > 0 && aid_len != 3)
HFGAL_RET_ERR(PM3_EINVARG, "--aid must be 3 bytes");
reverse_aid(aid_buf); // PM3 displays AIDs backwards
uint32_t aid = DesfireAIDByteToUint(aid_buf);
int site_key_len = 0;
uint8_t site_key[16] = {0};
memcpy(site_key, DEFAULT_SITE_KEY, ARRAYLEN(site_key));
CLIGetHexWithReturn(ctx, 2, site_key, &site_key_len);
if (site_key_len > 0 && site_key_len != 16)
HFGAL_RET_ERR(PM3_EINVARG, "--sitekey must be 16 bytes");
SetAPDULogging(arg_get_lit(ctx, 3));
bool verbose = arg_get_lit(ctx, 4);
bool continuous_mode = arg_get_lit(ctx, 5);
CLIParserFree(ctx);
if (!continuous_mode)
// Read single card
return hfgal_read_card(aid, site_key, verbose, false);
// Loop until <Enter> is pressed
PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
while (!kbd_enter_pressed())
hfgal_read_card(aid, site_key, verbose, !verbose);
return PM3_SUCCESS;
}
/**
* @brief Delete the CAD or an application that contains cardholder credentials.
*
* @param site_key MIFARE site key.
* @param aid Application ID to remove.
*/
static int hfgal_delete_app(DesfireContext_t *ctx, uint8_t *site_key,
uint32_t aid, bool verbose) {
// Select application & authenticate
DesfireSetKeyNoClear(ctx, 0, T_AES, site_key);
DesfireSetKdf(ctx, MFDES_KDF_ALGO_GALLAGHER, NULL, 0);
int res = select_aid_and_authenticate(ctx, aid, verbose);
HFGAL_RET_IF_ERR(res);
// Delete application
DesfireSetCommMode(ctx, DCMMACed);
res = DesfireDeleteApplication(ctx, aid);
HFGAL_RET_IF_ERR_WITH_MSG(res, "Failed deleting AID %06X", aid);
PrintAndLogEx(INFO, "Successfully deleted AID %06X", aid);
return PM3_SUCCESS;
}
/**
* @brief Create a new application to store Gallagher cardholder credentials.
*
@ -535,6 +378,55 @@ static int hfgal_create_creds_file(DesfireContext_t *ctx, uint8_t *site_key, uin
return PM3_SUCCESS;
}
/**
* @brief Read Gallagher Card Application Directory from card.
*
* @param dest_buf Buffer to copy Card Application Directory into.
* @param dest_buf_len Size of dest_buf. Must be at least 108 bytes.
* @param num_entries Will be set to the number of entries in the Card Application Directory.
*/
static int hfgal_read_cad(DesfireContext_t *ctx, uint8_t *dest_buf,
uint8_t dest_buf_len, uint8_t *num_entries, bool verbose) {
if (dest_buf_len < 3 * 36) {
PrintAndLogEx(ERR, "hfgal_read_cad destination buffer is incorrectly sized. "
"Received length %d, must be at least %d", dest_buf_len, 3 * 36);
return PM3_EINVARG;
}
// Get card AIDs from Card Application Directory (which contains 1 to 3 files)
int res = select_aid(ctx, CAD_AID, verbose);
HFGAL_RET_IF_ERR_WITH_MSG(res, "Failed selecting Card Application Directory, does AID %06X exist?", CAD_AID);
// Read up to 3 files with 6x 6-byte entries each
for (uint8_t i = 0; i < 3; i++) {
size_t read_len;
res = DesfireReadFile(ctx, i, 0, 36, &dest_buf[i * 36], &read_len);
if (res != PM3_SUCCESS && res != PM3_EAPDU_FAIL)
HFGAL_RET_ERR(res, "Failed reading file %d in Card Application Directory (AID %06X)", i, CAD_AID);
// end if the last entry is NULL
if (memcmp(&dest_buf[36 * i + 30], "\0\0\0\0\0\0", 6) == 0) break;
}
// Count number of entries (i.e. count until we hit a NULL entry)
*num_entries = 0;
for (uint8_t i = 0; i < dest_buf_len; i += 6) {
if (memcmp(&dest_buf[i], "\0\0\0\0\0\0", 6) == 0) break;
*num_entries += 1;
}
if (verbose) {
// Print what we found
PrintAndLogEx(SUCCESS, "Card Application Directory contains:" NOLF);
for (int i = 0; i < *num_entries; i++)
PrintAndLogEx(NORMAL, "%s %06X" NOLF, (i == 0) ? "" : ",",
cad_aid_byte_to_uint(&dest_buf[i * 6 + 3]));
PrintAndLogEx(NORMAL, "");
}
return PM3_SUCCESS;
}
/**
* @brief Create the Gallagher Card Application Directory.
*
@ -698,8 +590,7 @@ static int hfgal_remove_aid_from_cad(DesfireContext_t *ctx, uint8_t *site_key,
uint8_t cad[36 * 3] = {0};
uint8_t num_entries = 0;
int res = hfgal_read_cad(
ctx, cad, ARRAYLEN(cad), &num_entries, verbose);
int res = hfgal_read_cad(ctx, cad, ARRAYLEN(cad), &num_entries, verbose);
HFGAL_RET_IF_ERR(res);
// Check if facility already exists in CAD
@ -764,6 +655,114 @@ static int hfgal_remove_aid_from_cad(DesfireContext_t *ctx, uint8_t *site_key,
return PM3_SUCCESS;
}
/**
* @brief Read credentials from a Gallagher card.
*
* @param aid Application ID to read. If 0, then the Card Application Directory will be queried and all entries will be read.
* @param site_key MIFARE site key.
* @param quiet Suppress error messages. Used when in continuous reader mode.
*/
static int hfgal_read_card(uint32_t aid, uint8_t *site_key, bool verbose, bool quiet) {
DropField();
clearCommandBuffer();
// Set up context
DesfireContext_t dctx = {0};
DesfireClearContext(&dctx);
// Get card UID (for key diversification)
int res = DesfireGetCardUID(&dctx);
HFGAL_RET_IF_ERR_MAYBE_MSG(res, !quiet, "Failed retrieving card UID");
// Find AIDs to process (from CLI args or the Card Application Directory)
uint8_t cad[36 * 3] = {0};
uint8_t num_entries = 0;
if (aid != 0) {
cad_aid_uint_to_byte(aid, &cad[3]);
num_entries = 1;
} else {
res = hfgal_read_cad(&dctx, cad, ARRAYLEN(cad), &num_entries, verbose);
HFGAL_RET_IF_ERR_MAYBE_MSG(res, !quiet, "Failed reading Card Application Directory");
}
// Loop through each application in the CAD
for (uint8_t i = 0; i < num_entries * 6; i += 6) {
uint16_t region_code = cad[i + 0];
uint16_t facility_code = (cad[i + 1] << 8) + cad[i + 2];
uint32_t current_aid = cad_aid_byte_to_uint(&cad[i + 3]);
if (verbose) {
if (region_code > 0 || facility_code > 0)
PrintAndLogEx(INFO, "Reading AID: %06X, region: %u, facility: %u", current_aid, region_code, facility_code);
else
PrintAndLogEx(INFO, "Reading AID: %06X", current_aid);
}
// Read & decode credentials
GallagherCredentials_t creds = {0};
res = hfgal_read_creds_app(&dctx, current_aid, site_key, &creds, verbose);
HFGAL_RET_IF_ERR_MAYBE_MSG(res, !quiet, "Failed reading card application credentials");
PrintAndLogEx(SUCCESS, "GALLAGHER (AID %06X) - Region: " _GREEN_("%u") ", Facility: " _GREEN_("%u")
", Card No.: " _GREEN_("%u") ", Issue Level: " _GREEN_("%u"), current_aid,
creds.region_code, creds.facility_code, creds.card_number, creds.issue_level);
}
return PM3_SUCCESS;
}
static int CmdGallagherReader(const char *cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf gallagher reader",
"Read a GALLAGHER tag",
"hf gallagher reader --aid 2081f4 --sitekey 00112233445566778899aabbccddeeff"
" -> act as a reader that skips reading the Card Application Directory and uses a non-default site key\n"
"hf gallagher reader -@ -> continuous reader mode"
);
void *argtable[] = {
arg_param_begin,
arg_str0(NULL, "aid", "<hex>", "Application ID to read (3 bytes)"),
arg_str0("k", "sitekey", "<hex>", "Master site key to compute diversified keys (16 bytes) [default=3112B738D8862CCD34302EB299AAB456]"),
arg_lit0(NULL, "apdu", "Show APDU requests and responses"),
arg_lit0("v", "verbose", "Verbose mode"),
arg_lit0("@", "continuous", "Continuous reader mode"),
arg_param_end
};
CLIExecWithReturn(ctx, cmd, argtable, true);
int aid_len = 0;
uint8_t aid_buf[3] = {0};
CLIGetHexWithReturn(ctx, 1, aid_buf, &aid_len);
if (aid_len > 0 && aid_len != 3)
HFGAL_RET_ERR(PM3_EINVARG, "--aid must be 3 bytes");
reverse_aid(aid_buf); // PM3 displays AIDs backwards
uint32_t aid = DesfireAIDByteToUint(aid_buf);
int site_key_len = 0;
uint8_t site_key[16] = {0};
memcpy(site_key, DEFAULT_SITE_KEY, ARRAYLEN(site_key));
CLIGetHexWithReturn(ctx, 2, site_key, &site_key_len);
if (site_key_len > 0 && site_key_len != 16)
HFGAL_RET_ERR(PM3_EINVARG, "--sitekey must be 16 bytes");
SetAPDULogging(arg_get_lit(ctx, 3));
bool verbose = arg_get_lit(ctx, 4);
bool continuous_mode = arg_get_lit(ctx, 5);
CLIParserFree(ctx);
if (!continuous_mode)
// Read single card
return hfgal_read_card(aid, site_key, verbose, false);
// Loop until <Enter> is pressed
PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
while (!kbd_enter_pressed())
hfgal_read_card(aid, site_key, verbose, !verbose);
return PM3_SUCCESS;
}
static int CmdGallagherClone(const char *cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf gallagher clone",