1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // See LICENSE.txt for the text of the license.
15 //-----------------------------------------------------------------------------
16 // High frequency GALLAGHER tag commands.
17 // MIFARE DESFire, AIDs 2081F4-2F81F4
18 //-----------------------------------------------------------------------------
20 #include "cmdhfgallagher.h"
21 #include "generator.h"
23 #include "mifare/desfirecore.h"
24 #include "mifare/gallaghercore.h"
28 #include "commonutil.h"
29 #include "cmdparser.h"
30 #include "cliparser.h"
33 static int CmdHelp(const char *cmd
);
35 // Application ID for the Gallagher Card Application Directory
36 static const uint32_t CAD_AID
= 0x2F81F4;
38 // Default MIFARE site key (16 bytes)
39 static const uint8_t DEFAULT_SITE_KEY
[] = {
40 0x31, 0x12, 0xB7, 0x38, 0xD8, 0x86, 0x2C, 0xCD,
41 0x34, 0x30, 0x2E, 0xB2, 0x99, 0xAA, 0xB4, 0x56,
45 * @brief Create Gallagher Application Master Key by diversifying
46 * the MIFARE Site Key with card UID, key number, and application ID.
48 * @param site_key MIFARE Site Key (16 bytes).
49 * @param uid Card unique ID (4 or 7 bytes).
50 * @param uid_len Length of UID.
51 * @param key_num Key number (0 <= key_num <= 2).
52 * @param aid Application ID (3 bytes, e.g. 0x2081F4).
53 * @param key_output Buffer to copy the diversified key into (must be 16 bytes).
54 * @return PM3_SUCCESS if successful, PM3_EINVARG if an argument is invalid.
56 int hfgal_diversify_key(uint8_t *site_key
, uint8_t *uid
, uint8_t uid_len
,
57 uint8_t key_num
, uint32_t aid
, uint8_t *key_output
) {
58 // Generate diversification input
59 uint8_t kdf_input_len
= 11;
60 int res
= mfdes_kdf_input_gallagher(uid
, uid_len
, key_num
, aid
, key_output
, &kdf_input_len
);
61 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed generating Gallagher key diversification input");
63 uint8_t key
[sizeof(DEFAULT_SITE_KEY
)];
64 if (site_key
== NULL
) {
65 PrintAndLogEx(INFO
, "hfgal_diversify_key is using default site key");
66 memcpy(key
, DEFAULT_SITE_KEY
, sizeof(key
));
68 memcpy(key
, site_key
, sizeof(key
));
71 // Make temporary DesfireContext
72 DesfireContext_t dctx
= {0};
73 DesfireSetKey(&dctx
, 0, T_AES
, key
);
75 // Diversify input & copy to output buffer
76 MifareKdfAn10922(&dctx
, DCOMasterKey
, key_output
, kdf_input_len
);
77 memcpy(key_output
, dctx
.key
, CRYPTO_AES128_KEY_SIZE
);
83 * @brief Reverses the bytes in AID. Used when parsing CLI args
84 * (because Proxmark displays AIDs in reverse byte order).
86 // iceman todo: use commonutil.c fct
87 static void reverse_aid(uint8_t *aid
) {
94 * @brief Converts a Card Application Directory format application ID to an integer.
95 * Note: the CAD stores AIDs in reverse order, so this is different to DesfireAIDByteToUint().
98 // iceman todo: use commonutil.c fct
99 static uint32_t cad_aid_byte_to_uint(const uint8_t *data
) {
100 return data
[2] + (data
[1] << 8) + (data
[0] << 16);
104 * @brief Converts an integer application ID to Card Application Directory format.
105 * Note: the CAD stores AIDs in reverse order, so this is different to DesfireAIDUintToByte().
107 // iceman todo: use commonutil.c fct
108 static void cad_aid_uint_to_byte(uint32_t aid
, uint8_t *data
) {
109 data
[2] = aid
& 0xff;
110 data
[1] = (aid
>> 8) & 0xff;
111 data
[0] = (aid
>> 16) & 0xff;
115 * @brief Returns true if the Card Application Directory entry
116 * is for the specified region & facility, false otherwise.
118 static bool cad_facility_match(const uint8_t *entry
, uint8_t region_code
, uint16_t facility_code
) {
119 return (entry
[0] == region_code
) &&
120 ((entry
[1] << 8) + entry
[2] == facility_code
);
124 * @brief Select application ID.
126 static int select_aid(DesfireContext_t
*ctx
, uint32_t aid
, bool verbose
) {
127 // TODO: do these both need to be set?
128 DesfireSetCommMode(ctx
, DCMPlain
);
129 DesfireSetCommandSet(ctx
, DCCNativeISO
);
131 int res
= DesfireSelectEx(ctx
, true, ISW6bAID
, aid
, NULL
);
132 if (res
!= PM3_SUCCESS
) {
133 PrintAndLogEx(ERR
, "Desfire AID %06X select " _RED_("error"), aid
);
138 PrintAndLogEx(INFO
, "Selected AID %06X", aid
);
145 * @brief Authenticate to application. Uses existing authentication keys in context.
147 static int authenticate(DesfireContext_t
*ctx
, bool verbose
) {
148 // TODO: do these both need to be set?
149 DesfireSetCommMode(ctx
, DCMPlain
);
150 DesfireSetCommandSet(ctx
, DCCNativeISO
);
151 DesfireClearSession(ctx
);
153 int res
= DesfireAuthenticate(ctx
, DACEV1
, false);
154 if (res
!= PM3_SUCCESS
) {
155 PrintAndLogEx(ERR
, "Desfire authenticate " _RED_("error") ". Result: [%d] %s",
157 DesfireAuthErrorToStr(res
)
162 if (DesfireIsAuthenticated(ctx
)) {
164 PrintAndLogEx(INFO
, "Authenticated to AID " _YELLOW_("%06X"), ctx
->selectedAID
);
174 * @brief Select application ID & authenticate.
175 * Uses existing authentication keys in context.
177 static int select_aid_and_auth(DesfireContext_t
*ctx
, uint32_t aid
, bool verbose
) {
178 int res
= select_aid(ctx
, aid
, verbose
);
181 res
= authenticate(ctx
, verbose
);
188 * @brief Select application ID & authenticate with specified AES key.
190 static int select_aid_and_auth_with_key(DesfireContext_t
*ctx
, uint32_t aid
, uint8_t *key
,
191 uint8_t key_num
, bool should_diversify
, bool verbose
) {
192 int res
= select_aid(ctx
, aid
, verbose
);
195 // Set key & diversification algorithm.
196 uint8_t kdf_algo
= should_diversify
? MFDES_KDF_ALGO_GALLAGHER
: MFDES_KDF_ALGO_NONE
;
197 DesfireSetKeyNoClear(ctx
, key_num
, T_AES
, key
);
198 DesfireSetKdf(ctx
, kdf_algo
, NULL
, 0);
200 res
= authenticate(ctx
, verbose
);
207 * @brief Returns true if the specified application exists, false otherwise.
209 static bool aid_exists(DesfireContext_t
*ctx
, uint32_t aid
, bool verbose
) {
210 // TODO: do these both need to be set?
211 DesfireSetCommMode(ctx
, DCMPlain
);
212 DesfireSetCommandSet(ctx
, DCCNativeISO
);
214 int res
= DesfireSelectAIDHex(ctx
, aid
, false, 0);
215 if (res
!= PM3_SUCCESS
&& res
!= PM3_EAPDU_FAIL
) {
216 PM3_RET_ERR(false, "Select failed with error %d, assuming AID %06X does not exist", res
, aid
);
220 PrintAndLogEx(INFO
, "AID %06X %s",
222 res
== PM3_SUCCESS
? "exists" : "does not exist"
226 return (res
== PM3_SUCCESS
);
230 * @brief Returns the lowest available Gallagher application ID.
231 * @return The lowest available AID in the range 0x??81F4, where ?? >= 0x20.
233 static uint32_t find_available_gallagher_aid(DesfireContext_t
*ctx
, bool verbose
) {
235 int res
= select_aid(ctx
, 0x000000, verbose
);
238 // Retrieve the AID list
239 uint8_t aid_buf
[DESFIRE_BUFFER_SIZE
] = {0};
240 size_t aid_buf_len
= 0;
242 res
= DesfireGetAIDList(ctx
, aid_buf
, &aid_buf_len
);
243 if (res
!= PM3_SUCCESS
) {
244 PM3_RET_ERR(0, "Failed retrieving AID list");
249 PrintAndLogEx(INFO
, "Retrieved AID list:" NOLF
);
251 for (int i
= 0; i
< aid_buf_len
; i
+= 3) {
252 PrintAndLogEx(NORMAL
, "%s %06X" NOLF
,
254 DesfireAIDByteToUint(&aid_buf
[i
])
257 PrintAndLogEx(NORMAL
, "");
260 // Find lowest available in range F48120 -> F481FE, excluding the CAD
261 for (uint8_t aid_increment
= 0x20; aid_increment
< 0xFF; aid_increment
++) {
263 uint32_t aid
= 0x0081F4 | (aid_increment
<< 16);
264 if (aid
== CAD_AID
) {
268 // Check if AID exists in aid_buf
270 for (size_t idx
= 0; idx
< aid_buf_len
; idx
+= 3) {
272 if (DesfireAIDByteToUint(&aid_buf
[idx
]) == aid
) {
278 if (found
== false) {
283 // Failed to find an available AID. This is very unlikely to occur as
284 // DESFire cards rarely have more than 1 application on them
285 PM3_RET_ERR(0, "Checked 200+ AIDs and all exist, abandoning search");
289 * @brief Delete the CAD or an application that contains cardholder credentials.
291 * @param site_key MIFARE site key.
292 * @param aid Application ID to remove.
294 static int hfgal_delete_app(DesfireContext_t
*ctx
, uint8_t *site_key
,
295 uint32_t aid
, bool verbose
) {
296 // Select application & authenticate
297 int res
= select_aid_and_auth_with_key(ctx
, aid
, site_key
, 0, true, verbose
);
300 // Delete application
301 DesfireSetCommMode(ctx
, DCMMACed
);
302 res
= DesfireDeleteApplication(ctx
, aid
);
303 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed deleting AID %06X", aid
);
305 PrintAndLogEx(INFO
, "Successfully deleted AID " _YELLOW_("%06X"), aid
);
310 * @brief Read credentials from a single AID.
312 * @param aid Application ID to read.
313 * @param site_key MIFARE site key.
314 * @param creds Decoded credentials will be stored in this structure.
316 static int hfgal_read_creds_app(DesfireContext_t
*ctx
, uint32_t aid
, uint8_t *site_key
,
317 GallagherCredentials_t
*creds
, bool verbose
) {
318 // Check that card UID has been set
319 if (ctx
->uidlen
== 0) {
320 PM3_RET_ERR(PM3_EINVARG
, "Card UID must be set in DesfireContext (required for key div)");
323 // Select application & authenticate
324 int res
= select_aid_and_auth_with_key(ctx
, aid
, site_key
, 0, true, verbose
);
325 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed selecting/authenticating to AID %06X", aid
);
327 // Read file 0 (contains credentials)
328 uint8_t buf
[16] = {0};
330 DesfireSetCommMode(ctx
, DCMEncrypted
);
331 res
= DesfireReadFile(ctx
, 0, 0, 16, buf
, &read_len
);
332 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed reading file 0 in AID %06X", aid
);
334 // Check file contained 16 bytes of data
335 if (read_len
!= 16) {
336 PM3_RET_ERR(PM3_EFAILED
, "Failed reading file 0 in AID %06X, expected 16 bytes, got %zu bytes",
342 // Check second half of file is the bitwise inverse of the first half
343 for (uint8_t i
= 8; i
< 16; i
++) {
347 if (memcmp(buf
, &buf
[8], 8) != 0) {
348 PM3_RET_ERR(PM3_EFAILED
, "Invalid cardholder data in file 0 in AID %06X. Received %s",
350 sprint_hex_inrow(buf
, 16)
354 gallagher_decode_creds(buf
, creds
);
356 // TODO: read MIFARE Enhanced Security file
357 // https://github.com/megabug/gallagher-research/blob/master/formats/mes.md
363 * @brief Create a new application to store Gallagher cardholder credentials.
365 * @param site_key MIFARE site key.
366 * @param aid New application ID. 3 bytes, e.g. 0x2081F4.
368 static int hfgal_create_creds_app(DesfireContext_t
*ctx
, uint8_t *site_key
, uint32_t aid
, bool verbose
) {
369 // Select application & authenticate
370 int res
= select_aid_and_auth(ctx
, 0x000000, verbose
);
373 // UID is required for key diversification
374 if (ctx
->uidlen
== 0) {
375 PM3_RET_ERR(PM3_EINVARG
, "UID is required for key diversification. "
376 "Please fetch it before calling `hfgal_create_creds_app`");
379 // Create application
380 DesfireCryptoAlgorithm app_algo
= T_AES
;
381 uint8_t num_keys
= 3;
383 uint8_t ks2
= (DesfireKeyAlgoToType(app_algo
) << 6) | num_keys
;
385 uint8_t data
[5] = {0};
386 DesfireAIDUintToByte(aid
, &data
[0]);
390 DesfireSetCommMode(ctx
, DCMMACed
);
391 res
= DesfireCreateApplication(ctx
, data
, ARRAYLEN(data
));
392 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed creating application %06X. Does it already exist?", aid
);
395 PrintAndLogEx(INFO
, "Created application " _YELLOW_("%06X") " (empty contents & blank keys)", aid
);
398 // Select the new application
399 res
= select_aid(ctx
, aid
, verbose
);
400 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed selecting application %06X", aid
);
402 // Add key 2, then key 0 (we must authenticate with key 0 in order to make changes)
403 for (int i
= 2; i
>= 0; i
-= 2) {
405 uint8_t buf
[CRYPTO_AES128_KEY_SIZE
] = {0};
406 res
= hfgal_diversify_key(site_key
, ctx
->uid
, ctx
->uidlen
, i
, aid
, buf
);
407 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed diversifying key %d for AID %06X", i
, aid
);
409 PrintAndLogEx(INFO
, "Diversified key %d for AID %06X: " _GREEN_("%s"),
412 sprint_hex_inrow(buf
, ARRAYLEN(buf
))
416 uint8_t blank_key
[CRYPTO_AES128_KEY_SIZE
] = {0};
417 DesfireSetKeyNoClear(ctx
, 0, T_AES
, blank_key
);
418 DesfireSetKdf(ctx
, MFDES_KDF_ALGO_NONE
, NULL
, 0);
419 res
= authenticate(ctx
, verbose
);
420 PM3_RET_IF_ERR_WITH_MSG(res
, "Desfire authenticate error. Result: [%d] %s", res
, DesfireAuthErrorToStr(res
));
423 DesfireSetCommMode(ctx
, DCMEncryptedPlain
);
424 res
= DesfireChangeKey(ctx
, false, i
, app_algo
, 1, buf
, app_algo
, blank_key
, verbose
);
425 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed setting key %d for AID %06X", i
, aid
);
428 PrintAndLogEx(INFO
, "Successfully set key " _YELLOW_("%d") " for AID " _YELLOW_("%06X"), i
, aid
);
432 PrintAndLogEx(INFO
, "Successfully created credentials application " _YELLOW_("%06X"), aid
);
437 * @brief Create a new file containing Gallagher cardholder credentials.
439 * @param site_key MIFARE site key.
440 * @param aid Application ID to put the new file in.
441 * @param creds Gallagher cardholder credentials.
443 static int hfgal_create_creds_file(DesfireContext_t
*ctx
, uint8_t *site_key
, uint32_t aid
,
444 GallagherCredentials_t
*creds
, bool verbose
) {
445 // Select application & authenticate
446 int res
= select_aid_and_auth_with_key(ctx
, aid
, site_key
, 0, true, verbose
);
449 // Prepare create file command
450 uint8_t file_type
= 0; // standard data file
451 uint8_t file_id
= 0x00;
452 uint8_t file_size
= 16;
453 uint8_t file_access_mode
= 0x03; // encrypted
454 uint32_t file_rights
= 0x2000; // key 0 has God mode, key 2 can read
456 uint8_t data
[7] = {0};
458 data
[1] = file_access_mode
;
459 data
[2] = file_rights
& 0xff;
460 data
[3] = (file_rights
>> 8) & 0xff;
461 Uint3byteToMemLe(&data
[4], file_size
);
464 res
= DesfireCreateFile(ctx
, file_type
, data
, ARRAYLEN(data
), false);
465 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed creating file 0 in AID %06X", aid
);
468 PrintAndLogEx(INFO
, "Created file 0 in AID " _YELLOW_("%06X") " (empty contents)", aid
);
471 // Create file contents (2nd half is the bitwise inverse of the encoded creds)
472 uint8_t contents
[16] = {0};
473 gallagher_encode_creds(contents
, creds
);
474 for (int i
= 0; i
< 8; i
++) {
475 contents
[i
+ 8] = contents
[i
] ^ 0xFF;
479 DesfireSetCommMode(ctx
, DCMEncrypted
);
480 res
= DesfireWriteFile(ctx
, file_id
, 0, ARRAYLEN(contents
), contents
);
481 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed writing data to file 0 in AID %06X", aid
);
483 PrintAndLogEx(INFO
, "Successfully wrote cardholder credentials to file " _YELLOW_("0") " in AID " _YELLOW_("%06X"), aid
);
488 * @brief Read Gallagher Card Application Directory (CAD) from card.
490 * @param dest_buf Buffer to copy Card Application Directory into.
491 * @param dest_buf_len Size of dest_buf. Must be at least 108 bytes.
492 * @param num_entries Will be set to the number of entries in the CAD.
494 static int hfgal_read_cad(DesfireContext_t
*ctx
, uint8_t *dest_buf
,
495 uint8_t dest_buf_len
, uint8_t *num_entries_out
, bool verbose
) {
496 if (dest_buf_len
< 3 * 36) {
497 PrintAndLogEx(ERR
, "hfgal_read_cad destination buffer is incorrectly sized. Received len %d, must be at least %d",
504 // Get card AIDs from Card Application Directory (which contains 1 to 3 files)
505 int res
= select_aid(ctx
, CAD_AID
, verbose
);
506 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed selecting Card Application Directory, does AID %06X exist?", CAD_AID
);
508 // Read up to 3 files with 6x 6-byte entries each
509 for (uint8_t i
= 0; i
< 3; i
++) {
511 res
= DesfireReadFile(ctx
, i
, 0, 36, &dest_buf
[i
* 36], &read_len
);
512 if (res
!= PM3_SUCCESS
&& res
!= PM3_EAPDU_FAIL
) {
513 PM3_RET_ERR(res
, "Failed reading file %d in Card Application Directory (AID %06X)", i
, CAD_AID
);
516 // end if the last entry is NULL
517 if (memcmp(&dest_buf
[36 * i
+ 30], "\0\0\0\0\0\0", 6) == 0) {
522 // Count number of entries (i.e. count until we hit a NULL entry)
523 uint8_t num_entries
= 0;
524 for (uint8_t i
= 0; i
< dest_buf_len
; i
+= 6) {
525 if (memcmp(&dest_buf
[i
], "\0\0\0\0\0\0", 6) == 0) {
530 *num_entries_out
= num_entries
;
532 if (num_entries
== 0) {
533 PrintAndLogEx(WARNING
, "Card Application Directory is empty");
534 } else if (verbose
) {
535 // Print what we found
536 // iceman maybe on seperate lines for easier reading.
537 PrintAndLogEx(SUCCESS
, "Card Application Directory contains:" NOLF
);
538 for (int i
= 0; i
< num_entries
; i
++) {
539 PrintAndLogEx(NORMAL
, "%s %06X" NOLF
,
541 cad_aid_byte_to_uint(&dest_buf
[i
* 6 + 3])
544 PrintAndLogEx(NORMAL
, "");
551 * @brief Create the Gallagher Card Application Directory.
553 * @param key MIFARE site key, or custom CAD key.
554 * @param should_diversify True if using a site_key, false if using a custom CAD key.
556 static int hfgal_create_cad(DesfireContext_t
*ctx
, uint8_t *key
,
557 bool should_diversify
, bool verbose
) {
558 // Check that card UID has been set
559 if (ctx
->uidlen
== 0) {
560 PM3_RET_ERR(PM3_EINVARG
, "Card UID must be set in DesfireContext (required for key div)");
563 // Select application & authenticate
564 int res
= select_aid_and_auth(ctx
, 0x000000, verbose
);
567 // Create application
568 DesfireCryptoAlgorithm app_algo
= T_AES
;
569 uint8_t num_keys
= 1;
571 uint8_t ks2
= (DesfireKeyAlgoToType(app_algo
) << 6) | num_keys
;;
573 uint8_t data
[5] = {0};
574 DesfireAIDUintToByte(CAD_AID
, &data
[0]);
578 DesfireSetCommMode(ctx
, DCMMACed
);
579 res
= DesfireCreateApplication(ctx
, data
, ARRAYLEN(data
));
580 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed creating Card Application Directory (AID " _YELLOW_("%06X")"). Does it already exist?", CAD_AID
);
583 PrintAndLogEx(INFO
, "Created Card Application Directory (AID " _YELLOW_("%06X") ", empty contents & blank keys)",
588 // Select application & authenticate
589 uint8_t blank_key
[DESFIRE_MAX_KEY_SIZE
] = {0};
590 res
= select_aid_and_auth_with_key(ctx
, CAD_AID
, blank_key
, 0, false, verbose
);
593 uint8_t buf
[CRYPTO_AES128_KEY_SIZE
] = {0};
594 if (should_diversify
) {
596 res
= hfgal_diversify_key(key
, ctx
->uid
, ctx
->uidlen
, 0, CAD_AID
, buf
);
597 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed diversifying key 0 for AID %06X", CAD_AID
);
599 PrintAndLogEx(INFO
, "Diversified key " _YELLOW_("0") " for CAD (AID " _YELLOW_("%06X") "): " _GREEN_("%s"),
601 sprint_hex_inrow(buf
, ARRAYLEN(buf
))
604 } else if (verbose
) {
605 PrintAndLogEx(INFO
, "Using provided key " _YELLOW_("0") " for CAD (AID " _YELLOW_("%06X") "): " _GREEN_("%s"),
607 sprint_hex_inrow(key
, CRYPTO_AES128_KEY_SIZE
)
612 DesfireSetCommMode(ctx
, DCMEncryptedPlain
);
613 res
= DesfireChangeKey(ctx
, false, 0, app_algo
, 1, buf
, app_algo
, blank_key
, verbose
);
614 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed setting key 0 for CAD");
617 PrintAndLogEx(INFO
, "Successfully set key " _YELLOW_("0") " for CAD");
620 PrintAndLogEx(INFO
, "Successfully created Card Application Directory (AID " _YELLOW_("%06X") ")", CAD_AID
);
625 * @brief Update the Gallagher Card Application Directory with a new entry.
627 * @param key MIFARE site key, or custom CAD key.
628 * @param should_diversify True if using a site_key, false if using a custom CAD key.
629 * @param aid Application ID to add to the CAD.
630 * @param creds Gallagher cardholder credentials (region_code & facility_code are required).
632 static int hfgal_add_aid_to_cad(DesfireContext_t
*ctx
, uint8_t *key
, bool should_diversify
,
633 uint32_t aid
, GallagherCredentials_t
*creds
, bool verbose
) {
634 // Check if CAD exists
635 uint8_t cad
[36 * 3] = {0};
636 uint8_t num_entries
= 0;
637 if (aid_exists(ctx
, CAD_AID
, false)) {
639 PrintAndLogEx(INFO
, "Card Application Directory exists, reading entries...");
642 int res
= hfgal_read_cad(ctx
, cad
, ARRAYLEN(cad
), &num_entries
, verbose
);
645 // Check that there is space for the new entry
646 if (num_entries
>= 18) {
647 PM3_RET_ERR(PM3_EFATAL
, "Card application directory is full");
651 // CAD doesn't exist, we need to create it
653 PrintAndLogEx(INFO
, "Card Application Directory does not exist, creating it now...");
656 int res
= hfgal_create_cad(ctx
, key
, should_diversify
, verbose
);
660 // 6 entries per file
661 uint8_t file_id
= num_entries
/ 6;
662 uint8_t entry_num
= num_entries
% 6;
664 // Check if facility already exists in CAD.
665 for (uint8_t i
= 0; i
< ARRAYLEN(cad
); i
+= 6) {
666 if (cad_facility_match(&cad
[i
], creds
->region_code
, creds
->facility_code
)) {
667 PM3_RET_ERR(PM3_EFATAL
, "Facility already exists in CAD, delete or update AID %06X",
668 cad_aid_byte_to_uint(&cad
[i
+ 3])
674 uint8_t *entry
= &cad
[num_entries
* 6];
675 entry
[0] = creds
->region_code
;
676 entry
[1] = (creds
->facility_code
>> 8) & 0xFF;
677 entry
[2] = creds
->facility_code
& 0xFF;
678 cad_aid_uint_to_byte(aid
, &entry
[3]);
681 PrintAndLogEx(INFO
, "Adding entry to CAD (position " _YELLOW_("%d") " in file " _YELLOW_("%d") "): %s",
684 sprint_hex_inrow(entry
, 6)
688 // Select application & authenticate
689 int res
= select_aid_and_auth_with_key(ctx
, CAD_AID
, key
, 0, should_diversify
, verbose
);
692 // Create file if necessary
693 if (entry_num
== 0) {
695 PrintAndLogEx(INFO
, "Creating new file in CAD");
698 // Prepare create file command
699 uint8_t file_type
= 0; // standard data file
700 uint8_t file_size
= 36;
701 uint8_t file_access_mode
= 0x00; // plain
702 uint32_t file_rights
= 0xE000; // key 0 has God mode, everyone can read
704 uint8_t data
[7] = {0};
706 data
[1] = file_access_mode
;
707 data
[2] = file_rights
& 0xff;
708 data
[3] = (file_rights
>> 8) & 0xff;
709 Uint3byteToMemLe(&data
[4], file_size
);
712 res
= DesfireCreateFile(ctx
, file_type
, data
, ARRAYLEN(data
), false);
713 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed creating file %d in CAD (AID %06X)", file_id
, CAD_AID
);
716 PrintAndLogEx(INFO
, "Created file " _YELLOW_("%d") " in CAD (empty contents)", file_id
);
720 res
= DesfireWriteFile(ctx
, file_id
, 0, 36, &cad
[file_id
* 36]);
723 res
= DesfireWriteFile(ctx
, file_id
, entry_num
* 6, 6, entry
);
725 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed writing data to file %d in CAD AID %06X)", file_id
, CAD_AID
);
727 PrintAndLogEx(INFO
, "Successfully added new entry for " _YELLOW_("%06X") " to the Card Application Directory", aid
);
732 * @brief Remove an entry from the Gallagher Card Application Directory.
734 * @param key MIFARE site key, or custom CAD key.
735 * @param should_diversify True if using a site_key, false if using a custom CAD key.
736 * @param aid Application ID to remove from the CAD.
738 static int hfgal_remove_aid_from_cad(DesfireContext_t
*ctx
, uint8_t *key
,
739 bool should_diversify
, uint32_t aid
, bool verbose
) {
741 uint8_t cad
[36 * 3] = {0};
742 uint8_t num_entries
= 0;
743 int res
= hfgal_read_cad(ctx
, cad
, ARRAYLEN(cad
), &num_entries
, verbose
);
746 // Check if facility already exists in CAD
748 for (entry_idx
= 0; entry_idx
< num_entries
; entry_idx
++) {
749 if (aid
> 0 && aid
== cad_aid_byte_to_uint(&cad
[entry_idx
* 6 + 3])) {
753 if (entry_idx
>= num_entries
) {
754 PM3_RET_ERR(PM3_EINVARG
, "Specified facility or AID does not exist in the Card Application Directory");
757 // Remove entry (shift all entries left, then clear the last entry)
760 &cad
[(entry_idx
+ 1) * 6],
761 ARRAYLEN(cad
) - (entry_idx
+ 1) * 6
763 memset(&cad
[ARRAYLEN(cad
) - 6], 0, 6);
765 // Select application & authenticate
766 res
= select_aid_and_auth_with_key(ctx
, CAD_AID
, key
, 0, should_diversify
, verbose
);
769 // Determine what files we need to update
770 uint8_t file_id_start
= entry_idx
/ 6;
771 uint8_t file_id_stop
= (num_entries
- 1) / 6;
772 bool delete_last_file
= (num_entries
- 1) % 6 == 0;
774 for (uint8_t file_id
= file_id_start
; file_id
<= file_id_stop
- delete_last_file
; file_id
++) {
776 res
= DesfireWriteFile(ctx
, file_id
, 0, 36, &cad
[file_id
* 36]);
777 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed writing data to file %d in CAD (AID %06X)", file_id
, CAD_AID
);
780 PrintAndLogEx(INFO
, "Updated file " _YELLOW_("%d") " in CAD", file_id
);
784 // Delete empty file if necessary
785 if (delete_last_file
) {
786 uint8_t file_id
= file_id_stop
;
788 DesfireSetCommMode(ctx
, DCMMACed
);
789 res
= DesfireDeleteFile(ctx
, file_id
);
790 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed deleting file %d from CAD (AID %06X)", file_id
, CAD_AID
);
793 PrintAndLogEx(INFO
, "Deleted unnecessary file " _YELLOW_("%d") " from CAD (AID " _YELLOW_("%06X")")",
800 PrintAndLogEx(INFO
, "Successfully removed " _YELLOW_("%06X") " from the Card Application Directory", aid
);
805 * @brief Read credentials from a Gallagher card.
807 * @param aid Application ID to read. If 0, then the Card Application Directory
808 * will be queried and all entries will be read.
809 * @param site_key MIFARE site key.
810 * @param quiet Suppress error messages. Used when in continuous reader mode.
812 // iceman, verbose and quiet... one should be enough.
813 static int hfgal_read_card(uint32_t aid
, uint8_t *site_key
, bool verbose
, bool quiet
) {
815 clearCommandBuffer();
818 DesfireContext_t dctx
= {0};
819 DesfireClearContext(&dctx
);
821 // Get card UID (for key diversification)
822 int res
= DesfireGetCardUID(&dctx
);
823 PM3_RET_IF_ERR_MAYBE_MSG(res
, !quiet
, "Failed retrieving card UID");
825 // Find AIDs to process (from CLI args or the Card Application Directory)
826 uint8_t cad
[36 * 3] = {0};
827 uint8_t num_entries
= 0;
829 cad_aid_uint_to_byte(aid
, &cad
[3]);
832 res
= hfgal_read_cad(&dctx
, cad
, ARRAYLEN(cad
), &num_entries
, verbose
);
833 PM3_RET_IF_ERR_MAYBE_MSG(res
, !quiet
, "Failed reading Card Application Directory");
836 // Loop through each application in the CAD
837 for (uint16_t i
= 0; i
< num_entries
* 6; i
+= 6) {
838 uint16_t region_code
= cad
[i
+ 0];
839 uint16_t facility_code
= (cad
[i
+ 1] << 8) + cad
[i
+ 2];
840 uint32_t current_aid
= cad_aid_byte_to_uint(&cad
[i
+ 3]);
843 if (region_code
> 0 || facility_code
> 0 || current_aid
> 0) {
844 PrintAndLogEx(INFO
, "Reading AID: " _YELLOW_("%06X") ", region: " _YELLOW_("%c") " ( " _YELLOW_("%u") " ), facility: " _YELLOW_("%u"),
851 PrintAndLogEx(INFO
, "Reading AID: " _YELLOW_("%06X"), current_aid
);
855 // Read & decode credentials
856 GallagherCredentials_t creds
= {0};
857 res
= hfgal_read_creds_app(&dctx
, current_aid
, site_key
, &creds
, verbose
);
858 if (res
== HFGAL_AUTH_FAIL
) {
859 PrintAndLogEx(WARNING
, "Invalid site key for AID %06X", current_aid
);
862 PM3_RET_IF_ERR_MAYBE_MSG(res
, !quiet
, "Failed reading card application credentials");
864 PrintAndLogEx(SUCCESS
, "Gallagher (AID %06X) - region: " _GREEN_("%c") " ( " _GREEN_("%u") " )"
865 ", facility: " _GREEN_("%u")
866 ", card number: " _GREEN_("%u")
867 ", issue level: " _GREEN_("%u"),
869 'A' + creds
.region_code
,
879 static int CmdGallagherReader(const char *cmd
) {
880 CLIParserContext
*ctx
;
881 CLIParserInit(&ctx
, "hf gallagher reader",
882 "Read a Gallagher DESFire tag from the Card Application Directory, CAD\n"
883 "Specify site key is required if using non-default key\n",
884 "hf gallagher reader -@ -> continuous reader mode\n"
885 "hf gallagher reader --aid 2081f4 --sitekey 00112233445566778899aabbccddeeff -> skip CAD\n"
890 arg_str0(NULL
, "aid", "<hex>", "Application ID to read (3 bytes). If specified, the CAD is not used"),
891 arg_str0(NULL
, "sitekey", "<hex>", "Site key to compute diversified keys (16 bytes)"),
892 arg_lit0("@", "continuous", "Continuous reader mode"),
893 arg_lit0(NULL
, "apdu", "Show APDU requests and responses"),
894 arg_lit0("v", "verbose", "Verbose output"),
897 CLIExecWithReturn(ctx
, cmd
, argtable
, true);
900 uint8_t aid_buf
[3] = {0};
901 CLIGetHexWithReturn(ctx
, 1, aid_buf
, &aid_len
);
902 if (aid_len
> 0 && aid_len
!= 3) {
903 PM3_RET_ERR_FREE(PM3_EINVARG
, "--aid must be 3 bytes");
906 reverse_aid(aid_buf
); // PM3 displays AIDs backwards
907 uint32_t aid
= DesfireAIDByteToUint(aid_buf
);
909 int site_key_len
= 0;
910 uint8_t site_key
[16] = {0};
911 memcpy(site_key
, DEFAULT_SITE_KEY
, ARRAYLEN(site_key
));
912 CLIGetHexWithReturn(ctx
, 2, site_key
, &site_key_len
);
913 if (site_key_len
> 0 && site_key_len
!= 16) {
914 PM3_RET_ERR_FREE(PM3_EINVARG
, "--sitekey must be 16 bytes");
917 bool continuous_mode
= arg_get_lit(ctx
, 3);
918 SetAPDULogging(arg_get_lit(ctx
, 4));
919 bool verbose
= arg_get_lit(ctx
, 5);
922 if (continuous_mode
== false) {
924 return hfgal_read_card(aid
, site_key
, verbose
, false);
927 // Loop until <Enter> is pressed
928 PrintAndLogEx(INFO
, "Press " _GREEN_("<Enter>") " to exit");
929 while (kbd_enter_pressed() == false) {
930 hfgal_read_card(aid
, site_key
, verbose
, !verbose
);
935 static int CmdGallagherClone(const char *cmd
) {
936 CLIParserContext
*ctx
;
937 CLIParserInit(&ctx
, "hf gallagher clone",
938 "Clone Gallagher credentials to a writable DESFire card\n"
939 "Specify site key is required if using non-default key\n"
940 "Key, lengths for the different crypto: \n"
942 " 2TDEA or AES 16 bytes\n"
944 "AID, default finds lowest available in range 0x??81F4, where ?? >= 0x20.",
945 "hf gallagher clone --rc 1 --fc 22 --cn 3333 --il 4 --sitekey 00112233445566778899aabbccddeeff"
950 arg_int0("n", "keynum", "<dec>", "PICC key number [default = 0]"),
951 arg_str0("t", "algo", "<DES|2TDEA|3TDEA|AES>", "PICC crypt algo: DES, 2TDEA, 3TDEA, AES"),
952 arg_str0("k", "key", "<hex>", "Key for authentication to the PICC to create applications"),
953 arg_u64_1(NULL
, "rc", "<dec>", "Region code. 4 bits max"),
954 arg_u64_1(NULL
, "fc", "<dec>", "Facility code. 2 bytes max"),
955 arg_u64_1(NULL
, "cn", "<dec>", "Card number. 3 bytes max"),
956 arg_u64_1(NULL
, "il", "<dec>", "Issue level. 4 bits max"),
957 arg_str0(NULL
, "aid", "<hex>", "Application ID to write (3 bytes) [default automatically chooses]"),
958 arg_str0(NULL
, "sitekey", "<hex>", "Site key to compute diversified keys (16 bytes)"),
959 arg_str0(NULL
, "cadkey", "<hex>", "Custom AES key 0 to modify the Card Application Directory (16 bytes)"),
960 arg_lit0(NULL
, "nocadupdate", "Don't modify the Card Application Directory (only creates the app)"),
961 arg_lit0(NULL
, "noappcreate", "Don't create the application (only modifies the CAD)"),
962 arg_lit0(NULL
, "apdu", "Show APDU requests and responses"),
963 arg_lit0("v", "verbose", "Verbose output"),
966 CLIExecWithReturn(ctx
, cmd
, argtable
, false);
969 int picc_key_num
= arg_get_int_def(ctx
, arg
++, 0);
971 int picc_key_algo
= T_DES
;
972 if (CLIGetOptionList(arg_get_str(ctx
, arg
++), DesfireAlgoOpts
, &picc_key_algo
)) {
977 int picc_key_len
= 0;
978 uint8_t picc_key
[DESFIRE_MAX_KEY_SIZE
] = {0};
979 CLIGetHexWithReturn(ctx
, arg
++, picc_key
, &picc_key_len
);
980 if (picc_key_len
&& picc_key_len
!= desfire_get_key_length(picc_key_algo
)) {
981 PM3_RET_ERR_FREE(PM3_EINVARG
, "%s key must have %d bytes length instead of %d",
982 CLIGetOptionListStr(DesfireAlgoOpts
, picc_key_algo
),
983 desfire_get_key_length(picc_key_algo
),
987 if (picc_key_len
== 0) {
988 // Default to a key of all zeros
989 picc_key_len
= desfire_get_key_length(picc_key_algo
);
992 uint64_t region_code
= arg_get_u64(ctx
, arg
++); // uint4, input will be validated later
993 uint64_t facility_code
= arg_get_u64(ctx
, arg
++); // uint16
994 uint64_t card_number
= arg_get_u64(ctx
, arg
++); // uint24
995 uint64_t issue_level
= arg_get_u64(ctx
, arg
++); // uint4
998 uint8_t aid_buf
[3] = {0};
999 CLIGetHexWithReturn(ctx
, arg
++, aid_buf
, &aid_len
);
1000 if (aid_len
> 0 && aid_len
!= 3) {
1001 PM3_RET_ERR_FREE(PM3_EINVARG
, "--aid must be 3 bytes");
1003 reverse_aid(aid_buf
); // PM3 displays AIDs backwards
1004 uint32_t aid
= DesfireAIDByteToUint(aid_buf
);
1006 int site_key_len
= 0;
1007 uint8_t site_key
[16] = {0};
1008 memcpy(site_key
, DEFAULT_SITE_KEY
, ARRAYLEN(site_key
));
1009 CLIGetHexWithReturn(ctx
, arg
++, site_key
, &site_key_len
);
1010 if (site_key_len
> 0 && site_key_len
!= 16) {
1011 PM3_RET_ERR_FREE(PM3_EINVARG
, "--sitekey must be 16 bytes");
1014 int cad_key_len
= 0;
1015 uint8_t cad_key
[16] = {0};
1016 CLIGetHexWithReturn(ctx
, arg
++, cad_key
, &cad_key_len
);
1017 if (cad_key_len
> 0 && cad_key_len
!= 16) {
1018 PM3_RET_ERR_FREE(PM3_EINVARG
, "--cadkey must be 16 bytes");
1021 bool no_cad_update
= arg_get_lit(ctx
, arg
++);
1022 bool no_app_create
= arg_get_lit(ctx
, arg
++);
1024 SetAPDULogging(arg_get_lit(ctx
, arg
++));
1025 bool verbose
= arg_get_lit(ctx
, arg
++);
1028 if (gallagher_is_valid_creds(region_code
, facility_code
, card_number
, issue_level
) == false) {
1032 GallagherCredentials_t creds
= {
1033 .region_code
= region_code
,
1034 .facility_code
= facility_code
,
1035 .card_number
= card_number
,
1036 .issue_level
= issue_level
,
1041 DesfireContext_t dctx
= {0};
1042 DesfireClearContext(&dctx
);
1044 // Get card UID (for key diversification)
1045 int res
= DesfireGetCardUID(&dctx
);
1046 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed retrieving card UID");
1049 // Find available Gallagher AID if the user did not specify one
1050 aid
= find_available_gallagher_aid(&dctx
, verbose
);
1052 PM3_RET_ERR(PM3_EFATAL
, "Could not find an available AID, please specify with --aid");
1055 PrintAndLogEx(INFO
, "Using available AID: %06X", aid
);
1057 } else if (no_app_create
== false && aid_exists(&dctx
, aid
, verbose
)) {
1058 // AID was specified but is not available
1059 PM3_RET_ERR(PM3_EINVARG
, "AID already exists: %06X", aid
);
1062 // Update Card Application Directory
1063 if (no_cad_update
== false) {
1064 // Set keys so that hfgal_add_aid_to_cad can auth to 0x000000
1065 // if it needs to create the CAD application.
1066 DesfireSetKeyNoClear(&dctx
, picc_key_num
, picc_key_algo
, picc_key
);
1067 DesfireSetKdf(&dctx
, MFDES_KDF_ALGO_NONE
, NULL
, 0);
1069 bool should_diversify
= cad_key_len
== 0;
1070 uint8_t *key
= should_diversify
? site_key
: cad_key
;
1071 res
= hfgal_add_aid_to_cad(&dctx
, key
, should_diversify
, aid
, &creds
, verbose
);
1072 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed updating Gallagher Card Application Directory");
1075 // Create application
1076 if (no_app_create
== false) {
1077 // Set keys so that hfgal_create_creds_app can auth to 0x000000
1078 // when it creates the application.
1079 DesfireSetKeyNoClear(&dctx
, picc_key_num
, picc_key_algo
, picc_key
);
1080 DesfireSetKdf(&dctx
, MFDES_KDF_ALGO_NONE
, NULL
, 0);
1082 res
= hfgal_create_creds_app(&dctx
, site_key
, aid
, verbose
);
1083 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed creating Gallagher application");
1085 // Create credential files
1086 // Don't need to set keys here, they're generated automatically
1087 res
= hfgal_create_creds_file(&dctx
, site_key
, aid
, &creds
, verbose
);
1088 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed creating Gallagher credential file");
1091 PrintAndLogEx(SUCCESS
, "Done!");
1092 PrintAndLogEx(HINT
, "Hint: try " _YELLOW_("`hf gallagher reader`") " to verify");
1096 static int CmdGallagherDelete(const char *cmd
) {
1097 CLIParserContext
*ctx
;
1098 CLIParserInit(&ctx
, "hf gallagher delete",
1099 "Delete Gallagher application from a DESFire card\n"
1100 "Specify site key is required if using non-default key",
1101 "hf gallagher delete --aid 2081f4 --sitekey 00112233445566778899aabbccddeeff"
1104 void *argtable
[] = {
1106 arg_str1(NULL
, "aid", "<hex>", "Application ID to delete (3 bytes)"),
1107 arg_str0(NULL
, "sitekey", "<hex>", "Site key to compute diversified keys (16 bytes)"),
1108 arg_str0(NULL
, "cadkey", "<hex>", "Custom AES key 0 to modify the Card Application Directory (16 bytes)"),
1109 arg_lit0(NULL
, "nocadupdate", "Don't modify the Card Application Directory (only deletes the app)"),
1110 arg_lit0(NULL
, "noappdelete", "Don't delete the application (only modifies the CAD)"),
1111 arg_lit0(NULL
, "apdu", "Show APDU requests and responses"),
1112 arg_lit0("v", "verbose", "Verbose output"),
1115 CLIExecWithReturn(ctx
, cmd
, argtable
, false);
1119 uint8_t aid_buf
[3] = {0};
1120 CLIGetHexWithReturn(ctx
, arg
++, aid_buf
, &aid_len
);
1122 PM3_RET_ERR_FREE(PM3_EINVARG
, "--aid must be 3 bytes");
1124 reverse_aid(aid_buf
); // PM3 displays AIDs backwards
1125 uint32_t aid
= DesfireAIDByteToUint(aid_buf
);
1127 int site_key_len
= 0;
1128 uint8_t site_key
[16] = {0};
1129 memcpy(site_key
, DEFAULT_SITE_KEY
, ARRAYLEN(site_key
));
1130 CLIGetHexWithReturn(ctx
, arg
++, site_key
, &site_key_len
);
1131 if (site_key_len
> 0 && site_key_len
!= 16) {
1132 PM3_RET_ERR_FREE(PM3_EINVARG
, "--sitekey must be 16 bytes");
1135 int cad_key_len
= 0;
1136 uint8_t cad_key
[16] = {0};
1137 CLIGetHexWithReturn(ctx
, arg
++, cad_key
, &cad_key_len
);
1138 if (cad_key_len
> 0 && cad_key_len
!= 16) {
1139 PM3_RET_ERR_FREE(PM3_EINVARG
, "--cadkey must be 16 bytes");
1142 bool no_cad_update
= arg_get_lit(ctx
, arg
++);
1143 bool no_app_delete
= arg_get_lit(ctx
, arg
++);
1145 SetAPDULogging(arg_get_lit(ctx
, arg
++));
1146 bool verbose
= arg_get_lit(ctx
, arg
++);
1151 DesfireContext_t dctx
= {0};
1152 DesfireClearContext(&dctx
);
1154 // Get card UID (for key diversification)
1155 int res
= DesfireGetCardUID(&dctx
);
1156 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed retrieving card UID");
1158 // Update Card Application Directory
1159 if (no_cad_update
== false) {
1160 bool should_diversify
= cad_key_len
== 0;
1161 uint8_t *key
= should_diversify
? site_key
: cad_key
;
1162 res
= hfgal_remove_aid_from_cad(&dctx
, key
, should_diversify
, aid
, verbose
);
1163 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed removing %06X from the Card Application Directory", aid
);
1166 // Delete application
1167 if (no_app_delete
== false) {
1168 res
= hfgal_delete_app(&dctx
, site_key
, aid
, verbose
);
1169 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed deleting Gallagher application");
1172 PrintAndLogEx(SUCCESS
, "Done!");
1173 PrintAndLogEx(HINT
, "Hint: try " _YELLOW_("`hf gallagher reader`") " to verify");
1177 static int CmdGallagherDiversify(const char *cmd
) {
1178 CLIParserContext
*ctx
;
1179 CLIParserInit(&ctx
, "hf gallagher diversify",
1180 "Diversify Gallagher key\n"
1181 "Specify site key is required if using non-default key",
1182 "hf gallagher diversify --uid 11223344556677 --aid 2081f4"
1185 void *argtable
[] = {
1187 arg_str1(NULL
, "aid", "<hex>", "Application ID for diversification (3 bytes)"),
1188 arg_int0(NULL
, "keynum", "<dec>", "Key number [default = 0]"),
1189 arg_str0(NULL
, "uid", "<hex>", "Card UID to delete (4 or 7 bytes)"),
1190 arg_str0(NULL
, "sitekey", "<hex>", "Site key to compute diversified keys (16 bytes)"),
1191 arg_lit0(NULL
, "apdu", "Show APDU requests and responses"),
1194 CLIExecWithReturn(ctx
, cmd
, argtable
, false);
1197 uint8_t aid_buf
[3] = {0};
1198 CLIGetHexWithReturn(ctx
, 1, aid_buf
, &aid_len
);
1200 PM3_RET_ERR_FREE(PM3_EINVARG
, "--aid must be 3 bytes");
1202 reverse_aid(aid_buf
); // PM3 displays AIDs backwards
1203 uint32_t aid
= DesfireAIDByteToUint(aid_buf
);
1205 int key_num
= arg_get_int_def(ctx
, 2, 0);
1208 uint8_t uid
[7] = {0};
1209 CLIGetHexWithReturn(ctx
, 3, uid
, &uid_len
);
1210 if (uid_len
> 0 && uid_len
!= 4 && uid_len
!= 7) {
1211 PM3_RET_ERR_FREE(PM3_EINVARG
, "--uid must be 4 or 7 bytes");
1214 int site_key_len
= 0;
1215 uint8_t site_key
[16] = {0};
1216 memcpy(site_key
, DEFAULT_SITE_KEY
, ARRAYLEN(site_key
));
1217 CLIGetHexWithReturn(ctx
, 4, site_key
, &site_key_len
);
1218 if (site_key_len
> 0 && site_key_len
!= 16) {
1219 PM3_RET_ERR_FREE(PM3_EINVARG
, "--sitekey must be 16 bytes");
1222 SetAPDULogging(arg_get_lit(ctx
, 5));
1228 DesfireContext_t dctx
= {0};
1229 DesfireClearContext(&dctx
);
1231 // Get card UID (for key diversification)
1232 int res
= DesfireGetCardUID(&dctx
);
1233 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed retrieving card UID");
1235 uid_len
= dctx
.uidlen
;
1236 memcpy(uid
, dctx
.uid
, uid_len
);
1240 uint8_t key
[CRYPTO_AES128_KEY_SIZE
] = {0};
1241 int res
= hfgal_diversify_key(site_key
, uid
, uid_len
, key_num
, aid
, key
);
1242 PM3_RET_IF_ERR_WITH_MSG(res
, "Failed diversifying key");
1244 char *key_str
= sprint_hex_inrow(key
, ARRAYLEN(key
));
1245 PrintAndLogEx(SUCCESS
, "Successfully diversified key: " _GREEN_("%s"), key_str
);
1247 if (IfPm3Iso14443()) {
1248 PrintAndLogEx(HINT
, "Hint: try " _YELLOW_("`hf mfdes auth --aid %06X --keyno %d --algo AES --key %s`") " to verify",
1257 static int CmdGallagherDecode(const char *cmd
) {
1258 CLIParserContext
*ctx
;
1259 CLIParserInit(&ctx
, "hf gallagher decode",
1260 "Decode Gallagher credential block\n"
1261 "Credential block can be specified with or without the bitwise inverse.",
1262 "hf gallagher decode --data A3B4B0C151B0A31B"
1265 void *argtable
[] = {
1267 arg_str1(NULL
, "data", "<hex>", "Credential block (8 or 16 bytes)"),
1270 CLIExecWithReturn(ctx
, cmd
, argtable
, false);
1273 uint8_t data_buf
[16] = {0};
1274 CLIGetHexWithReturn(ctx
, 1, data_buf
, &data_len
);
1275 if (data_len
!= 8 && data_len
!= 16) {
1276 PM3_RET_ERR_FREE(PM3_EINVARG
, "--data must be 8 or 16 bytes");
1280 if (data_len
== 16) {
1281 // Check second half of file is the bitwise inverse of the first half
1282 for (uint8_t i
= 8; i
< 16; i
++) {
1283 data_buf
[i
] ^= 0xFF;
1286 if (memcmp(data_buf
, &data_buf
[8], 8) != 0) {
1287 PM3_RET_ERR(PM3_EFAILED
, "Invalid cardholder data, last 8 bytes should be bitwise inverse of first 16 bytes. Received %s",
1288 sprint_hex_inrow(data_buf
, 16)
1292 for (uint8_t i
= 0; i
< 8; i
++) {
1293 data_buf
[i
+ 8] = data_buf
[i
] ^ 0xFF;
1295 PrintAndLogEx(INFO
, "Full credential block with bitwise inverse: " _YELLOW_("%s"), sprint_hex_inrow(data_buf
, 16));
1298 GallagherCredentials_t creds
= {0};
1299 gallagher_decode_creds(data_buf
, &creds
);
1301 PrintAndLogEx(SUCCESS
, "Gallagher - region: " _GREEN_("%c") " ( " _GREEN_("%u") " )"
1302 ", facility: " _GREEN_("%u")
1303 ", card number: " _GREEN_("%u")
1304 ", issue level: " _GREEN_("%u"),
1305 'A' + creds
.region_code
,
1307 creds
.facility_code
,
1314 static int CmdGallagherEncode(const char *cmd
) {
1315 CLIParserContext
*ctx
;
1316 CLIParserInit(&ctx
, "hf gallagher encode",
1317 "Encode a Gallagher credential block\n"
1318 "Credential block can be specified with or without the bitwise inverse.",
1319 "hf gallagher encode --rc 1 --fc 22153 --cn 1253518 --il 1"
1322 void *argtable
[] = {
1324 arg_u64_1("r", "rc", "<dec>", "Region code. 4 bits max"),
1325 arg_u64_1("f", "fc", "<dec>", "Facility code. 2 bytes max"),
1326 arg_u64_1("c", "cn", "<dec>", "Card number. 3 bytes max"),
1327 arg_u64_1("i", "il", "<dec>", "Issue level. 4 bits max"),
1330 CLIExecWithReturn(ctx
, cmd
, argtable
, false);
1332 uint64_t region_code
= arg_get_u64(ctx
, 1); // uint4, input will be validated later
1333 uint64_t facility_code
= arg_get_u64(ctx
, 2); // uint16
1334 uint64_t card_number
= arg_get_u64(ctx
, 3); // uint24
1335 uint64_t issue_level
= arg_get_u64(ctx
, 4); // uint4
1339 GallagherCredentials_t creds
= {
1340 .region_code
= region_code
,
1341 .facility_code
= facility_code
,
1342 .card_number
= card_number
,
1343 .issue_level
= issue_level
,
1347 uint8_t contents
[16] = {0};
1349 gallagher_encode_creds(contents
, &creds
);
1350 for (int i
= 0; i
< 8; i
++) {
1351 contents
[i
+ 8] = contents
[i
] ^ 0xFF;
1354 PrintAndLogEx(SUCCESS
, "Raw: " _YELLOW_("%s"), sprint_hex_inrow(contents
, ARRAYLEN(contents
) / 2));
1355 PrintAndLogEx(SUCCESS
, "Bitwise: " _YELLOW_("%s"), sprint_hex_inrow(contents
, ARRAYLEN(contents
)));
1361 static command_t CommandTable
[] = {
1362 {"help", CmdHelp
, AlwaysAvailable
, "This help"},
1363 {"reader", CmdGallagherReader
, IfPm3Iso14443
, "Read & decode all Gallagher credentials on a DESFire card"},
1364 {"clone", CmdGallagherClone
, IfPm3Iso14443
, "Add Gallagher credentials to a DESFire card"},
1365 {"delete", CmdGallagherDelete
, IfPm3Iso14443
, "Delete Gallagher credentials from a DESFire card"},
1366 {"diversifykey", CmdGallagherDiversify
, AlwaysAvailable
, "Diversify Gallagher key"},
1367 {"decode", CmdGallagherDecode
, AlwaysAvailable
, "Decode Gallagher credential block"},
1368 {"encode", CmdGallagherEncode
, AlwaysAvailable
, "Encode Gallagher credential block"},
1369 {NULL
, NULL
, NULL
, NULL
}
1372 static int CmdHelp(const char *cmd
) {
1373 (void) cmd
; // cmd is not used so far
1374 CmdsHelp(CommandTable
);
1378 int CmdHFGallagher(const char *cmd
) {
1379 clearCommandBuffer();
1380 return CmdsParse(CommandTable
, cmd
);