2021-12-29 18:42:06 +08:00
/**
* Matt Moran ( @ DarkMatterMatt ) , 2021
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* This program is free software : you can redistribute it and / or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation , either version 3 of the License , or ( at your
* option ) any later version .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* High frequency GALLAGHER tag commands .
* MIFARE DESFire , AIDs 2081F 4 - 2F 81F 4
*/
# include "cmdhfgallagher.h"
2022-01-03 12:02:10 +08:00
# include "generator.h"
2022-01-01 17:22:57 +08:00
# include "mifare.h"
# include "mifare/desfirecore.h"
2021-12-29 18:42:06 +08:00
# include "mifare/gallaghercore.h"
# include <stdio.h>
2022-01-01 17:22:57 +08:00
# include <string.h>
2021-12-29 18:42:06 +08:00
# include "common.h"
2022-01-01 17:22:57 +08:00
# include "commonutil.h"
2021-12-29 18:42:06 +08:00
# include "cmdparser.h"
# include "cliparser.h"
# include "ui.h"
2022-01-04 12:03:22 +08:00
/** Application ID for the Gallagher Card Application Directory */
static const uint32_t CAD_AID = 0x2F81F4 ;
2022-01-04 11:59:59 +08:00
/**
* @ brief Reverses the bytes in aid . Used when encoding / decoding Card Application Directory entries .
*/
2022-01-01 17:22:57 +08:00
static void reverseAid ( uint8_t * aid ) {
uint8_t tmp = aid [ 0 ] ;
aid [ 0 ] = aid [ 2 ] ;
aid [ 2 ] = tmp ;
} ;
2021-12-29 18:42:06 +08:00
static int CmdHelp ( const char * Cmd ) ;
2022-01-04 11:59:59 +08:00
/**
* @ brief Read Gallagher Card Application Directory from card .
*
* @ param destBuf Buffer to copy Card Application Directory into .
* @ param destBufLen Size of destBuf . Must be at least 108 bytes .
* @ param numEntries Will be set to the number of entries in the Card Application Directory .
*/
2022-01-01 17:22:57 +08:00
static int readCardApplicationDirectory ( DesfireContext_t * ctx , uint8_t * destBuf , uint8_t destBufLen , uint8_t * numEntries , bool verbose ) {
2022-01-04 11:59:59 +08:00
if ( destBufLen < 3 * 36 ) {
2022-01-01 17:22:57 +08:00
PrintAndLogEx ( ERR , " readCardApplicationDirectory destination buffer is incorrectly sized. "
2022-01-04 11:59:59 +08:00
" Received length %d, must be at least %d " , destBufLen , 3 * 36 ) ;
2022-01-01 17:22:57 +08:00
return PM3_EINVARG ;
}
// Get card AIDs from Card Application Directory (which contains 1 to 3 files)
2022-01-04 11:59:59 +08:00
DesfireSetCommMode ( ctx , DCMPlain ) ;
2022-01-04 12:03:22 +08:00
int res = DesfireSelectAIDHex ( ctx , CAD_AID , false , 0 ) ;
2022-01-04 11:59:59 +08:00
HFGAL_RET_IF_ERR_WITH_MSG ( res , " Failed selecting Card Application Directory, does AID F4812F exist? " ) ;
2022-01-01 17:22:57 +08:00
// Read up to 3 files with 6x 6-byte entries each
for ( uint8_t i = 0 ; i < 3 ; i + + ) {
size_t readLen ;
2022-01-04 11:59:59 +08:00
res = DesfireReadFile ( ctx , i , 0 , 36 , & destBuf [ i * 36 ] , & readLen ) ;
if ( res ! = PM3_SUCCESS )
PrintAndLogEx ( WARNING , " Failed reading file %d in Card Application Directory (AID F4812F) " , i ) ;
2022-01-03 12:02:10 +08:00
// end if the last entry is NULL
if ( memcmp ( & destBuf [ 36 * i + 30 ] , " \0 \0 \0 \0 \0 \0 " , 6 ) = = 0 ) break ;
2022-01-01 17:22:57 +08:00
}
2022-01-04 11:59:59 +08:00
// Count number of entries (i.e. count until we hit a NULL entry)
2022-01-01 17:22:57 +08:00
* numEntries = 0 ;
for ( uint8_t i = 0 ; i < destBufLen ; i + = 6 ) {
if ( memcmp ( & destBuf [ i ] , " \0 \0 \0 \0 \0 \0 " , 6 ) = = 0 ) break ;
* numEntries + = 1 ;
}
if ( verbose ) {
// Print what we found
PrintAndLogEx ( SUCCESS , " Card Application Directory contains: " NOLF ) ;
for ( int i = 0 ; i < * numEntries ; i + + )
2022-01-04 11:59:59 +08:00
PrintAndLogEx ( NORMAL , " %s %06X " NOLF , ( i = = 0 ) ? " " : " , " , DesfireAIDByteToUint ( & destBuf [ i * 6 + 3 ] ) ) ;
2022-01-01 17:22:57 +08:00
PrintAndLogEx ( NORMAL , " " ) ;
}
return PM3_SUCCESS ;
}
2022-01-04 11:59:59 +08:00
/**
* @ brief Read credentials from a single AID .
*
* @ param aid Application ID to read .
* @ param sitekey MIFARE site key .
* @ param creds Decoded credentials will be stored in this structure .
*/
static int readCardApplicationCredentials ( DesfireContext_t * ctx , uint32_t aid , uint8_t * sitekey , GallagherCredentials_t * creds , bool verbose ) {
2022-01-01 17:22:57 +08:00
// Check that card UID has been set
2022-01-04 11:59:59 +08:00
if ( ctx - > uidlen = = 0 )
HFGAL_RET_ERR ( PM3_EINVARG , " Card UID must be set in DesfireContext (required for key diversification) " ) ;
2022-01-01 17:22:57 +08:00
// Set up context
DesfireSetKeyNoClear ( ctx , 2 , T_AES , sitekey ) ;
DesfireSetKdf ( ctx , MFDES_KDF_ALGO_GALLAGHER , NULL , 0 ) ;
DesfireSetCommMode ( ctx , DCMPlain ) ;
// Select and authenticate to AID
2022-01-04 11:59:59 +08:00
int res = DesfireSelectAndAuthenticateAppW ( ctx , DACEV1 , ISW6bAID , aid , false , verbose ) ;
HFGAL_RET_IF_ERR_WITH_MSG ( res , " Failed selecting/authenticating to AID %06X " , aid ) ;
2022-01-01 17:22:57 +08:00
// Read file 0 (contains credentials)
2022-01-03 12:02:10 +08:00
uint8_t buf [ 16 ] = { 0 } ;
2022-01-01 17:22:57 +08:00
size_t readLen = 0 ;
DesfireSetCommMode ( ctx , DCMEncrypted ) ;
res = DesfireReadFile ( ctx , 0 , 0 , 16 , buf , & readLen ) ;
2022-01-04 11:59:59 +08:00
HFGAL_RET_IF_ERR_WITH_MSG ( res , " Failed reading file 0 in AID %06X " , aid ) ;
2022-01-01 17:22:57 +08:00
// Check file contained 16 bytes of data
if ( readLen ! = 16 ) {
2022-01-04 11:59:59 +08:00
HFGAL_RET_ERR ( PM3_EFAILED , " Failed reading file 0 in AID %06X, expected 16 bytes but received %d bytes " , aid , readLen ) ;
2022-01-01 17:22:57 +08:00
}
// Check second half of file is the bitwise inverse of the first half
for ( uint8_t i = 8 ; i < 16 ; i + + )
buf [ i ] ^ = 0xFF ;
if ( memcmp ( buf , & buf [ 8 ] , 8 ) ! = 0 ) {
2022-01-04 11:59:59 +08:00
HFGAL_RET_ERR ( PM3_EFAILED , " Invalid cardholder data in file 0 in AID %06X. Received %s " , sprint_hex_inrow ( buf , 16 ) ) ;
2022-01-01 17:22:57 +08:00
}
decodeCardholderCredentials ( buf , creds ) ;
// TODO: read MIFARE Enhanced Security file
// https://github.com/megabug/gallagher-research/blob/master/formats/mes.md
return PM3_SUCCESS ;
}
2022-01-04 11:59:59 +08:00
/**
* @ brief Read credentials from a Gallagher card .
*
* @ param aid Application ID to read . If 0 , then the Card Application Directory will be queried and all entries will be read .
* @ param sitekey MIFARE site key .
* @ param quiet Suppress error messages . Used when in continuous reader mode .
*/
static int readCard ( uint32_t aid , uint8_t * sitekey , bool verbose , bool quiet ) {
2022-01-01 17:22:57 +08:00
DropField ( ) ;
clearCommandBuffer ( ) ;
// Set up context
DesfireContext_t dctx = { 0 } ;
DesfireClearContext ( & dctx ) ;
// Get card UID (for key diversification)
int res = DesfireGetCardUID ( & dctx ) ;
2022-01-04 11:59:59 +08:00
HFGAL_RET_IF_ERR_MAYBE_MSG ( res , ! quiet , " Failed retrieving card UID. " ) ;
2022-01-01 17:22:57 +08:00
// Find AIDs to process (from CLI args or the Card Application Directory)
uint8_t cad [ 36 * 3 ] = { 0 } ;
uint8_t numEntries = 0 ;
2022-01-04 11:59:59 +08:00
if ( aid ! = 0 ) {
DesfireAIDUintToByte ( aid , & cad [ 3 ] ) ;
2022-01-01 17:22:57 +08:00
reverseAid ( & cad [ 3 ] ) ; // CAD stores AIDs backwards
numEntries = 1 ;
} else {
res = readCardApplicationDirectory ( & dctx , cad , ARRAYLEN ( cad ) , & numEntries , verbose ) ;
2022-01-04 11:59:59 +08:00
HFGAL_RET_IF_ERR_MAYBE_MSG ( res , ! quiet , " Failed reading card application directory. " ) ;
2022-01-01 17:22:57 +08:00
}
// Loop through each application in the CAD
for ( uint8_t i = 0 ; i < numEntries * 6 ; i + = 6 ) {
2022-01-04 11:59:59 +08:00
uint16_t regionCode = cad [ i + 0 ] ;
uint16_t facilityCode = ( cad [ i + 1 ] < < 8 ) + cad [ i + 2 ] ;
2022-01-01 17:22:57 +08:00
// Copy AID out of CAD record
2022-01-04 11:59:59 +08:00
uint8_t currentAidBuf [ 3 ] ;
memcpy ( currentAidBuf , & cad [ 3 ] , 3 ) ;
reverseAid ( currentAidBuf ) ; // CAD stores AIDs backwards
uint32_t currentAid = DesfireAIDByteToUint ( currentAidBuf ) ;
2022-01-01 17:22:57 +08:00
if ( verbose ) {
2022-01-04 11:59:59 +08:00
if ( regionCode > 0 | | facilityCode > 0 )
PrintAndLogEx ( INFO , " Reading AID: %06X, region: %u, facility: %u " , currentAid , regionCode , facilityCode ) ;
2022-01-01 17:22:57 +08:00
else
2022-01-04 11:59:59 +08:00
PrintAndLogEx ( INFO , " Reading AID: %06X " , currentAid ) ;
2022-01-01 17:22:57 +08:00
}
// Read & decode credentials
GallagherCredentials_t creds = { 0 } ;
res = readCardApplicationCredentials ( & dctx , currentAid , sitekey , & creds , verbose ) ;
2022-01-04 11:59:59 +08:00
HFGAL_RET_IF_ERR_MAYBE_MSG ( res , ! quiet , " Failed reading card application credentials. " ) ;
2022-01-01 17:22:57 +08:00
PrintAndLogEx ( SUCCESS , " GALLAGHER - Region: " _GREEN_ ( " %u " ) " , Facility: " _GREEN_ ( " %u " ) " , Card No.: " _GREEN_ ( " %u " ) " , Issue Level: " _GREEN_ ( " %u " ) ,
creds . region_code , creds . facility_code , creds . card_number , creds . issue_level ) ;
}
return PM3_SUCCESS ;
}
2021-12-29 18:42:06 +08:00
static int CmdGallagherReader ( const char * Cmd ) {
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " hf gallagher reader " ,
" read a GALLAGHER tag " ,
2022-01-01 17:22:57 +08:00
" hf gallagher reader --aid 2081f4 --sitekey 00112233445566778899aabbccddeeff "
" -> act as a reader that doesn't skips the Card Application Directory and uses a non-default site key \n "
" hf gallagher reader -@ -> continuous reader mode "
2021-12-29 18:42:06 +08:00
) ;
void * argtable [ ] = {
arg_param_begin ,
2022-01-01 17:22:57 +08:00
arg_str0 ( NULL , " aid " , " <hex> " , " Application ID to read (3 bytes) " ) ,
arg_str1 ( " k " , " sitekey " , " <hex> " , " Master site key to compute diversified keys (16 bytes) " ) ,
arg_lit0 ( NULL , " apdu " , " show APDU requests and responses " ) ,
arg_lit0 ( " v " , " verbose " , " Verbose mode " ) ,
arg_lit0 ( " @ " , " continuous " , " Continuous reader mode " ) ,
2021-12-29 18:42:06 +08:00
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , true ) ;
2022-01-01 17:22:57 +08:00
int aidLen = 0 ;
2022-01-04 11:59:59 +08:00
uint8_t aidBuf [ 3 ] = { 0 } ;
CLIGetHexWithReturn ( ctx , 1 , aidBuf , & aidLen ) ;
if ( aidLen > 0 & & aidLen ! = 3 )
HFGAL_RET_ERR ( PM3_EINVARG , " --aid must be 3 bytes " ) ;
reverseAid ( aidBuf ) ; // PM3 displays AIDs backwards
uint32_t aid = DesfireAIDByteToUint ( aidBuf ) ;
2022-01-01 17:22:57 +08:00
int sitekeyLen = 0 ;
uint8_t sitekey [ 16 ] = { 0 } ;
CLIGetHexWithReturn ( ctx , 2 , sitekey , & sitekeyLen ) ;
2022-01-04 11:59:59 +08:00
if ( sitekeyLen > 0 & & sitekeyLen ! = 16 )
HFGAL_RET_ERR ( PM3_EINVARG , " --sitekey must be 16 bytes " ) ;
2022-01-01 17:22:57 +08:00
SetAPDULogging ( arg_get_lit ( ctx , 3 ) ) ;
bool verbose = arg_get_lit ( ctx , 4 ) ;
bool continuousMode = arg_get_lit ( ctx , 5 ) ;
2021-12-29 18:42:06 +08:00
CLIParserFree ( ctx ) ;
2022-01-04 11:59:59 +08:00
if ( ! continuousMode ) {
// Read single card
return readCard ( aid , sitekey , verbose , false ) ;
}
2022-01-01 17:22:57 +08:00
2022-01-04 11:59:59 +08:00
// Loop until <Enter> is pressed
PrintAndLogEx ( INFO , " Press " _GREEN_ ( " <Enter> " ) " to exit " ) ;
while ( ! kbd_enter_pressed ( ) ) {
readCard ( aid , sitekey , verbose , ! verbose ) ;
}
return PM3_SUCCESS ;
2021-12-29 18:42:06 +08:00
}
2022-01-03 12:02:10 +08:00
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 ) ;
2022-01-04 11:59:59 +08:00
HFGAL_RET_IF_ERR_WITH_MSG ( res , " Failed generating Gallagher key diversification input. " ) ;
2022-01-03 12:02:10 +08:00
// 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 ;
}
2022-01-04 11:59:59 +08:00
/**
* @ brief Create a new application to store Gallagher cardholder credentials .
*
* @ param sitekey MIFARE site key .
* @ param aid New application ID . Should be 0x2 ? 81F 4 , where 0 < = ? < = 0xB .
*/
static int createGallagherCredentialsApplication ( DesfireContext_t * ctx , uint8_t * sitekey , uint32_t aid , bool verbose ) {
2022-01-03 12:02:10 +08:00
DesfireSetCommMode ( ctx , DCMPlain ) ;
DesfireSetCommandSet ( ctx , DCCNativeISO ) ;
int res = DesfireSelectAndAuthenticateAppW ( ctx , DACEV1 , ISW6bAID , 0x000000 , false , verbose ) ;
2022-01-04 11:59:59 +08:00
HFGAL_RET_IF_ERR ( res ) ;
// UID is required for key diversification
if ( ctx - > uidlen = = 0 )
HFGAL_RET_ERR ( PM3_EINVARG , " UID is required for key diversification. Please fetch it before calling `createGallagherCredentialsApplication`. " ) ;
2022-01-03 12:02:10 +08:00
// 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 } ;
2022-01-04 11:59:59 +08:00
DesfireAIDUintToByte ( aid , & data [ 0 ] ) ;
2022-01-03 12:02:10 +08:00
data [ 3 ] = ks1 ;
data [ 4 ] = ks2 ;
DesfireSetCommMode ( ctx , DCMMACed ) ;
res = DesfireCreateApplication ( ctx , data , ARRAYLEN ( data ) ) ;
2022-01-04 11:59:59 +08:00
HFGAL_RET_IF_ERR_WITH_MSG ( res , " Failed creating application %06X. Does it already exist? " , aid ) ;
if ( verbose )
PrintAndLogEx ( INFO , " Created application %06X (current has empty contents & blank keys) " , aid ) ;
2022-01-03 12:02:10 +08:00
// Select the new application
DesfireSetCommMode ( ctx , DCMPlain ) ;
2022-01-04 11:59:59 +08:00
res = DesfireSelectEx ( ctx , true , ISW6bAID , aid , NULL ) ;
HFGAL_RET_IF_ERR_WITH_MSG ( res , " Failed selecting application %06X " , aid ) ;
2022-01-03 12:02:10 +08:00
// 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 } ;
2022-01-04 11:59:59 +08:00
res = GallagherDiversifyKey ( sitekey , ctx - > uid , ctx - > uidlen , i , aid , buf ) ;
HFGAL_RET_IF_ERR_WITH_MSG ( res , " Failed diversifying key %d for AID %06X " , i , aid ) ;
2022-01-03 12:02:10 +08:00
2022-01-04 11:59:59 +08:00
PrintAndLogEx ( INFO , " Diversified key %d for AID %06X: " _GREEN_ ( " %s " ) , i , aid , sprint_hex_inrow ( buf , ARRAYLEN ( buf ) ) ) ;
2022-01-03 12:02:10 +08:00
// Authenticate
2022-01-04 11:59:59 +08:00
uint8_t blankKey [ CRYPTO_AES128_KEY_SIZE ] = { 0 } ;
2022-01-03 12:02:10 +08:00
DesfireSetKeyNoClear ( ctx , 0 , T_AES , blankKey ) ;
DesfireSetCommMode ( ctx , DCMPlain ) ;
res = DesfireAuthenticate ( ctx , DACEV1 , verbose ) ;
2022-01-04 11:59:59 +08:00
HFGAL_RET_IF_ERR_WITH_MSG ( res , " Desfire authenticate error. Result: [%d] %s " , res , DesfireAuthErrorToStr ( res ) ) ;
2022-01-03 12:02:10 +08:00
// Change key
DesfireSetCommMode ( ctx , DCMEncryptedPlain ) ;
2022-01-04 11:59:59 +08:00
res = DesfireChangeKey ( ctx , false , i , dstalgo , 1 , buf , dstalgo , blankKey , verbose ) ;
HFGAL_RET_IF_ERR_WITH_MSG ( res , " Failed setting key %d for AID %06X " , i , aid ) ;
2022-01-03 12:02:10 +08:00
if ( verbose )
2022-01-04 11:59:59 +08:00
PrintAndLogEx ( INFO , " Successfully set key %d for AID %06X " , i , aid ) ;
2022-01-03 12:02:10 +08:00
}
2022-01-04 11:59:59 +08:00
PrintAndLogEx ( INFO , " Successfully created credentials application %06X " , aid ) ;
2022-01-03 12:02:10 +08:00
return PM3_SUCCESS ;
}
2022-01-04 11:59:59 +08:00
/**
* @ brief Create a new file containing Gallagher cardholder credentials .
*
* @ param sitekey MIFARE site key .
* @ param aid Application ID to put the new file in .
* @ param creds Gallagher cardholder credentials .
*/
static int createGallagherCredentialsFile ( DesfireContext_t * ctx , uint8_t * sitekey , uint32_t aid , GallagherCredentials_t * creds , bool verbose ) {
2022-01-03 12:02:10 +08:00
// Set up context
DesfireSetKeyNoClear ( ctx , 0 , T_AES , sitekey ) ;
DesfireSetKdf ( ctx , MFDES_KDF_ALGO_GALLAGHER , NULL , 0 ) ;
DesfireSetCommMode ( ctx , DCMPlain ) ;
// Select application
2022-01-04 11:59:59 +08:00
int res = DesfireSelectAndAuthenticateAppW ( ctx , DACEV1 , ISW6bAID , aid , false , verbose ) ;
HFGAL_RET_IF_ERR ( res ) ;
2022-01-03 12:02:10 +08:00
// 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 ) ;
2022-01-04 11:59:59 +08:00
HFGAL_RET_IF_ERR_WITH_MSG ( res , " Failed creating file 0 in AID %06X " , aid ) ;
if ( verbose )
PrintAndLogEx ( INFO , " Created file 0 in AID %06X (current has empty contents) " , aid ) ;
2022-01-03 12:02:10 +08:00
// 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 ) ;
2022-01-04 11:59:59 +08:00
HFGAL_RET_IF_ERR_WITH_MSG ( res , " Failed writing data to file 0 in AID %06X " ) ;
PrintAndLogEx ( INFO , " Successfully wrote cardholder credentials to file 0 in AID %06X " , aid ) ;
2022-01-03 12:02:10 +08:00
return PM3_SUCCESS ;
}
2022-01-04 11:59:59 +08:00
/**
* @ brief Create the Gallagher Card Application Directory .
*
* @ param sitekey MIFARE site key .
*/
2022-01-03 12:02:10 +08:00
static int createGallagherCAD ( DesfireContext_t * ctx , uint8_t * sitekey , bool verbose ) {
2022-01-04 11:59:59 +08:00
// Check that card UID has been set
if ( ctx - > uidlen = = 0 )
HFGAL_RET_ERR ( PM3_EINVARG , " Card UID must be set in DesfireContext (required for key diversification) " ) ;
2022-01-03 12:02:10 +08:00
DesfireClearSession ( ctx ) ;
DesfireSetCommMode ( ctx , DCMPlain ) ;
DesfireSetCommandSet ( ctx , DCCNativeISO ) ;
2022-01-04 11:59:59 +08:00
2022-01-03 12:02:10 +08:00
int res = DesfireSelectAndAuthenticateAppW ( ctx , DACEV1 , ISW6bAID , 0x000000 , false , verbose ) ;
2022-01-04 11:59:59 +08:00
HFGAL_RET_IF_ERR ( res ) ;
2022-01-03 12:02:10 +08:00
// Create application
DesfireCryptoAlgorithm dstalgo = T_AES ;
uint8_t keycount = 1 ;
uint8_t ks1 = 0x0B ;
uint8_t ks2 = ( DesfireKeyAlgoToType ( dstalgo ) < < 6 ) | keycount ; ;
uint8_t data [ 5 ] = { 0 } ;
2022-01-04 12:03:22 +08:00
DesfireAIDUintToByte ( CAD_AID , & data [ 0 ] ) ;
2022-01-03 12:02:10 +08:00
data [ 3 ] = ks1 ;
data [ 4 ] = ks2 ;
DesfireSetCommMode ( ctx , DCMMACed ) ;
res = DesfireCreateApplication ( ctx , data , ARRAYLEN ( data ) ) ;
2022-01-04 12:03:22 +08:00
HFGAL_RET_IF_ERR_WITH_MSG ( res , " Failed creating Card Application Directory. Does it already exist? " , CAD_AID ) ;
2022-01-03 12:02:10 +08:00
if ( verbose )
2022-01-04 12:03:22 +08:00
PrintAndLogEx ( INFO , " Created Card Application Directory (AID %06X, current has empty contents & blank keys) " , CAD_AID ) ;
2022-01-03 12:02:10 +08:00
2022-01-04 11:59:59 +08:00
// Select & authenticate
2022-01-03 12:02:10 +08:00
uint8_t blankKey [ DESFIRE_MAX_KEY_SIZE ] = { 0 } ;
DesfireSetKeyNoClear ( ctx , 0 , T_AES , blankKey ) ;
DesfireSetCommMode ( ctx , DCMPlain ) ;
2022-01-04 11:59:59 +08:00
DesfireSetCommandSet ( ctx , DCCNativeISO ) ;
2022-01-04 12:03:22 +08:00
res = DesfireSelectAndAuthenticateAppW ( ctx , DACEV1 , ISW6bAID , CAD_AID , false , verbose ) ;
2022-01-04 11:59:59 +08:00
HFGAL_RET_IF_ERR ( res ) ;
// Diversify key
uint8_t buf [ CRYPTO_AES128_KEY_SIZE ] = { 0 } ;
2022-01-04 12:03:22 +08:00
res = GallagherDiversifyKey ( sitekey , ctx - > uid , ctx - > uidlen , 0 , CAD_AID , buf ) ;
HFGAL_RET_IF_ERR_WITH_MSG ( res , " Failed diversifying key 0 for AID %06X " , CAD_AID ) ;
2022-01-04 11:59:59 +08:00
2022-01-04 12:03:22 +08:00
PrintAndLogEx ( INFO , " Diversified key 0 for CAD (AID %06X): " _GREEN_ ( " %s " ) , CAD_AID , sprint_hex_inrow ( buf , ARRAYLEN ( buf ) ) ) ;
2022-01-03 12:02:10 +08:00
// Change key
DesfireSetCommMode ( ctx , DCMEncryptedPlain ) ;
2022-01-04 11:59:59 +08:00
res = DesfireChangeKey ( ctx , false , 0 , dstalgo , 1 , buf , dstalgo , blankKey , verbose ) ;
HFGAL_RET_IF_ERR_WITH_MSG ( res , " Failed setting key 0 for CAD " ) ;
2022-01-03 12:02:10 +08:00
if ( verbose )
2022-01-04 11:59:59 +08:00
PrintAndLogEx ( INFO , " Successfully set key 0 for CAD " ) ;
2022-01-03 12:02:10 +08:00
2022-01-04 12:03:22 +08:00
PrintAndLogEx ( INFO , " Successfully created Card Application Directory (AID %06X) " , CAD_AID ) ;
2022-01-03 12:02:10 +08:00
return PM3_SUCCESS ;
}
2022-01-04 11:59:59 +08:00
/**
* @ brief Update the Gallagher Card Application Directory with a new entry .
*
* @ param sitekey MIFARE site key .
* @ param aid Application ID to add to the CAD .
* @ param creds Gallagher cardholder credentials ( region_code & facility_code are required ) .
*/
static int updateGallagherCAD ( DesfireContext_t * ctx , uint8_t * sitekey , uint32_t aid , GallagherCredentials_t * creds , bool verbose ) {
2022-01-03 12:02:10 +08:00
// Check if CAD exists
uint8_t cad [ 36 * 3 ] = { 0 } ;
uint8_t numEntries = 0 ;
2022-01-04 12:03:22 +08:00
int res = DesfireSelectEx ( ctx , true , ISW6bAID , CAD_AID , NULL ) ;
2022-01-03 12:02:10 +08:00
if ( res = = PM3_SUCCESS ) {
2022-01-04 11:59:59 +08:00
if ( verbose )
PrintAndLogEx ( INFO , " Card Application Directory exists, reading entries... " ) ;
2022-01-03 12:02:10 +08:00
res = readCardApplicationDirectory ( ctx , cad , ARRAYLEN ( cad ) , & numEntries , verbose ) ;
2022-01-04 11:59:59 +08:00
HFGAL_RET_IF_ERR ( res ) ;
2022-01-03 12:02:10 +08:00
// Check that there is space for the new entry
2022-01-04 11:59:59 +08:00
if ( numEntries > = 18 )
HFGAL_RET_ERR ( PM3_EFATAL , " Card application directory is full. " ) ;
} else {
if ( verbose )
PrintAndLogEx ( INFO , " Card Application Directory does not exist, creating it now... " ) ;
res = createGallagherCAD ( ctx , sitekey , verbose ) ;
HFGAL_RET_IF_ERR ( res ) ;
2022-01-03 12:02:10 +08:00
}
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 ;
2022-01-04 11:59:59 +08:00
DesfireAIDUintToByte ( aid , & entry [ 3 ] ) ;
2022-01-03 12:02:10 +08:00
reverseAid ( & entry [ 3 ] ) ; // CAD stores AIDs backwards
2022-01-04 11:59:59 +08:00
if ( verbose )
PrintAndLogEx ( INFO , " Adding entry to CAD (position %d in file %d): %s " , entryNum , fileId , sprint_hex_inrow ( entry , 6 ) ) ;
2022-01-03 12:02:10 +08:00
// Set up context
DesfireSetKeyNoClear ( ctx , 0 , T_AES , sitekey ) ;
DesfireSetKdf ( ctx , MFDES_KDF_ALGO_GALLAGHER , NULL , 0 ) ;
DesfireSetCommMode ( ctx , DCMPlain ) ;
2022-01-04 11:59:59 +08:00
DesfireSetCommandSet ( ctx , DCCNativeISO ) ;
2022-01-03 12:02:10 +08:00
// Select application
2022-01-04 12:03:22 +08:00
res = DesfireSelectAndAuthenticateAppW ( ctx , DACEV1 , ISW6bAID , CAD_AID , false , verbose ) ;
2022-01-04 11:59:59 +08:00
HFGAL_RET_IF_ERR ( res ) ;
2022-01-03 12:02:10 +08:00
// Create file if necessary
if ( entryNum = = 0 ) {
2022-01-04 11:59:59 +08:00
if ( verbose )
PrintAndLogEx ( INFO , " Creating new file in CAD " ) ;
2022-01-03 12:02:10 +08:00
// 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 ) ;
2022-01-04 12:03:22 +08:00
HFGAL_RET_IF_ERR_WITH_MSG ( res , " Failed creating file %d in CAD (AID %06X) " , fileId , CAD_AID ) ;
2022-01-04 11:59:59 +08:00
if ( verbose )
PrintAndLogEx ( INFO , " Created file %d in CAD (current has empty contents) " , fileId ) ;
2022-01-03 12:02:10 +08:00
// Write file
res = DesfireWriteFile ( ctx , fileId , fileId * 36 , 36 , entry ) ;
} else {
// Write file
res = DesfireWriteFile ( ctx , fileId , entryNum * 6 , 6 , entry ) ;
}
2022-01-04 12:03:22 +08:00
HFGAL_RET_IF_ERR_WITH_MSG ( res , " Failed writing data to file %d in CAD (AID %06X) " , fileId , CAD_AID ) ;
2022-01-04 11:59:59 +08:00
PrintAndLogEx ( INFO , " Successfully added new entry for %06X to the Card Application Directory " , aid ) ;
2022-01-03 12:02:10 +08:00
return PM3_SUCCESS ;
}
2021-12-29 18:42:06 +08:00
static int CmdGallagherClone ( const char * Cmd ) {
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " hf gallagher clone " ,
" clone a GALLAGHER card to a blank DESFire card. " ,
2022-01-03 12:02:10 +08:00
" hf gallagher clone --rc 1 --fc 22 --cn 3333 --il 4 --sitekey 00112233445566778899aabbccddeeff "
2021-12-29 18:42:06 +08:00
) ;
void * argtable [ ] = {
arg_param_begin ,
2022-01-03 12:02:10 +08:00
arg_lit0 ( NULL , " apdu " , " show APDU requests and responses " ) ,
arg_lit0 ( " v " , " verbose " , " Verbose mode " ) ,
arg_int0 ( " n " , " keyno " , " <decimal> " , " Key number [default=0] " ) ,
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) " ) ,
2021-12-29 18:42:06 +08:00
arg_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , false ) ;
2022-01-03 12:02:10 +08:00
SetAPDULogging ( arg_get_lit ( ctx , 1 ) ) ;
bool verbose = arg_get_lit ( ctx , 2 ) | | true ;
int keyNum = arg_get_int_def ( ctx , 3 , 0 ) ;
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 ) ) {
2022-01-04 11:59:59 +08:00
HFGAL_RET_ERR ( PM3_EINVARG , " %s key must have %d bytes length instead of %d " , CLIGetOptionListStr ( DesfireAlgoOpts , algo ) , desfire_get_key_length ( algo ) , keyLen ) ;
2022-01-03 12:02:10 +08:00
}
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 ;
2022-01-04 11:59:59 +08:00
uint8_t aidBuf [ 3 ] = " \x20 \x81 \xF4 " ;
CLIGetHexWithReturn ( ctx , 10 , aidBuf , & aidLen ) ;
2022-01-03 12:02:10 +08:00
if ( aidLen > 0 & & aidLen ! = 3 ) {
2022-01-04 11:59:59 +08:00
HFGAL_RET_ERR ( PM3_EINVARG , " --aid must be 3 bytes " ) ;
2022-01-03 12:02:10 +08:00
}
2022-01-04 11:59:59 +08:00
reverseAid ( aidBuf ) ; // PM3 displays AIDs backwards
uint32_t aid = DesfireAIDByteToUint ( aidBuf ) ;
2022-01-03 12:02:10 +08:00
// Check that the AID is in the expected range
2022-01-04 11:59:59 +08:00
if ( memcmp ( aidBuf , " \xF4 \x81 " , 2 ) ! = 0 | | aidBuf [ 2 ] < 0x20 | | aidBuf [ 2 ] > 0x2B )
// TODO: this should probably be a warning, but key diversification will throw an error later even if we don't
HFGAL_RET_ERR ( PM3_EINVARG , " Invalid Gallagher AID %06X, expected 2?81F4, where 0 <= ? <= 0xB " , aid ) ;
2022-01-03 12:02:10 +08:00
int sitekeyLen = 0 ;
uint8_t sitekey [ 16 ] = { 0 } ;
CLIGetHexWithReturn ( ctx , 11 , sitekey , & sitekeyLen ) ;
2022-01-04 11:59:59 +08:00
if ( sitekeyLen > 0 & & sitekeyLen ! = 16 )
HFGAL_RET_ERR ( PM3_EINVARG , " --sitekey must be 16 bytes " ) ;
2021-12-29 18:42:06 +08:00
CLIParserFree ( ctx ) ;
2022-01-04 11:59:59 +08:00
if ( ! isValidGallagherCredentials ( region_code , facility_code , card_number , issue_level ) )
2021-12-29 18:42:06 +08:00
return PM3_EINVARG ;
2022-01-04 11:59:59 +08:00
2022-01-03 12:02:10 +08:00
GallagherCredentials_t creds = {
. region_code = region_code ,
. facility_code = facility_code ,
. card_number = card_number ,
. issue_level = issue_level ,
} ;
2021-12-29 18:42:06 +08:00
2022-01-03 12:02:10 +08:00
// Set up context
DropField ( ) ;
DesfireContext_t dctx = { 0 } ;
DesfireClearContext ( & dctx ) ;
2021-12-29 18:42:06 +08:00
2022-01-03 12:02:10 +08:00
// Get card UID (for key diversification)
int res = DesfireGetCardUID ( & dctx ) ;
2022-01-04 11:59:59 +08:00
HFGAL_RET_IF_ERR_WITH_MSG ( res , " Failed retrieving card UID " ) ;
2022-01-03 12:02:10 +08:00
// Create application
2022-01-04 11:59:59 +08:00
DesfireSetKeyNoClear ( & dctx , keyNum , algo , key ) ;
DesfireSetKdf ( & dctx , MFDES_KDF_ALGO_NONE , NULL , 0 ) ;
res = createGallagherCredentialsApplication ( & dctx , sitekey , aid , verbose ) ;
HFGAL_RET_IF_ERR_WITH_MSG ( res , " Failed creating Gallagher application " ) ;
2022-01-03 12:02:10 +08:00
// Create credential files
2022-01-04 11:59:59 +08:00
// Don't need to set keys here, they're generated automatically
res = createGallagherCredentialsFile ( & dctx , sitekey , aid , & creds , verbose ) ;
HFGAL_RET_IF_ERR_WITH_MSG ( res , " Failed creating Gallagher credential file. " ) ;
2022-01-03 12:02:10 +08:00
// Update card application directory
2022-01-04 11:59:59 +08:00
DesfireSetKeyNoClear ( & dctx , keyNum , algo , key ) ;
DesfireSetKdf ( & dctx , MFDES_KDF_ALGO_NONE , NULL , 0 ) ;
res = updateGallagherCAD ( & dctx , sitekey , aid , & creds , verbose ) ;
HFGAL_RET_IF_ERR_WITH_MSG ( res , " Failed updating Gallagher card application directory. " ) ;
2021-12-29 18:42:06 +08:00
PrintAndLogEx ( SUCCESS , " Done " ) ;
PrintAndLogEx ( HINT , " Hint: try " _YELLOW_ ( " `hf gallagher reader` " ) " to verify " ) ;
2022-01-04 11:59:59 +08:00
return PM3_SUCCESS ;
2021-12-29 18:42:06 +08:00
}
static int CmdGallagherSim ( const char * Cmd ) {
CLIParserContext * ctx ;
CLIParserInit ( & ctx , " hf gallagher sim " ,
" Enables simulation of GALLAGHER card with specified card number. \n "
" Simulation runs until the button is pressed or another USB command is issued. \n " ,
" hf gallagher sim --rc 1 --fc 22 --cn 3333 --il 4 "
) ;
void * argtable [ ] = {
arg_param_begin ,
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_param_end
} ;
CLIExecWithReturn ( ctx , Cmd , argtable , false ) ;
uint64_t region_code = arg_get_u64 ( ctx , 1 ) ; // uint16, will be validated later
uint64_t facility_code = arg_get_u64 ( ctx , 2 ) ; // uint32, will be validated later
uint64_t card_number = arg_get_u64 ( ctx , 3 ) ; // uint64
uint64_t issue_level = arg_get_u64 ( ctx , 4 ) ; // uint32, will be validated later
CLIParserFree ( ctx ) ;
2022-01-04 11:59:59 +08:00
if ( ! isValidGallagherCredentials ( region_code , facility_code , card_number , issue_level ) )
2021-12-29 18:42:06 +08:00
return PM3_EINVARG ;
// TODO: create data
// TODO: simulate
return PM3_ENOTIMPL ;
}
static command_t CommandTable [ ] = {
{ " help " , CmdHelp , AlwaysAvailable , " This help " } ,
{ " reader " , CmdGallagherReader , IfPm3Iso14443 , " attempt to read and extract tag data " } ,
{ " clone " , CmdGallagherClone , IfPm3Iso14443 , " clone GALLAGHER tag to a blank DESFire card " } ,
{ " sim " , CmdGallagherSim , IfPm3Iso14443 , " simulate GALLAGHER tag " } ,
{ NULL , NULL , NULL , NULL }
} ;
static int CmdHelp ( const char * Cmd ) {
( void ) Cmd ; // Cmd is not used so far
CmdsHelp ( CommandTable ) ;
return PM3_SUCCESS ;
}
int CmdHFGallagher ( const char * Cmd ) {
clearCommandBuffer ( ) ;
return CmdsParse ( CommandTable , Cmd ) ;
}