diff --git a/Doc/DESFireGallagherReadme.md b/Doc/DESFireGallagherReadme.md new file mode 100644 index 00000000..41949f01 --- /dev/null +++ b/Doc/DESFireGallagherReadme.md @@ -0,0 +1,54 @@ +# Emulating Gallagher on MIFARE DESFire +As of November 2022, Chameleon supports emulation of Gallagher on top of MIFARE DESFire cards. However, there are few limitations. +Namely, only the AES encryption was tested. Other encryption modes (such as 3K tripple DES) may work if written on the Chameleon using a Proxmark or another reader. +They will, however, NOT work when using the commands below. + +Mainly, there is now support for Gallagher self-programming using terminal commands. The supported actions are the following: +* Create a new DESFire card with a Gallagher application +* Select a Gallagher application to perform tasks over +* Create a Gallagher application +* Update a Gallagher application +* Update the card ID of a Gallagher application + +However, these things are missing: +* Create a standalone Gallagher application directory app (however, it can be created as a part of creating the whole card) +* Changing the site key (the default site key is used) + +For performing all the following commands, you first need to select the current slot to appear as a DESFire card. Eg ``CONFIG=MF_DESFIRE_4KEV1``. + +## Create a new DESFire card with a Gallagher application (DF_SETUP_GALL) +This command sets up a new Gallagher encoded DESFire card with a single application. This is usually enough to emulate a personal access card. +If you just need a card copy, this is the only Gallagher command you need. If you need to change the UID of the Chameleon, remember to do it before running this command. + +The syntax is: ``DF_SETUP_GALL=CFIR`` eg ``DF_SETUP_GALL=C123456F1234I1R2``. + +Card ID is a 32bit unsigned integer, facility ID is a 16bit unsigned integer and issue level as well as region code are 8bit unsigned integers. + +## Select a Gallagher application to perform tasks over (DF_SEL_GALLAPP) +Selects the AID of the Gallagher app to perform operations over. This is needed for ``DF_CRE_GALLAPP``, ``DF_UP_GALLAPP``, and ``DF_UP_GALL_CID``. You do not need to run this command if you ran ``DF_SETUP_GALL`` previously as the default value is the last app an operation was executed over. + +The syntax is: ``DF_SEL_GALLAPP=`` eg ``DF_SEL_GALLAPP=`` or ``DF_SEL_GALLAPP=<000100>``. + +The AID has three bytes and all six hexadecimal digits need to be present when running this command. + +## Create a Gallagher application (DF_CRE_GALLAPP) +Run ``DF_SEL_GALLAPP`` or ``DF_SETUP_GALL`` first! + +This command sets up the Gallagher application only. It does not set up or update the application directory app. The syntax is the same as with ``DF_SETUP_GAL``. If you want to update data in an already existiing application, use ``DF_UP_GALLAPP``. + +Warning! You need to set up the Gallagher application directory app yoursef. However, the DESFire card application directory will be updated for you. + +## Update a Gallagher application (DF_UP_GALLAPP) +Run ``DF_SEL_GALLAPP`` or ``DF_SETUP_GALL`` first! + +This command lets you update the contents of a Gallagher application. The syntax is the same as with ``DF_SETUP_GAL``. If you want to create a new application, use ``DF_CRE_GALLAPP``. + +## Update the card ID of a Gallagher application (DF_UP_GALL_CID) +Run ``DF_SEL_GALLAPP`` or ``DF_SETUP_GALL`` first! + +This command lets you update the Gallagher card ID of a already existing Gallagher app. The facility ID, region and issue level are taken from the last operation. The AID is either taken from the last app create operation or from ``DF_SEL_GALLAPP``. + +The syntax is ``DF_UP_GALL_CIDP=``. Eg. ``DF_UP_GALL_CIDP=123456``. The card ID is a 32bit unsigned integer. + +## Change the site key (DF_SET_GALLKEY) +This command will return OK WITH TEXT - NOT IMPLEMENTED diff --git a/Firmware/Chameleon-Mini/Application/CryptoCMAC.c b/Firmware/Chameleon-Mini/Application/CryptoCMAC.c index bb35b7ab..7971c46f 100644 --- a/Firmware/Chameleon-Mini/Application/CryptoCMAC.c +++ b/Firmware/Chameleon-Mini/Application/CryptoCMAC.c @@ -199,8 +199,12 @@ bool DesfireCMACGenerateSubkeys(uint8_t cryptoType, const uint8_t *keyData, uint return true; } +bool DesfireCryptoCMAC(uint8_t cryptoType, const uint8_t *keyData, uint8_t *bufferDataIn, uint16_t bufferSize, uint8_t *IV, uint8_t *cmac){ + return DesfireCryptoCMACEx(cryptoType, keyData, bufferDataIn, bufferSize, IV, cmac, 0); +} + //Taken from https://github.com/RfidResearchGroup/proxmark3/blob/master/client/src/mifare/desfirecrypto.c -bool DesfireCryptoCMAC(uint8_t cryptoType, const uint8_t *keyData, uint8_t *bufferDataIn, uint16_t bufferSize, uint8_t *IV, uint8_t *cmac) { +bool DesfireCryptoCMACEx(uint8_t cryptoType, const uint8_t *keyData, uint8_t *bufferDataIn, uint16_t bufferSize, uint8_t *IV, uint8_t *cmac, uint16_t minlen) { uint8_t kbs; uint8_t len = bufferSize; uint8_t * bufferData = bufferDataIn + bufferSize; @@ -223,9 +227,9 @@ bool DesfireCryptoCMAC(uint8_t cryptoType, const uint8_t *keyData, uint8_t *buff DesfireCMACGenerateSubkeys(cryptoType, keyData, sk1, sk2); - if ((!len) || (len % kbs)) { + if ((!len) || (len % kbs) || (len < minlen)) { bufferData[len++] = 0x80; - while (len % kbs) { + while (len % kbs || len < minlen) { bufferData[len++] = 0x00; } bin_xor(bufferData + len - kbs, sk2, kbs); diff --git a/Firmware/Chameleon-Mini/Application/CryptoCMAC.h b/Firmware/Chameleon-Mini/Application/CryptoCMAC.h index 8ee397cd..1494c0a9 100644 --- a/Firmware/Chameleon-Mini/Application/CryptoCMAC.h +++ b/Firmware/Chameleon-Mini/Application/CryptoCMAC.h @@ -43,5 +43,6 @@ uint16_t appendBufferMAC(const uint8_t *keyData, uint8_t *bufferData, uint16_t b bool checkBufferCMAC(uint8_t *bufferData, uint16_t bufferSize, uint16_t checksumSize); bool DesfireCryptoCMAC(uint8_t cryptoType, const uint8_t *keyData, uint8_t *bufferDataIn, uint16_t bufferSize, uint8_t *IV, uint8_t *cmac); +bool DesfireCryptoCMACEx(uint8_t cryptoType, const uint8_t *keyData, uint8_t *bufferDataIn, uint16_t bufferSize, uint8_t *IV, uint8_t *cmac, uint16_t minlen); #endif diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminal.c b/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminal.c index c8db3727..ee021810 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminal.c +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminal.c @@ -22,6 +22,9 @@ This notice must be retained at the top of all source files where indicated. /* * DESFireChameleonTerminal.c * Maxie D. Schmidt (github.com/maxieds) + * + * Part of this file was added by Tomas Preucil (github.com/tomaspre) + * This part is indicated in the code below */ #if defined(CONFIG_MF_DESFIRE_SUPPORT) && !defined(DISABLE_DESFIRE_TERMINAL_COMMANDS) @@ -34,6 +37,11 @@ This notice must be retained at the top of all source files where indicated. #include "DESFirePICCControl.h" #include "DESFireMemoryOperations.h" #include "DESFireLogging.h" +#include "DESFireGallagher.h" + +#include + +extern DESFireAidType selectedGallagherAID; bool IsDESFireConfiguration(void) { return GlobalSettings.ActiveSettingPtr->Configuration == CONFIG_MF_DESFIRE || @@ -151,6 +159,7 @@ CommandStatusIdType CommandDESFireSetHeaderProperty(char *OutParam, const char * MemoryStoreDesfireHeaderBytes(); return COMMAND_INFO_OK_ID; } + #endif /* DISABLE_PERMISSIVE_DESFIRE_SETTINGS */ CommandStatusIdType CommandDESFireSetCommMode(char *OutParam, const char *InParams) { @@ -224,4 +233,144 @@ CommandStatusIdType CommandDESFireSetEncryptionMode(char *OutParam, const char * return COMMAND_INFO_OK; } +//The rest of the file was added by tomaspre +CommandStatusIdType CommandDESFireSetupGallagher(char *OutMessage, const char *InParams) { + if (!IsDESFireConfiguration()) { + return COMMAND_ERR_INVALID_USAGE_ID; + } + + uint32_t cardId = 0xFFFFFFFF; + uint16_t facilityId = 0xFFFF; + uint8_t issueLevel = 0xFF; + uint8_t regionCode = 0xFF; + + if (sscanf_P(InParams, PSTR("C%"SCNu32"F%"SCNu16"I%"SCNu8"R%"SCNu8), &cardId, &facilityId, &issueLevel, ®ionCode) != 4) { + return COMMAND_ERR_INVALID_PARAM_ID; + } + + bool ret = CreateGallagherCard(cardId, facilityId, issueLevel, regionCode); + + if (!ret) { + return COMMAND_ERR_INVALID_USAGE_ID; + } + + return COMMAND_INFO_OK_ID; +} + +CommandStatusIdType CommandDESFireCreateGallagherApp(char *OutMessage, const char *InParams) { + if (!IsDESFireConfiguration()) { + return COMMAND_ERR_INVALID_USAGE_ID; + } + + if (selectedGallagherAID[0] == 0xFF && selectedGallagherAID[1] == 0xFF && selectedGallagherAID[2] == 0xFF) { + snprintf_P(OutMessage, TERMINAL_BUFFER_SIZE, PSTR("SET AID FIRST")); + return COMMAND_ERR_INVALID_USAGE_ID; + } + + DESFireAidType AID; + uint32_t cardId = 0xFFFFFFFF; + uint16_t facilityId = 0xFFFF; + uint8_t issueLevel = 0xFF; + uint8_t regionCode = 0xFF; + + if (sscanf_P(InParams, PSTR("C%"SCNu32"F%"SCNu16"I%"SCNu8"R%"SCNu8), + &cardId, &facilityId, &issueLevel, ®ionCode + ) != 4) { + return COMMAND_ERR_INVALID_PARAM_ID; + } + + bool ret = CreateGallagherAppWithAID(cardId, facilityId, issueLevel, regionCode, selectedGallagherAID); + + if (!ret) { + return COMMAND_ERR_INVALID_USAGE_ID; + } + + return COMMAND_INFO_OK_ID; +} + +CommandStatusIdType CommandDESFireUpdateGallagherApp(char *OutMessage, const char *InParams) { + if (!IsDESFireConfiguration()) { + return COMMAND_ERR_INVALID_USAGE_ID; + } + + if (selectedGallagherAID[0] == 0xFF && selectedGallagherAID[1] == 0xFF && selectedGallagherAID[2] == 0xFF) { + snprintf_P(OutMessage, TERMINAL_BUFFER_SIZE, PSTR("SET AID FIRST")); + return COMMAND_ERR_INVALID_USAGE_ID; + } + + DESFireAidType AID; + uint32_t cardId = 0xFFFFFFFF; + uint16_t facilityId = 0xFFFF; + uint8_t issueLevel = 0xFF; + uint8_t regionCode = 0xFF; + + if (sscanf_P(InParams, PSTR("C%"SCNu32"F%"SCNu16"I%"SCNu8"R%"SCNu8), + &cardId, &facilityId, &issueLevel, ®ionCode + ) != 4) { + return COMMAND_ERR_INVALID_PARAM_ID; + } + + bool ret = UpdateGallagherFile(cardId, facilityId, issueLevel, regionCode, selectedGallagherAID); + + if (!ret) { + return COMMAND_ERR_INVALID_USAGE_ID; + } + + return COMMAND_INFO_OK_ID; +} + +CommandStatusIdType CommandDESFireUpdateGallagherCardId(char *OutMessage, const char *InParams) { + if (!IsDESFireConfiguration()) { + return COMMAND_ERR_INVALID_USAGE_ID; + } + + if (selectedGallagherAID[0] == 0xFF && selectedGallagherAID[1] == 0xFF && selectedGallagherAID[2] == 0xFF) { + snprintf_P(OutMessage, TERMINAL_BUFFER_SIZE, PSTR("SET AID FIRST")); + return COMMAND_ERR_INVALID_USAGE_ID; + } + + uint32_t cardId; + + if (sscanf_P(InParams, PSTR("%"SCNu32), &cardId) != 1) { + return COMMAND_ERR_INVALID_PARAM_ID; + } + + bool ret = UpdateGallagherAppCardID(cardId); + + if (!ret) { + return COMMAND_ERR_INVALID_USAGE_ID; + } + + return COMMAND_INFO_OK_ID; +} + +CommandStatusIdType CommandDESFireSelectGallagherApp(char *OutMessage, const char *InParams) { + if (!IsDESFireConfiguration()) { + return COMMAND_ERR_INVALID_USAGE_ID; + } + + DESFireAidType AID; + + if (sscanf_P(InParams, PSTR("%2"SCNx8"%2"SCNx8"%2"SCNx8), + &AID[0], &AID[1], &AID[2]) != 3) { + return COMMAND_ERR_INVALID_PARAM_ID; + } + + SelectGallagherAID(AID); + + return COMMAND_INFO_OK_ID; +} + +CommandStatusIdType CommandDESFireSetGallagherSiteKey(char *OutMessage, const char *InParams) { + if (!IsDESFireConfiguration()) { + return COMMAND_ERR_INVALID_USAGE_ID; + } + + //Use the SetGallagherSiteKey and ResetGallagherSiteKey fucntions + + snprintf_P(OutMessage, TERMINAL_BUFFER_SIZE, PSTR("NOT IMPLEMENTED")); + return COMMAND_INFO_OK_WITH_TEXT_ID; +} + #endif /* CONFIG_MF_DESFIRE_SUPPORT */ + diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminal.h b/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminal.h index 44a8b511..967deabf 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminal.h +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminal.h @@ -22,6 +22,9 @@ This notice must be retained at the top of all source files where indicated. /* * DESFireChameleonTerminal.h * Maxie D. Schmidt (github.com/maxieds) + * + * Part of this file was added by Tomas Preucil (github.com/tomaspre) + * This part is indicated in the code below */ #ifndef __DESFIRE_CHAMELEON_TERMINAL_H__ @@ -47,6 +50,25 @@ CommandStatusIdType CommandDESFireSetCommMode(char *OutMessage, const char *InPa #define DFCOMMAND_SET_ENCMODE "DF_ENCMODE" CommandStatusIdType CommandDESFireSetEncryptionMode(char *OutMessage, const char *InParams); +//The rest of the file was added by tomaspre +#define DFCOMMAND_SETUP_GALLAGHER "DF_SETUP_GALL" +CommandStatusIdType CommandDESFireSetupGallagher(char *OutMessage, const char *InParams); + +#define DFCOMMAND_CREATE_GALLAGHER_APP "DF_CRE_GALLAPP" +CommandStatusIdType CommandDESFireCreateGallagherApp(char *OutMessage, const char *InParams); + +#define DFCOMMAND_UPDATE_GALLAGHER_APP "DF_UP_GALLAPP" +CommandStatusIdType CommandDESFireUpdateGallagherApp(char *OutMessage, const char *InParams); + +#define DFCOMMAND_UPDATE_GALLAGHER_CARD_ID "DF_UP_GALL_CID" +CommandStatusIdType CommandDESFireUpdateGallagherCardId(char *OutMessage, const char *InParams); + +#define DFCOMMAND_SELECT_GALLAGHER_APP "DF_SEL_GALLAPP" +CommandStatusIdType CommandDESFireSelectGallagherApp(char *OutMessage, const char *InParams); + +#define DFCOMMAND_SET_GALLAGHER_SITE_KEY "DF_SET_GALLKEY" +CommandStatusIdType CommandDESFireSetGallagherSiteKey(char *OutMessage, const char *InParams); + #endif /* DESFire Support */ #endif /* __DESFIRE_CHAMELEON_TERMINAL_H__ */ diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminalInclude.c b/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminalInclude.c index 0df8437c..c324cacf 100644 --- a/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminalInclude.c +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireChameleonTerminalInclude.c @@ -22,6 +22,9 @@ This notice must be retained at the top of all source files where indicated. /* * DESFireChameleonTerminalInclude.c * Maxie D. Schmidt (github.com/maxieds) + * + * Part of this file was added by Tomas Preucil (github.com/tomaspre) + * This part is indicated in the code below */ #ifdef CONFIG_MF_DESFIRE_SUPPORT @@ -46,8 +49,46 @@ This notice must be retained at the top of all source files where indicated. .ExecParamFunc = NO_FUNCTION, .SetFunc = CommandDESFireSetEncryptionMode, .GetFunc = NO_FUNCTION +}, {//The rest of the file was added by tomaspre + .Command = DFCOMMAND_SETUP_GALLAGHER, + .ExecFunc = NO_FUNCTION, + .ExecParamFunc = NO_FUNCTION, + .SetFunc = CommandDESFireSetupGallagher, + .GetFunc = NO_FUNCTION +}, { + .Command = DFCOMMAND_CREATE_GALLAGHER_APP, + .ExecFunc = NO_FUNCTION, + .ExecParamFunc = NO_FUNCTION, + .SetFunc = CommandDESFireCreateGallagherApp, + .GetFunc = NO_FUNCTION +}, { + .Command = DFCOMMAND_UPDATE_GALLAGHER_APP, + .ExecFunc = NO_FUNCTION, + .ExecParamFunc = NO_FUNCTION, + .SetFunc = CommandDESFireUpdateGallagherApp, + .GetFunc = NO_FUNCTION +}, { + .Command = DFCOMMAND_UPDATE_GALLAGHER_CARD_ID, + .ExecFunc = NO_FUNCTION, + .ExecParamFunc = NO_FUNCTION, + .SetFunc = CommandDESFireUpdateGallagherCardId, + .GetFunc = NO_FUNCTION +}, { + .Command = DFCOMMAND_SELECT_GALLAGHER_APP, + .ExecFunc = NO_FUNCTION, + .ExecParamFunc = NO_FUNCTION, + .SetFunc = CommandDESFireSelectGallagherApp, + .GetFunc = NO_FUNCTION +}, { + .Command = DFCOMMAND_SET_GALLAGHER_SITE_KEY, + .ExecFunc = NO_FUNCTION, + .ExecParamFunc = NO_FUNCTION, + .SetFunc = CommandDESFireSetGallagherSiteKey, + .GetFunc = NO_FUNCTION }, + + #endif #endif /* CONFIG_MF_DESFIRE_SUPPORT */ diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireGallagher.c b/Firmware/Chameleon-Mini/Application/DESFire/DESFireGallagher.c new file mode 100644 index 00000000..16af7313 --- /dev/null +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireGallagher.c @@ -0,0 +1,239 @@ +/* +This file from this firmware source +is free software written by Tomas Preucil (github.com/tomaspre): +You can redistribute it and/or modify +it under the terms of this license. + +This software is intended for demonstration and testing purposes on your own hardware only. +When setting up a Gallagher system, always use a non-default site key! + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +This notice must be retained at the top of all source files where indicated. +*/ + +/* + * DESFireGallagher.c + * Tomas Preucil (github.com/tomaspre) + */ + +#include +#include "DESFireGallagher.h" +#include "DESFireGallagherTools.h" +#include "DESFireLogging.h" +#include "DESFireUtils.h" +//SelectedApp +//Authenticated, wthkey, wthmasterkey +//CRYPTO_AES_KEY_SIZE + +#define CAD_BLOCK_LEN 0x24 +#define GALL_BLOCK_LEN 0x10 + +uint32_t lastCardId = 0xFFFFFFFF; +uint16_t lastFacilityId = 0xFFFF; +uint8_t lastIssueLevel = 0xFF; +uint8_t lastRegionCode = 0xFF; +DESFireAidType selectedGallagherAID = {0xFF, 0xFF, 0xFF}; + + +//Defaults to the default gallagher site key +uint8_t GallagherSiteKey[16] = { + 0x31, 0x12, 0xB7, 0x38, 0xD8, 0x86, 0x2C, 0xCD, + 0x34, 0x30, 0x2E, 0xB2, 0x99, 0xAA, 0xB4, 0x56, +}; + +//Warning - running this function resets the AUTH state! +bool CreateGallagherCard(uint32_t cardId, uint16_t facilityId, uint8_t issueLevel, uint8_t regionCode) { + + //TODO: Find a suitable AID + DESFireAidType AID = {0xF4, 0x81, 0x20}; + + return CreateGallagherCardWithAID(cardId, facilityId, issueLevel, regionCode, AID); +} + +//Warning - running this function resets the AUTH state! +bool CreateGallagherCardWithAID(uint32_t cardId, uint16_t facilityId, uint8_t issueLevel, uint8_t regionCode, DESFireAidType AID) { + uint8_t Status = 0; + ConfigurationUidType UID_GALL; + GetPiccUid(UID_GALL); + InvalidateAuthState(false); + + //TODO: Is this needed? + Authenticated = true; + AuthenticatedWithKey = 0; + AuthenticatedWithPICCMasterKey = true; + + //Create card app directory app + DESFireAidType CADAid; + CADAid[0] = 0xF4; + CADAid[1] = 0x81; + CADAid[2] = 0x2F;//0x2F81F4 + uint8_t KeyCount = 1; + uint8_t KeySettings = 0x0B; + Status = CreateApp(CADAid, KeyCount, KeySettings); + + if (Status != STATUS_OPERATION_OK) { + DEBUG_PRINT_P(PSTR("Err: %u"), Status); + return false; + } + + //Difersify key for app directory app + uint8_t CADKeyZero[CRYPTO_AES_KEY_SIZE]; + hfgal_diversify_key(GallagherSiteKey, UID_GALL, 7, 0, CADAid, CADKeyZero); + + //Select the app direcory app + SelectApp(CADAid); + + //Channge key + uint8_t nextKeyVersion = ReadKeyVersion(SelectedApp.Slot, 0) + 1; + WriteAppKey(SelectedApp.Slot, 0, CADKeyZero, CRYPTO_AES_KEY_SIZE); + WriteKeyVersion(SelectedApp.Slot, 0, nextKeyVersion); + WriteKeyCryptoType(SelectedApp.Slot, 0, CRYPTO_TYPE_AES128); + + //Select the app direcory app + SelectApp(CADAid); + + //Create file in CAD + Status = CreateStandardFile(0x00, 0x00, 0xE000, CAD_BLOCK_LEN); + + if (Status != STATUS_OPERATION_OK) { + return false; + } + + //TODO: This needs to be rewritten to support more than one Gallagher app on the card + //Get CAD block to write + uint8_t CADBlock[CAD_BLOCK_LEN]; + CADBlock[0] = regionCode; + CADBlock[1] = (facilityId >> 8) & 0xFF; + CADBlock[2] = facilityId & 0xFF; + CADBlock[3] = AID[2]; + CADBlock[4] = AID[1]; + CADBlock[5] = AID[0]; + memset(CADBlock+6, 0, CAD_BLOCK_LEN-6); + + //Update file in CAD + uint8_t fileIndex = LookupFileNumberIndex(SelectedApp.Slot, 0); + uint8_t fileType = ReadFileType(SelectedApp.Slot, fileIndex); + Status = WriteDataFileSetup(fileIndex, fileType, 0x00, 0, CAD_BLOCK_LEN); + + if (Status != STATUS_OPERATION_OK) { + return false; + } + + Status = WriteDataFileIterator(CADBlock, CAD_BLOCK_LEN); + + if (Status != STATUS_OPERATION_OK) { + return false; + } + + return CreateGallagherAppWithAID(cardId, facilityId, issueLevel, regionCode, AID); +} + +void SetGallagherSiteKey(uint8_t* key) { + for (uint8_t i = 0; i < 16; ++i) { + GallagherSiteKey[i] = key[i]; + } +} + +void ResetGallagherSiteKey() { + uint8_t key[16] = { + 0x31, 0x12, 0xB7, 0x38, 0xD8, 0x86, 0x2C, 0xCD, + 0x34, 0x30, 0x2E, 0xB2, 0x99, 0xAA, 0xB4, 0x56, + }; + SetGallagherSiteKey(key); +} + +bool CreateGallagherAppWithAID(uint32_t cardId, uint16_t facilityId, uint8_t issueLevel, uint8_t regionCode, DESFireAidType AID) { + //Create Gall app + uint8_t KeyCount = 3; + uint8_t KeySettings = 0x0B; + uint8_t Status = CreateApp(AID, KeyCount, KeySettings); + + ConfigurationUidType UID_GALL; + GetPiccUid(UID_GALL); + + SelectApp(AID); + + //Diversify and change key 2 + uint8_t GallAppKeyTwo[CRYPTO_AES_KEY_SIZE]; + hfgal_diversify_key(GallagherSiteKey, UID_GALL, 7, 2, AID, GallAppKeyTwo); + + uint8_t nextKeyVersion = ReadKeyVersion(SelectedApp.Slot, 2) + 1; + WriteAppKey(SelectedApp.Slot, 2, GallAppKeyTwo, CRYPTO_AES_KEY_SIZE); + WriteKeyVersion(SelectedApp.Slot, 2, nextKeyVersion); + WriteKeyCryptoType(SelectedApp.Slot, 2, CRYPTO_TYPE_AES128); + + //Diversify and change key 0 + uint8_t GallAppKeyZero[CRYPTO_AES_KEY_SIZE]; + hfgal_diversify_key(GallagherSiteKey, UID_GALL, 7, 0, AID, GallAppKeyZero); + + nextKeyVersion = ReadKeyVersion(SelectedApp.Slot, 0) + 1; + WriteAppKey(SelectedApp.Slot, 0, GallAppKeyZero, CRYPTO_AES_KEY_SIZE); + WriteKeyVersion(SelectedApp.Slot, 0, nextKeyVersion); + WriteKeyCryptoType(SelectedApp.Slot, 0, CRYPTO_TYPE_AES128); + + //Create file + Status = CreateStandardFile(0x00, 0x03, 0x2000, GALL_BLOCK_LEN); + + if (Status != STATUS_OPERATION_OK) { + DEBUG_PRINT_P(PSTR("Err: %u"), Status); + return false; + } + + selectedGallagherAID[0] = AID[0]; + selectedGallagherAID[1] = AID[1]; + selectedGallagherAID[2] = AID[2]; + + return UpdateGallagherFile(cardId, facilityId, issueLevel, regionCode, AID); + +} + +bool UpdateGallagherFile(uint32_t cardId, uint16_t facilityId, uint8_t issueLevel, uint8_t regionCode, DESFireAidType AID) { + SelectApp(AID); + + //Get Gallagher block to write + uint8_t GallBlock[GALL_BLOCK_LEN]; + gallagher_encode_creds(GallBlock, regionCode, facilityId, cardId, issueLevel); + for (int i = 0; i < 8; i++) { + GallBlock[i + 8] = GallBlock[i] ^ 0xFF; + } + + //Update file with Gall access data + uint8_t fileIndex = LookupFileNumberIndex(SelectedApp.Slot, 0); + uint8_t fileType = ReadFileType(SelectedApp.Slot, fileIndex); + uint8_t Status = WriteDataFileSetup(fileIndex, fileType, 0x03, 0, GALL_BLOCK_LEN); + + if (Status != STATUS_OPERATION_OK) { + return false; + } + + Status = WriteDataFileIterator(GallBlock, GALL_BLOCK_LEN); + + if (Status != STATUS_OPERATION_OK) { + return false; + } + + lastCardId = cardId; + lastFacilityId = facilityId; + lastIssueLevel = issueLevel; + lastRegionCode = regionCode; + + return true; +} + +bool UpdateGallagherAppCardID(uint32_t cardId) { + if ((lastCardId == 0xFFFFFFFF && lastFacilityId == 0xFFFF && lastIssueLevel == 0xFF && lastRegionCode == 0xFF) || + (selectedGallagherAID[0] == 0xFF && selectedGallagherAID[1] == 0xFF && selectedGallagherAID[2] == 0xFF)) { + return false; + } + + return UpdateGallagherFile(cardId, lastFacilityId, lastIssueLevel, lastRegionCode, selectedGallagherAID); +} + +void SelectGallagherAID(DESFireAidType AID) { + selectedGallagherAID[0] = AID[0]; + selectedGallagherAID[1] = AID[1]; + selectedGallagherAID[2] = AID[2]; +} diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireGallagher.h b/Firmware/Chameleon-Mini/Application/DESFire/DESFireGallagher.h new file mode 100644 index 00000000..df044b76 --- /dev/null +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireGallagher.h @@ -0,0 +1,45 @@ +/* +This file from this firmware source +is free software written by Tomas Preucil (github.com/tomaspre): +You can redistribute it and/or modify +it under the terms of this license. + +This software is intended for demonstration and testing purposes on your own hardware only. +When setting up a Gallagher system, always use a non-default site key! + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +This notice must be retained at the top of all source files where indicated. +*/ + +/* + * DESFireGallagher.h + * Tomas Preucil (github.com/tomaspre) + */ + +#ifndef CHAMELEON_MINI_DESFIREGALLAGHER_H +#define CHAMELEON_MINI_DESFIREGALLAGHER_H + +#include "DESFireCrypto.h" +#include "DESFireApplicationDirectory.h" +#include "Configuration.h" +#include "DESFirePICCControl.h" +#include "DESFireStatusCodes.h" + +//Warning - running this function resets the AUTH state! +bool CreateGallagherCard(uint32_t cardId, uint16_t facilityId, uint8_t issueLevel, uint8_t regionCode); + +//Warning - running this function resets the AUTH state! +bool CreateGallagherCardWithAID(uint32_t cardId, uint16_t facilityId, uint8_t issueLevel, uint8_t regionCode, uint8_t* AID); + +bool CreateGallagherAppWithAID(uint32_t cardId, uint16_t facilityId, uint8_t issueLevel, uint8_t regionCode, DESFireAidType AID); +bool UpdateGallagherFile(uint32_t cardId, uint16_t facilityId, uint8_t issueLevel, uint8_t regionCode, DESFireAidType AID); +bool UpdateGallagherAppCardID(uint32_t cardId); +void SelectGallagherAID(DESFireAidType AID); + +void SetGallagherSiteKey(uint8_t* key); +void ResetGallagherSiteKey(); + +#endif //CHAMELEON_MINI_DESFIREGALLAGHER_H diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireGallagherTools.c b/Firmware/Chameleon-Mini/Application/DESFire/DESFireGallagherTools.c new file mode 100644 index 00000000..e6ad9566 --- /dev/null +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireGallagherTools.c @@ -0,0 +1,188 @@ +// This file contains adapted Gallagher functions from https://github.com/RfidResearchGroup/proxmark3 +// All credit goes to this repo + +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md @ https://github.com/RfidResearchGroup/proxmark3 for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt @ https://github.com/RfidResearchGroup/proxmark3 for the text of the license. + +#include +#include "DESFireGallagherTools.h" +#include "DESFireLogging.h" +#include "DESFireUtils.h" + +// This function is almot like cmac(...). but with some key differences. +bool MifareKdfAn10922(uint8_t *key, uint8_t CryptoType, + const uint8_t *data, size_t len, uint8_t *diversified_key) { + if (data == NULL || len < 1 || len > 31) + return false; + + uint8_t kbs; + switch (CryptoType) { + case CRYPTO_TYPE_AES128: + kbs = CRYPTO_AES_BLOCK_SIZE; + break; + case CRYPTO_TYPE_2KTDEA: + kbs = CRYPTO_2KTDEA_BLOCK_SIZE; + break; + case CRYPTO_TYPE_3K3DES: + kbs = CRYPTO_3KTDEA_BLOCK_SIZE; + break; + default: + return false; + } + + uint8_t buffer[96] = {0}; + + if (CryptoType == CRYPTO_TYPE_AES128) { + buffer[0] = 0x01; + memcpy(&buffer[1], data, len); + + uint8_t IV[CRYPTO_AES_BLOCK_SIZE]; + memset(IV, 0, CRYPTO_AES_BLOCK_SIZE); + DesfireCryptoCMACEx(CryptoType, key, buffer, len + 1, IV, diversified_key, 2*kbs); + + return true; + } else if (CryptoType == CRYPTO_TYPE_2KTDEA) { + /* TODO: Adapt this code + buffer[0] = 0x21; + memcpy(&buffer[1], data, len); + + DesfireClearIV(ctx); + DesfireCryptoCMACEx(ctx, key_type, buffer, len + 1, kbs * 2, cmac); + + buffer[0] = 0x22; + memcpy(&buffer[1], data, len); + + DesfireClearIV(ctx); + DesfireCryptoCMACEx(ctx, key_type, buffer, len + 1, kbs * 2, &cmac[kbs]); + + memcpy(ctx->key, cmac, kbs * 2);*/ + } else if (CryptoType == CRYPTO_TYPE_3K3DES) { + /* TODO: Adapt this code + buffer[0] = 0x31; + memcpy(&buffer[1], data, len); + + DesfireClearIV(ctx); + DesfireCryptoCMACEx(ctx, key_type, buffer, len + 1, kbs * 2, cmac); + + buffer[0] = 0x32; + memcpy(&buffer[1], data, len); + + DesfireClearIV(ctx); + DesfireCryptoCMACEx(ctx, key_type, buffer, len + 1, kbs * 2, &cmac[kbs]); + + buffer[0] = 0x33; + memcpy(&buffer[1], data, len); + + DesfireClearIV(ctx); + DesfireCryptoCMACEx(ctx, key_type, buffer, len + 1, kbs * 2, &cmac[kbs * 2]); + + memcpy(ctx->key, cmac, kbs * 3);*/ + } + return false; +} + +uint8_t mfdes_kdf_input_gallagher(uint8_t *uid, uint8_t uidLen, uint8_t keyNo, + DESFireAidType aid, uint8_t *kdfInputOut, uint8_t *kdfInputLen) { + if (uid == NULL || (uidLen != 4 && uidLen != 7) || keyNo > 2 || kdfInputOut == NULL || kdfInputLen == NULL) { + return 1; + } + + int len = 0; + // If the keyNo == 1 or the aid is 000000, then omit the UID. + // On the other hand, if the aid is 1f81f4 (config card) always include the UID. + if ((keyNo != 1 && aid != 0x000000) || (aid[0] == 0x1f && aid[1] == 0x81 && aid[2] == 0xf4)) { + if (*kdfInputLen < (4 + uidLen)) { + return 2; + } + + memcpy(kdfInputOut, uid, uidLen); + len += uidLen; + } else if (*kdfInputLen < 4) { + return 4; + } + + kdfInputOut[len++] = keyNo; + + kdfInputOut[len++] = aid[0]; + kdfInputOut[len++] = aid[1]; + kdfInputOut[len++] = aid[2]; + + *kdfInputLen = len; + + return 0; +} + +bool hfgal_diversify_key(uint8_t *site_key, uint8_t *uid, uint8_t uid_len, + uint8_t key_num, DESFireAidType aid, uint8_t *key_output) { + // Generate diversification input + uint8_t kdf_input_len = 11; + uint32_t res = mfdes_kdf_input_gallagher(uid, uid_len, key_num, aid, key_output, &kdf_input_len); + if (res) { + return false; + } + + uint8_t key[CRYPTO_AES_KEY_SIZE]; + if (site_key == NULL) { + return false; + } + memcpy(key, site_key, sizeof(key)); + + // Diversify input & copy to output buffer + uint8_t diversified_key[CRYPTO_AES_KEY_SIZE]; + bool ret = MifareKdfAn10922(key, CRYPTO_TYPE_AES128, key_output, kdf_input_len, diversified_key); + memcpy(key_output, diversified_key, CRYPTO_AES_KEY_SIZE); + + return ret; +} + +void scramble(uint8_t *arr, uint8_t len) { + const uint8_t lut[] = { + 0xa3, 0xb0, 0x80, 0xc6, 0xb2, 0xf4, 0x5c, 0x6c, 0x81, 0xf1, 0xbb, 0xeb, 0x55, 0x67, 0x3c, 0x05, + 0x1a, 0x0e, 0x61, 0xf6, 0x22, 0xce, 0xaa, 0x8f, 0xbd, 0x3b, 0x1f, 0x5e, 0x44, 0x04, 0x51, 0x2e, + 0x4d, 0x9a, 0x84, 0xea, 0xf8, 0x66, 0x74, 0x29, 0x7f, 0x70, 0xd8, 0x31, 0x7a, 0x6d, 0xa4, 0x00, + 0x82, 0xb9, 0x5f, 0xb4, 0x16, 0xab, 0xff, 0xc2, 0x39, 0xdc, 0x19, 0x65, 0x57, 0x7c, 0x20, 0xfa, + 0x5a, 0x49, 0x13, 0xd0, 0xfb, 0xa8, 0x91, 0x73, 0xb1, 0x33, 0x18, 0xbe, 0x21, 0x72, 0x48, 0xb6, + 0xdb, 0xa0, 0x5d, 0xcc, 0xe6, 0x17, 0x27, 0xe5, 0xd4, 0x53, 0x42, 0xf3, 0xdd, 0x7b, 0x24, 0xac, + 0x2b, 0x58, 0x1e, 0xa7, 0xe7, 0x86, 0x40, 0xd3, 0x98, 0x97, 0x71, 0xcb, 0x3a, 0x0f, 0x01, 0x9b, + 0x6e, 0x1b, 0xfc, 0x34, 0xa6, 0xda, 0x07, 0x0c, 0xae, 0x37, 0xca, 0x54, 0xfd, 0x26, 0xfe, 0x0a, + 0x45, 0xa2, 0x2a, 0xc4, 0x12, 0x0d, 0xf5, 0x4f, 0x69, 0xe0, 0x8a, 0x77, 0x60, 0x3f, 0x99, 0x95, + 0xd2, 0x38, 0x36, 0x62, 0xb7, 0x32, 0x7e, 0x79, 0xc0, 0x46, 0x93, 0x2f, 0xa5, 0xba, 0x5b, 0xaf, + 0x52, 0x1d, 0xc3, 0x75, 0xcf, 0xd6, 0x4c, 0x83, 0xe8, 0x3d, 0x30, 0x4e, 0xbc, 0x08, 0x2d, 0x09, + 0x06, 0xd9, 0x25, 0x9e, 0x89, 0xf2, 0x96, 0x88, 0xc1, 0x8c, 0x94, 0x0b, 0x28, 0xf0, 0x47, 0x63, + 0xd5, 0xb3, 0x68, 0x56, 0x9c, 0xf9, 0x6f, 0x41, 0x50, 0x85, 0x8b, 0x9d, 0x59, 0xbf, 0x9f, 0xe2, + 0x8e, 0x6a, 0x11, 0x23, 0xa1, 0xcd, 0xb5, 0x7d, 0xc7, 0xa9, 0xc8, 0xef, 0xdf, 0x02, 0xb8, 0x03, + 0x6b, 0x35, 0x3e, 0x2c, 0x76, 0xc9, 0xde, 0x1c, 0x4b, 0xd1, 0xed, 0x14, 0xc5, 0xad, 0xe9, 0x64, + 0x4a, 0xec, 0x8d, 0xf7, 0x10, 0x43, 0x78, 0x15, 0x87, 0xe4, 0xd7, 0x92, 0xe1, 0xee, 0xe3, 0x90 + }; + + for (int i = 0; i < len; i++) { + arr[i] = lut[arr[i]]; + } +} + +void gallagher_encode_creds(uint8_t *eight_bytes, uint8_t rc, uint16_t fc, uint32_t cn, uint8_t il) { + // put data into the correct places (Gallagher obfuscation) + eight_bytes[0] = (cn & 0xffffff) >> 16; + eight_bytes[1] = (fc & 0xfff) >> 4; + eight_bytes[2] = (cn & 0x7ff) >> 3; + eight_bytes[3] = (cn & 0x7) << 5 | (rc & 0xf) << 1; + eight_bytes[4] = (cn & 0xffff) >> 11; + eight_bytes[5] = (fc & 0xffff) >> 12; + eight_bytes[6] = 0; + eight_bytes[7] = (fc & 0xf) << 4 | (il & 0xf); + + // more obfuscation + scramble(eight_bytes, 8); +} diff --git a/Firmware/Chameleon-Mini/Application/DESFire/DESFireGallagherTools.h b/Firmware/Chameleon-Mini/Application/DESFire/DESFireGallagherTools.h new file mode 100644 index 00000000..a9e019a1 --- /dev/null +++ b/Firmware/Chameleon-Mini/Application/DESFire/DESFireGallagherTools.h @@ -0,0 +1,35 @@ +// This file contains adapted Gallagher functions from https://github.com/RfidResearchGroup/proxmark3 +// All credit goes to this repo + +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md @ https://github.com/RfidResearchGroup/proxmark3 for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt @ https://github.com/RfidResearchGroup/proxmark3 for the text of the license. + +#ifndef CHAMELEON_MINI_DESFIREGALLAGHERTOOLS_H +#define CHAMELEON_MINI_DESFIREGALLAGHERTOOLS_H + +#include "DESFireCrypto.h" +#include "DESFireApplicationDirectory.h" +#include "Configuration.h" +#include "DESFirePICCControl.h" +#include "DESFireStatusCodes.h" + +//bool MifareKdfAn10922(uint8_t *key, uint8_t CryptoType, const uint8_t *data, size_t len, uint8_t *diversified_key); +//uint8_t mfdes_kdf_input_gallagher(uint8_t *uid, uint8_t uidLen, uint8_t keyNo, uint32_t aid, uint8_t *kdfInputOut, uint8_t *kdfInputLen); +bool hfgal_diversify_key(uint8_t *site_key, uint8_t *uid, uint8_t uid_len, uint8_t key_num, DESFireAidType aid, uint8_t *key_output); + +//void scramble(uint8_t *arr, uint8_t len); +void gallagher_encode_creds(uint8_t *eight_bytes, uint8_t rc, uint16_t fc, uint32_t cn, uint8_t il); + +#endif //CHAMELEON_MINI_DESFIREGALLAGHERTOOLS_H