mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-21 05:43:48 -07:00
Add basic cloning support
This commit is contained in:
parent
7b0a85a0af
commit
aed687cb96
2 changed files with 355 additions and 14 deletions
|
@ -11,6 +11,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "cmdhfgallagher.h"
|
#include "cmdhfgallagher.h"
|
||||||
|
#include "generator.h"
|
||||||
#include "mifare.h"
|
#include "mifare.h"
|
||||||
#include "mifare/desfirecore.h"
|
#include "mifare/desfirecore.h"
|
||||||
#include "mifare/gallaghercore.h"
|
#include "mifare/gallaghercore.h"
|
||||||
|
@ -48,6 +49,8 @@ static int readCardApplicationDirectory(DesfireContext_t *ctx, uint8_t *destBuf,
|
||||||
for (uint8_t i = 0; i < 3; i++) {
|
for (uint8_t i = 0; i < 3; i++) {
|
||||||
size_t readLen;
|
size_t readLen;
|
||||||
DesfireReadFile(ctx, i, 0, 36, &destBuf[i * 36], &readLen);
|
DesfireReadFile(ctx, i, 0, 36, &destBuf[i * 36], &readLen);
|
||||||
|
// end if the last entry is NULL
|
||||||
|
if (memcmp(&destBuf[36 * i + 30], "\0\0\0\0\0\0", 6) == 0) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep only valid entries
|
// Keep only valid entries
|
||||||
|
@ -82,11 +85,11 @@ static int readCardApplicationCredentials(DesfireContext_t *ctx, uint8_t *aid, u
|
||||||
DesfireSetCommandSet(ctx, DCCNativeISO);
|
DesfireSetCommandSet(ctx, DCCNativeISO);
|
||||||
|
|
||||||
// Select and authenticate to AID
|
// Select and authenticate to AID
|
||||||
int res = DesfireSelectAndAuthenticateAppW(ctx, DACEV1, ISW6bAID, le24toh(aid), false, verbose);
|
int res = DesfireSelectAndAuthenticateAppW(ctx, DACEV1, ISW6bAID, DesfireAIDByteToUint(aid), false, verbose);
|
||||||
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
|
||||||
// Read file 0 (contains credentials)
|
// Read file 0 (contains credentials)
|
||||||
uint8_t buf[16] = { 0x00 };
|
uint8_t buf[16] = {0};
|
||||||
size_t readLen = 0;
|
size_t readLen = 0;
|
||||||
DesfireSetCommMode(ctx, DCMEncrypted);
|
DesfireSetCommMode(ctx, DCMEncrypted);
|
||||||
res = DesfireReadFile(ctx, 0, 0, 16, buf, &readLen);
|
res = DesfireReadFile(ctx, 0, 0, 16, buf, &readLen);
|
||||||
|
@ -220,39 +223,362 @@ static int CmdGallagherReader(const char *Cmd) {
|
||||||
return continuousMode ? PM3_SUCCESS : res;
|
return continuousMode ? PM3_SUCCESS : res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int GallagherDiversifyKey(uint8_t *sitekey, uint8_t *uid, uint8_t uidLen, uint8_t keyNo, uint32_t aid, uint8_t *keyOut) {
|
||||||
|
// Generate diversification input
|
||||||
|
uint8_t kdfInputLen = 11;
|
||||||
|
int res = mfdes_kdf_input_gallagher(uid, uidLen, keyNo, aid, keyOut, &kdfInputLen);
|
||||||
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
|
||||||
|
// Make temporary DesfireContext
|
||||||
|
DesfireContext_t dctx = {0};
|
||||||
|
DesfireSetKey(&dctx, 0, T_AES, sitekey);
|
||||||
|
|
||||||
|
// Diversify input & copy to output buffer
|
||||||
|
MifareKdfAn10922(&dctx, DCOMasterKey, keyOut, kdfInputLen);
|
||||||
|
memcpy(keyOut, dctx.key, CRYPTO_AES128_KEY_SIZE);
|
||||||
|
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int createGallagherCredentialsApplication(DesfireContext_t *ctx, uint8_t *sitekey, uint8_t *aid, bool verbose) {
|
||||||
|
DesfireSetCommMode(ctx, DCMPlain);
|
||||||
|
DesfireSetCommandSet(ctx, DCCNativeISO);
|
||||||
|
int res = DesfireSelectAndAuthenticateAppW(ctx, DACEV1, ISW6bAID, 0x000000, false, verbose);
|
||||||
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
|
||||||
|
// Create application
|
||||||
|
DesfireCryptoAlgorithm dstalgo = T_AES;
|
||||||
|
uint8_t keycount = 3;
|
||||||
|
uint8_t ks1 = 0x0B;
|
||||||
|
uint8_t ks2 = (DesfireKeyAlgoToType(dstalgo) << 6) | keycount;;
|
||||||
|
|
||||||
|
uint8_t data[5] = {0};
|
||||||
|
memcpy(&data[0], aid, 3);
|
||||||
|
data[3] = ks1;
|
||||||
|
data[4] = ks2;
|
||||||
|
|
||||||
|
DesfireSetCommMode(ctx, DCMMACed);
|
||||||
|
res = DesfireCreateApplication(ctx, data, ARRAYLEN(data));
|
||||||
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
|
||||||
|
// Select the new application
|
||||||
|
DesfireSetCommMode(ctx, DCMPlain);
|
||||||
|
res = DesfireSelectEx(ctx, true, ISW6bAID, DesfireAIDByteToUint(aid), NULL);
|
||||||
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
if (verbose)
|
||||||
|
PrintAndLogEx(INFO, "AID: %06x is " _GREEN_("selected"), DesfireAIDByteToUint(aid));
|
||||||
|
|
||||||
|
// Add key 2, then key 0 (we must authenticate with key 0 in order to make changes)
|
||||||
|
for (int i = 2; i >= 0; i -= 2) {
|
||||||
|
// Diversify key
|
||||||
|
uint8_t buf[CRYPTO_AES128_KEY_SIZE] = {0};
|
||||||
|
res = GallagherDiversifyKey(sitekey, ctx->uid, ctx->uidlen, i, DesfireAIDByteToUint(aid), buf);
|
||||||
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
PrintAndLogEx(INFO, " Diversified key %d: " _GREEN_("%s"), i, sprint_hex_inrow(buf, ARRAYLEN(buf)));
|
||||||
|
|
||||||
|
// Authenticate
|
||||||
|
uint8_t blankKey[DESFIRE_MAX_KEY_SIZE] = {0};
|
||||||
|
DesfireSetKeyNoClear(ctx, 0, T_AES, blankKey);
|
||||||
|
DesfireSetCommMode(ctx, DCMPlain);
|
||||||
|
res = DesfireAuthenticate(ctx, DACEV1, verbose);
|
||||||
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
|
||||||
|
// Change key
|
||||||
|
DesfireSetCommMode(ctx, DCMEncryptedPlain);
|
||||||
|
res = DesfireChangeKey(ctx, false, i, dstalgo, 1, buf, dstalgo, blankKey, true);
|
||||||
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
if (verbose)
|
||||||
|
PrintAndLogEx(INFO, " Successfully set key %d", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int createGallagherCredentialsFile(DesfireContext_t *ctx, uint8_t *sitekey, uint8_t *aid, GallagherCredentials_t *creds, bool verbose) {
|
||||||
|
// Set up context
|
||||||
|
DesfireSetKeyNoClear(ctx, 0, T_AES, sitekey);
|
||||||
|
DesfireSetKdf(ctx, MFDES_KDF_ALGO_GALLAGHER, NULL, 0);
|
||||||
|
DesfireSetCommMode(ctx, DCMPlain);
|
||||||
|
|
||||||
|
// Select application
|
||||||
|
int res = DesfireSelectAndAuthenticateAppW(ctx, DACEV1, ISW6bAID, DesfireAIDByteToUint(aid), false, verbose);
|
||||||
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
|
||||||
|
// Prepare create file command
|
||||||
|
uint8_t fileType = 0; // standard data file
|
||||||
|
uint8_t fileId = 0x00;
|
||||||
|
uint8_t fileSize = 16;
|
||||||
|
uint8_t fileAccessMode = 0x03; // encrypted
|
||||||
|
uint32_t fileRights = 0x2000; // key 0 has God mode, key 2 can read
|
||||||
|
|
||||||
|
uint8_t data[7] = {0};
|
||||||
|
data[0] = fileId;
|
||||||
|
data[1] = fileAccessMode;
|
||||||
|
data[2] = fileRights & 0xff;
|
||||||
|
data[3] = (fileRights >> 8) & 0xff;
|
||||||
|
Uint3byteToMemLe(&data[4], fileSize);
|
||||||
|
|
||||||
|
// Create file
|
||||||
|
res = DesfireCreateFile(ctx, fileType, data, ARRAYLEN(data), false);
|
||||||
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
|
||||||
|
// Create file contents (2nd half is the bitwise inverse of the encoded creds)
|
||||||
|
uint8_t contents[16] = {0};
|
||||||
|
encodeCardholderCredentials(contents, creds);
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
contents[i + 8] = contents[i] ^ 0xFF;
|
||||||
|
|
||||||
|
// Write file
|
||||||
|
DesfireSetCommMode(ctx, DCMEncrypted);
|
||||||
|
res = DesfireWriteFile(ctx, fileId, 0, ARRAYLEN(contents), contents);
|
||||||
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int createGallagherCAD(DesfireContext_t *ctx, uint8_t *sitekey, bool verbose) {
|
||||||
|
DesfireClearSession(ctx);
|
||||||
|
DesfireSetCommMode(ctx, DCMPlain);
|
||||||
|
DesfireSetCommandSet(ctx, DCCNativeISO);
|
||||||
|
int res = DesfireSelectAndAuthenticateAppW(ctx, DACEV1, ISW6bAID, 0x000000, false, verbose);
|
||||||
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
|
||||||
|
// Create application
|
||||||
|
uint32_t aid = 0x2F81F4;
|
||||||
|
DesfireCryptoAlgorithm dstalgo = T_AES;
|
||||||
|
uint8_t keycount = 1;
|
||||||
|
uint8_t ks1 = 0x0B;
|
||||||
|
uint8_t ks2 = (DesfireKeyAlgoToType(dstalgo) << 6) | keycount;;
|
||||||
|
|
||||||
|
uint8_t data[5] = {0};
|
||||||
|
DesfireAIDUintToByte(aid, &data[0]);
|
||||||
|
data[3] = ks1;
|
||||||
|
data[4] = ks2;
|
||||||
|
|
||||||
|
DesfireSetCommMode(ctx, DCMMACed);
|
||||||
|
res = DesfireCreateApplication(ctx, data, ARRAYLEN(data));
|
||||||
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
|
||||||
|
// Select the new application
|
||||||
|
DesfireSetCommMode(ctx, DCMPlain);
|
||||||
|
res = DesfireSelectEx(ctx, true, ISW6bAID, aid, NULL);
|
||||||
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
if (verbose)
|
||||||
|
PrintAndLogEx(INFO, "AID: %06x is " _GREEN_("selected"), aid);
|
||||||
|
|
||||||
|
// Diversify key
|
||||||
|
uint8_t buf[CRYPTO_AES128_KEY_SIZE] = {0};
|
||||||
|
res = GallagherDiversifyKey(sitekey, ctx->uid, ctx->uidlen, 0, aid, buf);
|
||||||
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
PrintAndLogEx(INFO, " Diversified key 0: " _GREEN_("%s"), sprint_hex_inrow(buf, ARRAYLEN(buf)));
|
||||||
|
|
||||||
|
// Authenticate
|
||||||
|
uint8_t blankKey[DESFIRE_MAX_KEY_SIZE] = {0};
|
||||||
|
DesfireSetKeyNoClear(ctx, 0, T_AES, blankKey);
|
||||||
|
DesfireSetCommMode(ctx, DCMPlain);
|
||||||
|
res = DesfireAuthenticate(ctx, DACEV1, verbose);
|
||||||
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
|
||||||
|
// Change key
|
||||||
|
DesfireSetCommMode(ctx, DCMEncryptedPlain);
|
||||||
|
res = DesfireChangeKey(ctx, false, 0, dstalgo, 1, buf, dstalgo, blankKey, true);
|
||||||
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
if (verbose)
|
||||||
|
PrintAndLogEx(INFO, " Successfully set key 0");
|
||||||
|
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int updateGallagherCAD(DesfireContext_t *ctx, uint8_t *sitekey, uint8_t *aid, GallagherCredentials_t *creds, bool verbose) {
|
||||||
|
uint32_t cadAid = 0x2F81F4;
|
||||||
|
|
||||||
|
|
||||||
|
// Check if CAD exists
|
||||||
|
uint8_t cad[36 * 3] = {0};
|
||||||
|
uint8_t numEntries = 0;
|
||||||
|
int res = DesfireSelectEx(ctx, true, ISW6bAID, cadAid, NULL);
|
||||||
|
if (res == PM3_SUCCESS) {
|
||||||
|
res = readCardApplicationDirectory(ctx, cad, ARRAYLEN(cad), &numEntries, verbose);
|
||||||
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
} else {
|
||||||
|
DesfireSetCommandSet(ctx, DCCNativeISO);
|
||||||
|
res = createGallagherCAD(ctx, sitekey, verbose);
|
||||||
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
|
||||||
|
// Check that there is space for the new entry
|
||||||
|
if (numEntries == 18) {
|
||||||
|
PrintAndLogEx(ERR, "Card application directory is full.");
|
||||||
|
return PM3_EFATAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t fileId = numEntries / 6; // 6 entries per file
|
||||||
|
uint8_t entryNum = numEntries % 6;
|
||||||
|
|
||||||
|
// Create entry
|
||||||
|
uint8_t *entry = &cad[numEntries * 6];
|
||||||
|
entry[0] = creds->region_code;
|
||||||
|
entry[1] = (creds->facility_code >> 8) & 0xFF;
|
||||||
|
entry[2] = creds->facility_code & 0xFF;
|
||||||
|
memcpy(&entry[3], aid, 3);
|
||||||
|
reverseAid(&entry[3]); // CAD stores AIDs backwards
|
||||||
|
|
||||||
|
// Set up context
|
||||||
|
DesfireSetKeyNoClear(ctx, 0, T_AES, sitekey);
|
||||||
|
DesfireSetKdf(ctx, MFDES_KDF_ALGO_GALLAGHER, NULL, 0);
|
||||||
|
DesfireSetCommMode(ctx, DCMPlain);
|
||||||
|
|
||||||
|
// Select application
|
||||||
|
res = DesfireSelectAndAuthenticateAppW(ctx, DACEV1, ISW6bAID, cadAid, false, verbose);
|
||||||
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
|
||||||
|
// Create file if necessary
|
||||||
|
if (entryNum == 0) {
|
||||||
|
// Prepare create file command
|
||||||
|
uint8_t fileType = 0; // standard data file
|
||||||
|
uint8_t fileSize = 36;
|
||||||
|
uint8_t fileAccessMode = 0x00; // plain
|
||||||
|
uint32_t fileRights = 0xE000; // key 0 has God mode, everyone can read
|
||||||
|
|
||||||
|
uint8_t data[7] = {0};
|
||||||
|
data[0] = fileId;
|
||||||
|
data[1] = fileAccessMode;
|
||||||
|
data[2] = fileRights & 0xff;
|
||||||
|
data[3] = (fileRights >> 8) & 0xff;
|
||||||
|
Uint3byteToMemLe(&data[4], fileSize);
|
||||||
|
|
||||||
|
// Create file
|
||||||
|
res = DesfireCreateFile(ctx, fileType, data, ARRAYLEN(data), false);
|
||||||
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
|
||||||
|
// Write file
|
||||||
|
res = DesfireWriteFile(ctx, fileId, fileId * 36, 36, entry);
|
||||||
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
} else {
|
||||||
|
// Write file
|
||||||
|
res = DesfireWriteFile(ctx, fileId, entryNum * 6, 6, entry);
|
||||||
|
HF_GALLAGHER_RETURN_IF_ERROR(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
static int CmdGallagherClone(const char *Cmd) {
|
static int CmdGallagherClone(const char *Cmd) {
|
||||||
CLIParserContext *ctx;
|
CLIParserContext *ctx;
|
||||||
CLIParserInit(&ctx, "hf gallagher clone",
|
CLIParserInit(&ctx, "hf gallagher clone",
|
||||||
"clone a GALLAGHER card to a blank DESFire card.",
|
"clone a GALLAGHER card to a blank DESFire card.",
|
||||||
"hf gallagher clone --rc 1 --fc 22 --cn 3333 --il 4"
|
"hf gallagher clone --rc 1 --fc 22 --cn 3333 --il 4 --sitekey 00112233445566778899aabbccddeeff"
|
||||||
);
|
);
|
||||||
|
|
||||||
void *argtable[] = {
|
void *argtable[] = {
|
||||||
arg_param_begin,
|
arg_param_begin,
|
||||||
arg_u64_1(NULL, "rc", "<decimal>", "Region code. 4 bits max"),
|
arg_lit0(NULL, "apdu", "show APDU requests and responses"),
|
||||||
arg_u64_1(NULL, "fc", "<decimal>", "Facility code. 2 bytes max"),
|
arg_lit0("v", "verbose", "Verbose mode"),
|
||||||
arg_u64_1(NULL, "cn", "<decimal>", "Card number. 3 bytes max"),
|
arg_int0("n", "keyno", "<decimal>", "Key number [default=0]"),
|
||||||
arg_u64_1(NULL, "il", "<decimal>", "Issue level. 4 bits max"),
|
arg_str0("t", "algo", "<DES/2TDEA/3TDEA/AES>", "Crypt algo: DES, 2TDEA, 3TDEA, AES"),
|
||||||
|
arg_str0("k", "key", "<hex>", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"),
|
||||||
|
|
||||||
|
arg_u64_1(NULL, "rc", "<decimal>", "Region code. 4 bits max"),
|
||||||
|
arg_u64_1(NULL, "fc", "<decimal>", "Facility code. 2 bytes max"),
|
||||||
|
arg_u64_1(NULL, "cn", "<decimal>", "Card number. 3 bytes max"),
|
||||||
|
arg_u64_1(NULL, "il", "<decimal>", "Issue level. 4 bits max"),
|
||||||
|
arg_str0(NULL, "aid", "<hex>", "Application ID to write (3 bytes) [default=2081F4]"),
|
||||||
|
arg_str1(NULL, "sitekey", "<hex>", "Master site key to compute diversified keys (16 bytes)"),
|
||||||
arg_param_end
|
arg_param_end
|
||||||
};
|
};
|
||||||
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||||
|
|
||||||
uint64_t region_code = arg_get_u64(ctx, 1); // uint16, will be validated later
|
SetAPDULogging(arg_get_lit(ctx, 1));
|
||||||
uint64_t facility_code = arg_get_u64(ctx, 2); // uint32, will be validated later
|
bool verbose = arg_get_lit(ctx, 2) || true;
|
||||||
uint64_t card_number = arg_get_u64(ctx, 3); // uint64
|
int keyNum = arg_get_int_def(ctx, 3, 0);
|
||||||
uint64_t issue_level = arg_get_u64(ctx, 4); // uint32, will be validated later
|
|
||||||
|
int algo = T_DES;
|
||||||
|
if (CLIGetOptionList(arg_get_str(ctx, 4), DesfireAlgoOpts, &algo)) return PM3_ESOFT;
|
||||||
|
|
||||||
|
int keyLen = 0;
|
||||||
|
uint8_t key[DESFIRE_MAX_KEY_SIZE] = {0};
|
||||||
|
CLIGetHexWithReturn(ctx, 5, key, &keyLen);
|
||||||
|
if (keyLen && keyLen != desfire_get_key_length(algo)) {
|
||||||
|
PrintAndLogEx(ERR, "%s key must have %d bytes length instead of %d.", CLIGetOptionListStr(DesfireAlgoOpts, algo), desfire_get_key_length(algo), keyLen);
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
if (keyLen == 0) {
|
||||||
|
// Default to a key of all zeros
|
||||||
|
keyLen = desfire_get_key_length(algo);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t region_code = arg_get_u64(ctx, 6); // uint16, will be validated later
|
||||||
|
uint64_t facility_code = arg_get_u64(ctx, 7); // uint32, will be validated later
|
||||||
|
uint64_t card_number = arg_get_u64(ctx, 8); // uint64
|
||||||
|
uint64_t issue_level = arg_get_u64(ctx, 9); // uint32, will be validated later
|
||||||
|
|
||||||
|
int aidLen = 0;
|
||||||
|
uint8_t aid[3] = "\x20\x81\xF4";
|
||||||
|
CLIGetHexWithReturn(ctx, 10, aid, &aidLen);
|
||||||
|
if (aidLen > 0 && aidLen != 3) {
|
||||||
|
PrintAndLogEx(ERR, "--aid must be 3 bytes");
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
reverseAid(aid); // PM3 displays AIDs backwards
|
||||||
|
|
||||||
|
// Check that the AID is in the expected range
|
||||||
|
if (memcmp(aid, "\xF4\x81", 2) != 0 || aid[2] < 0x20 || aid[2] > 0x2B) {
|
||||||
|
PrintAndLogEx(WARNING, "Invalid Gallagher AID %06X, expected 2?81F4.", DesfireAIDByteToUint(aid));
|
||||||
|
}
|
||||||
|
|
||||||
|
int sitekeyLen = 0;
|
||||||
|
uint8_t sitekey[16] = {0};
|
||||||
|
CLIGetHexWithReturn(ctx, 11, sitekey, &sitekeyLen);
|
||||||
|
if (sitekeyLen > 0 && sitekeyLen != 16) {
|
||||||
|
PrintAndLogEx(ERR, "--sitekey must be 16 bytes");
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
CLIParserFree(ctx);
|
CLIParserFree(ctx);
|
||||||
|
|
||||||
if (!isValidGallagherCredentials(region_code, facility_code, card_number, issue_level)) {
|
if (!isValidGallagherCredentials(region_code, facility_code, card_number, issue_level)) {
|
||||||
return PM3_EINVARG;
|
return PM3_EINVARG;
|
||||||
}
|
}
|
||||||
|
GallagherCredentials_t creds = {
|
||||||
|
.region_code = region_code,
|
||||||
|
.facility_code = facility_code,
|
||||||
|
.card_number = card_number,
|
||||||
|
.issue_level = issue_level,
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: create data
|
// Set up context
|
||||||
|
DropField();
|
||||||
|
DesfireContext_t dctx = {0};
|
||||||
|
DesfireClearContext(&dctx);
|
||||||
|
|
||||||
PrintAndLogEx(INFO, "Preparing to clone Gallagher from specified data.");
|
// Get card UID (for key diversification)
|
||||||
|
int res = DesfireGetCardUID(&dctx);
|
||||||
|
HF_GALLAGHER_FAIL_IF_ERROR(res, true, "Failed retrieving card UID.");
|
||||||
|
|
||||||
// TODO: write data
|
// Create application
|
||||||
|
DesfireSetKeyNoClear(&dctx, keyNum, algo, key);
|
||||||
|
DesfireSetKdf(&dctx, MFDES_KDF_ALGO_NONE, NULL, 0);
|
||||||
|
res = createGallagherCredentialsApplication(&dctx, sitekey, aid, verbose);
|
||||||
|
HF_GALLAGHER_FAIL_IF_ERROR(res, true, "Failed creating Gallagher application.");
|
||||||
|
DropField();
|
||||||
|
|
||||||
|
// Create credential files
|
||||||
|
// Don't need to set keys here, they're generated automatically
|
||||||
|
res = createGallagherCredentialsFile(&dctx, sitekey, aid, &creds, verbose);
|
||||||
|
HF_GALLAGHER_FAIL_IF_ERROR(res, true, "Failed creating Gallagher credential file.");
|
||||||
|
DropField();
|
||||||
|
|
||||||
|
// Update card application directory
|
||||||
|
DesfireSetKeyNoClear(&dctx, keyNum, algo, key);
|
||||||
|
DesfireSetKdf(&dctx, MFDES_KDF_ALGO_NONE, NULL, 0);
|
||||||
|
res = updateGallagherCAD(&dctx, sitekey, aid, &creds, verbose);
|
||||||
|
HF_GALLAGHER_FAIL_IF_ERROR(res, true, "Failed updating Gallagher card application directory.");
|
||||||
|
DropField();
|
||||||
|
|
||||||
|
DropField();
|
||||||
PrintAndLogEx(SUCCESS, "Done");
|
PrintAndLogEx(SUCCESS, "Done");
|
||||||
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf gallagher reader`") " to verify");
|
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf gallagher reader`") " to verify");
|
||||||
return PM3_ENOTIMPL;
|
return PM3_ENOTIMPL;
|
||||||
|
|
|
@ -13,9 +13,24 @@
|
||||||
#define CMDHFGALLAGHER_H__
|
#define CMDHFGALLAGHER_H__
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
int CmdHFGallagher(const char *Cmd);
|
int CmdHFGallagher(const char *Cmd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create Gallagher Application Master Key by diversifying
|
||||||
|
* the MIFARE Site Key with card UID, key number, and application ID.
|
||||||
|
*
|
||||||
|
* @param sitekey MIFARE Site Key (16 bytes).
|
||||||
|
* @param uid Card unique ID (4 or 7 bytes).
|
||||||
|
* @param uidLen Length of UID.
|
||||||
|
* @param keyNum Key number (0 <= keyNum <= 2).
|
||||||
|
* @param aid Application ID (0x2?81F4 where 0 <= ? <= B).
|
||||||
|
* @param keyOut Buffer to copy the diversified key into (must be 16 bytes).
|
||||||
|
* @return PM3_SUCCESS if successful, PM3_EINVARG if an argument is invalid.
|
||||||
|
*/
|
||||||
|
int GallagherDiversifyKey(uint8_t *sitekey, uint8_t *uid, uint8_t uidLen, uint8_t keyNum, uint32_t aid, uint8_t *keyOut);
|
||||||
|
|
||||||
#define HF_GALLAGHER_RETURN_IF_ERROR(res) if (res != PM3_SUCCESS) { return res; }
|
#define HF_GALLAGHER_RETURN_IF_ERROR(res) if (res != PM3_SUCCESS) { return res; }
|
||||||
#define HF_GALLAGHER_FAIL_IF_ERROR(res, verbose, reason) if (res != PM3_SUCCESS) { if (verbose) PrintAndLogEx(ERR, reason " Error code %d", res); DropField(); return res; }
|
#define HF_GALLAGHER_FAIL_IF_ERROR(res, verbose, reason) if (res != PM3_SUCCESS) { if (verbose) PrintAndLogEx(ERR, reason " Error code %d", res); DropField(); return res; }
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue