diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index f7920b4e0..0aa5f2dc3 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -226,6 +226,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/mifare/mifarehost.c ${PM3_ROOT}/client/src/nfc/ndef.c ${PM3_ROOT}/client/src/mifare/desfire_crypto.c + ${PM3_ROOT}/client/src/mifare/desfirecore.c ${PM3_ROOT}/client/src/uart/uart_posix.c ${PM3_ROOT}/client/src/uart/uart_win32.c ${PM3_ROOT}/client/src/ui/overlays.ui diff --git a/client/Makefile b/client/Makefile index 526bf21b0..9a2ef3c5c 100644 --- a/client/Makefile +++ b/client/Makefile @@ -589,6 +589,7 @@ SRCS = aiddesfire.c \ loclass/elite_crack.c \ loclass/ikeys.c \ mifare/desfire_crypto.c \ + mifare/desfirecore.c \ mifare/mad.c \ mifare/mfkey.c \ mifare/mifare4.c \ diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 2608a29eb..d141a22b8 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -1,5 +1,6 @@ //----------------------------------------------------------------------------- // Copyright (C) 2014 Iceman +// Copyright (C) 2021 Merlok // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of @@ -28,6 +29,7 @@ #include "iso7816/iso7816core.h" // APDU logging #include "util_posix.h" // msleep #include "mifare/desfire_crypto.h" +#include "mifare/desfirecore.h" #include "crapto1/crapto1.h" #include "fileutils.h" #include "mifare/mifaredefault.h" // default keys @@ -508,125 +510,6 @@ static int DESFIRESendApdu(bool activate_field, bool leavefield_on, sAPDU apdu, return PM3_SUCCESS; } -static const char *getstatus(uint16_t *sw) { - if (sw == NULL) return "--> sw argument error. This should never happen !"; - if (((*sw >> 8) & 0xFF) == 0x91) { - switch (*sw & 0xFF) { - case MFDES_E_OUT_OF_EEPROM: - return "Out of Eeprom, insufficient NV-Memory to complete command"; - case MFDES_E_ILLEGAL_COMMAND_CODE: - return "Command code not supported"; - - case MFDES_E_INTEGRITY_ERROR: - return "CRC or MAC does not match data / Padding bytes invalid"; - - case MFDES_E_NO_SUCH_KEY: - return "Invalid key number specified"; - - case MFDES_E_LENGTH: - return "Length of command string invalid"; - - case MFDES_E_PERMISSION_DENIED: - return "Current configuration/status does not allow the requested command"; - - case MFDES_E_PARAMETER_ERROR: - return "Value of the parameter(s) invalid"; - - case MFDES_E_APPLICATION_NOT_FOUND: - return "Requested AID not present on PICC"; - - case MFDES_E_APPL_INTEGRITY: - return "Application integrity error, application will be disabled"; - - case MFDES_E_AUTHENTIFICATION_ERROR: - return "Current authentication status does not allow the requested command"; - - case MFDES_E_BOUNDARY: - return "Attempted to read/write data from/to beyond the file's/record's limit"; - - case MFDES_E_PICC_INTEGRITY: - return "PICC integrity error, PICC will be disabled"; - - case MFDES_E_COMMAND_ABORTED: - return "Previous command was not fully completed / Not all Frames were requested or provided by the PCD"; - - case MFDES_E_PICC_DISABLED: - return "PICC was disabled by an unrecoverable error"; - - case MFDES_E_COUNT: - return "Application count is limited to 28, not addition CreateApplication possible"; - - case MFDES_E_DUPLICATE: - return "Duplicate entry: File/Application/ISO Text does already exist"; - - case MFDES_E_EEPROM: - return "Eeprom error due to loss of power, internal backup/rollback mechanism activated"; - - case MFDES_E_FILE_NOT_FOUND: - return "Specified file number does not exist"; - - case MFDES_E_FILE_INTEGRITY: - return "File integrity error, file will be disabled"; - - default: - return "Unknown error"; - } - } - return "Unknown error"; -} - -static const char *GetErrorString(int res, uint16_t *sw) { - switch (res) { - case PM3_EAPDU_FAIL: - return getstatus(sw); - case PM3_EUNDEF: - return "Undefined error"; - case PM3_EINVARG: - return "Invalid argument(s)"; - case PM3_EDEVNOTSUPP: - return "Operation not supported by device"; - case PM3_ETIMEOUT: - return "Operation timed out"; - case PM3_EOPABORTED: - return "Operation aborted (by user)"; - case PM3_ENOTIMPL: - return "Not (yet) implemented"; - case PM3_ERFTRANS: - return "Error while RF transmission"; - case PM3_EIO: - return "Input / output error"; - case PM3_EOVFLOW: - return "Buffer overflow"; - case PM3_ESOFT: - return "Software error"; - case PM3_EFLASH: - return "Flash error"; - case PM3_EMALLOC: - return "Memory allocation error"; - case PM3_EFILE: - return "File error"; - case PM3_ENOTTY: - return "Generic TTY error"; - case PM3_EINIT: - return "Initialization error"; - case PM3_EWRONGANSWER: - return "Expected a different answer error"; - case PM3_EOUTOFBOUND: - return "Memory out-of-bounds error"; - case PM3_ECARDEXCHANGE: - return "Exchange with card error"; - case PM3_EAPDU_ENCODEFAIL: - return "Failed to create APDU"; - case PM3_ENODATA: - return "No data"; - case PM3_EFATAL: - return "Fatal error"; - default: - break; - } - return ""; -} - static int send_desfire_cmd(sAPDU *apdu, bool select, uint8_t *dest, uint32_t *recv_len, uint16_t *sw, uint32_t splitbysize, bool readalldata) { if (apdu == NULL) { PrintAndLogEx(DEBUG, "APDU=NULL"); @@ -648,7 +531,7 @@ static int send_desfire_cmd(sAPDU *apdu, bool select, uint8_t *dest, uint32_t *r uint32_t i = 1; int res = DESFIRESendApdu(select, true, *apdu, data, sizeof(data), &resplen, sw); if (res != PM3_SUCCESS) { - PrintAndLogEx(DEBUG, "%s", GetErrorString(res, sw)); + PrintAndLogEx(DEBUG, "%s", DesfireGetErrorString(res, sw)); DropFieldDesfire(); return res; } @@ -673,7 +556,7 @@ static int send_desfire_cmd(sAPDU *apdu, bool select, uint8_t *dest, uint32_t *r res = DESFIRESendApdu(false, true, *apdu, data, sizeof(data), &resplen, sw); if (res != PM3_SUCCESS) { - PrintAndLogEx(DEBUG, "%s", GetErrorString(res, sw)); + PrintAndLogEx(DEBUG, "%s", DesfireGetErrorString(res, sw)); DropFieldDesfire(); return res; } @@ -1340,7 +1223,7 @@ static int mifare_desfire_change_key(uint8_t key_no, uint8_t *new_key, uint8_t n int res = send_desfire_cmd(&apdu, false, NULL, &recv_len, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_("can't change key -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_("can't change key -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -1770,7 +1653,7 @@ static int handler_desfire_select_application(uint8_t *aid) { PrintAndLogEx(WARNING, _RED_(" Can't select AID 0x%X -> %s"), (aid[2] << 16) + (aid[1] << 8) + aid[0], - GetErrorString(res, &sw) + DesfireGetErrorString(res, &sw) ); DropFieldDesfire(); return res; @@ -1813,7 +1696,7 @@ static int handler_desfire_fileids(uint8_t *dest, uint32_t *file_ids_len) { *file_ids_len = 0; int res = send_desfire_cmd(&apdu, false, dest, &recv_len, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't get file ids -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't get file ids -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -1832,7 +1715,7 @@ static int handler_desfire_filesettings(uint8_t file_id, uint8_t *dest, uint32_t uint16_t sw = 0; int res = send_desfire_cmd(&apdu, false, dest, destlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't get file settings -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't get file settings -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -1872,7 +1755,7 @@ static int handler_desfire_createapp(aidhdr_t *aidhdr, bool usename, bool usefid free(data); } if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't create aid -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't create aid -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); } return res; @@ -1887,7 +1770,7 @@ static int handler_desfire_deleteapp(const uint8_t *aid) { uint32_t recvlen = 0; int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't delete aid -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't delete aid -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); } return res; @@ -1905,7 +1788,7 @@ static int handler_desfire_credit(mfdes_value_t *value, uint8_t cs) { int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't credit value -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't credit value -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -1924,7 +1807,7 @@ static int handler_desfire_limitedcredit(mfdes_value_t *value, uint8_t cs) { int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't credit limited value -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't credit limited value -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -1943,7 +1826,7 @@ static int handler_desfire_debit(mfdes_value_t *value, uint8_t cs) { int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't debit value -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't debit value -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -1981,7 +1864,7 @@ static int handler_desfire_readdata(mfdes_data_t *data, MFDES_FILE_TYPE_T type, uint32_t resplen = 0; int res = send_desfire_cmd(&apdu, false, data->data, &resplen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't read data -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't read data -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -2013,7 +1896,7 @@ static int handler_desfire_getvalue(mfdes_value_t *value, uint32_t *resplen, uin int res = send_desfire_cmd(&apdu, false, value->value, resplen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't read data -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't read data -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -2089,7 +1972,7 @@ static int handler_desfire_writedata(mfdes_data_t *data, MFDES_FILE_TYPE_T type, res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't write data -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't write data -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -2098,7 +1981,7 @@ static int handler_desfire_writedata(mfdes_data_t *data, MFDES_FILE_TYPE_T type, } if (type == MFDES_RECORD_FILE) { if (handler_desfire_commit_transaction() != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't commit transaction -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't commit transaction -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -2115,7 +1998,7 @@ static int handler_desfire_deletefile(uint8_t file_no) { uint32_t recvlen = 0; int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't delete file -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't delete file -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -2131,13 +2014,13 @@ static int handler_desfire_clear_record_file(uint8_t file_no) { uint32_t recvlen = 0; int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't clear record file -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't clear record file -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } else { res = handler_desfire_commit_transaction(); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't commit transaction -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't commit transaction -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -2154,7 +2037,7 @@ static int handler_desfire_create_value_file(mfdes_value_file_t *value) { uint32_t recvlen = 0; int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't create value -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't create value -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -2171,7 +2054,7 @@ static int handler_desfire_create_std_file(mfdes_file_t *file) { uint32_t recvlen = 0; int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't create file -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't create file -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -2189,7 +2072,7 @@ static int handler_desfire_create_linearrecordfile(mfdes_linear_t *file) { uint32_t recvlen = 0; int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't create linear record file -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't create linear record file -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -2209,7 +2092,7 @@ static int handler_desfire_create_cyclicrecordfile(mfdes_linear_t *file) { uint32_t recvlen = 0; int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't create cyclic record file -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't create cyclic record file -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -2225,7 +2108,7 @@ static int handler_desfire_create_backup_file(mfdes_file_t *file) { uint32_t recvlen = 0; int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't create backup file -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't create backup file -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -3572,7 +3455,7 @@ static int CmdHF14ADesFormatPICC(const char *Cmd) { uint32_t recvlen = 0; int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't format picc -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't format picc -> %s"), DesfireGetErrorString(res, &sw)); } else { PrintAndLogEx(INFO, "Card successfully reset"); } @@ -5115,10 +4998,320 @@ static int CmdHF14aDesMAD(const char *Cmd) { return PM3_SUCCESS; } */ +static uint8_t defaultKeyNum = 0; +static enum DESFIRE_CRYPTOALGO defaultAlgoId = T_DES; +static uint8_t defaultKey[DESFIRE_MAX_KEY_SIZE] = {0}; +static int defaultKdfAlgo = MFDES_KDF_ALGO_NONE; +static int defaultKdfInputLen = 0; +static uint8_t defaultKdfInput[50] = {0}; +static DesfireSecureChannel defaultSecureChannel = DACEV1; +static DesfireCommandSet defaultCommSet = DCCNativeISO; +static DesfireCommunicationMode defaultCommMode = DCMPlain; + +static int CmdDesGetSessionParameters(CLIParserContext *ctx, DesfireContext *dctx, + uint8_t keynoid, uint8_t algoid, uint8_t keyid, + uint8_t kdfid, uint8_t kdfiid, + uint8_t cmodeid, uint8_t ccsetid, uint8_t schannid, + int *securechannel) { + + uint8_t keynum = defaultKeyNum; + int algores = defaultAlgoId; + uint8_t key[DESFIRE_MAX_KEY_SIZE] = {0}; + memcpy(key, defaultKey, DESFIRE_MAX_KEY_SIZE); + int kdfAlgo = defaultKdfAlgo; + int kdfInputLen = defaultKdfInputLen; + uint8_t kdfInput[50] = {0}; + memcpy(kdfInput, defaultKdfInput, defaultKdfInputLen); + int commmode = defaultCommMode; + int commset = defaultCommSet; + int secchann = defaultSecureChannel; + + if (keynoid) { + keynum = arg_get_int_def(ctx, keynoid, keynum); + } + + if (algoid) { + if (CLIGetOptionList(arg_get_str(ctx, algoid), DesfireAlgoOpts, &algores)) + return PM3_ESOFT; + } + + if (keyid) { + int keylen = 0; + uint8_t keydata[200] = {0}; + if (CLIParamHexToBuf(arg_get_str(ctx, keyid), keydata, sizeof(keydata), &keylen)) + return PM3_ESOFT; + if (keylen && keylen != desfire_get_key_length(algores)) { + PrintAndLogEx(ERR, "%s key must have %d bytes length instead of %d.", CLIGetOptionListStr(DesfireAlgoOpts, algores), desfire_get_key_length(algores), keylen); + return PM3_EINVARG; + } + if (keylen) + memcpy(key, keydata, keylen); + } + + if (kdfid) { + if (CLIGetOptionList(arg_get_str(ctx, kdfid), DesfireKDFAlgoOpts, &kdfAlgo)) + return PM3_ESOFT; + } + + if (kdfiid) { + int datalen = kdfInputLen; + uint8_t data[200] = {0}; + if (CLIParamHexToBuf(arg_get_str(ctx, kdfiid), data, sizeof(data), &datalen)) + return PM3_ESOFT; + if (datalen) { + kdfInputLen = datalen; + memcpy(kdfInput, data, datalen); + } + } + + if (cmodeid) { + if (CLIGetOptionList(arg_get_str(ctx, cmodeid), DesfireCommunicationModeOpts, &commmode)) + return PM3_ESOFT; + } + + if (ccsetid) { + if (CLIGetOptionList(arg_get_str(ctx, ccsetid), DesfireCommandSetOpts, &commset)) + return PM3_ESOFT; + } + + if (schannid) { + if (CLIGetOptionList(arg_get_str(ctx, schannid), DesfireSecureChannelOpts, &secchann)) + return PM3_ESOFT; + } + + DesfireSetKey(dctx, keynum, algores, key); + DesfireSetKdf(dctx, kdfAlgo, kdfInput, kdfInputLen); + DesfireSetCommandSet(dctx, commset); + DesfireSetCommMode(dctx, commmode); + if (securechannel) + *securechannel = secchann; + + return PM3_SUCCESS; +} + +static int CmdHF14ADesDefault(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes default", + "Get Application IDs, ISO IDs and DF names from card. Master key needs to be provided.", + "hf mfdes getappnames -n 0 -t des -k 0000000000000000 -f none -> execute with default factory setup"); + + void *argtable[] = { + arg_param_begin, + arg_int0("n", "keyno", "", "Key number"), + arg_str0("t", "algo", "", "Crypt algo: DES, 2TDEA, 3TDEA, AES"), + arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), + arg_str0("f", "kdf", "", "Key Derivation Function (KDF): None, AN10922, Gallagher"), + arg_str0("i", "kdfi", "", "KDF input (HEX 1-31 bytes)"), + arg_str0("m", "cmode", "", "Communicaton mode: plain/mac/encrypt"), + arg_str0("c", "ccset", "", "Communicaton command set: native/niso/iso"), + arg_str0("s", "schann", "", "Secure channel: d40/ev1/ev2"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + DesfireContext dctx; + int securechann = defaultSecureChannel; + int res = CmdDesGetSessionParameters(ctx, &dctx, 1, 2, 3, 4, 5, 6, 7, 8, &securechann); + if (res) { + CLIParserFree(ctx); + return res; + } + + CLIParserFree(ctx); + + defaultKeyNum = dctx.keyNum; + defaultAlgoId = dctx.keyType; + memcpy(defaultKey, dctx.key, DESFIRE_MAX_KEY_SIZE); + defaultKdfAlgo = dctx.kdfAlgo; + defaultKdfInputLen = dctx.kdfInputLen; + memcpy(defaultKdfInput, dctx.kdfInput, sizeof(dctx.kdfInput)); + defaultSecureChannel = securechann; + defaultCommSet = dctx.cmdSet; + defaultCommMode = dctx.commMode; + + PrintAndLogEx(INFO, "-----------" _CYAN_("Default parameters") "---------------------------------"); + + PrintAndLogEx(INFO, "Key Num : %d", defaultKeyNum); + PrintAndLogEx(INFO, "Algo : %s", CLIGetOptionListStr(DesfireAlgoOpts, defaultAlgoId)); + PrintAndLogEx(INFO, "Key : %s", sprint_hex(defaultKey, desfire_get_key_length(defaultAlgoId))); + PrintAndLogEx(INFO, "KDF algo : %s", CLIGetOptionListStr(DesfireKDFAlgoOpts, defaultKdfAlgo)); + PrintAndLogEx(INFO, "KDF input : [%d] %s", defaultKdfInputLen, sprint_hex(defaultKdfInput, defaultKdfInputLen)); + PrintAndLogEx(INFO, "Secure chan : %s", CLIGetOptionListStr(DesfireSecureChannelOpts, defaultSecureChannel)); + PrintAndLogEx(INFO, "Command set : %s", CLIGetOptionListStr(DesfireCommandSetOpts, defaultCommSet)); + PrintAndLogEx(INFO, "Comm mode : %s", CLIGetOptionListStr(DesfireCommunicationModeOpts, defaultCommMode)); + + return PM3_SUCCESS; +} + +static int CmdHF14ADesGetAIDs(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes getaids", + "Get Application IDs list from card. Master key needs to be provided.", + "hf mfdes getaids -n 0 -t des -k 0000000000000000 -f none -> execute with default factory setup"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("a", "apdu", "show APDU requests and responses"), + arg_lit0("v", "verbose", "show technical data"), + arg_int0("n", "keyno", "", "Key number"), + arg_str0("t", "algo", "", "Crypt algo: DES, 2TDEA, 3TDEA, AES"), + arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), + arg_str0("f", "kdf", "", "Key Derivation Function (KDF): None, AN10922, Gallagher"), + arg_str0("i", "kdfi", "", "KDF input (HEX 1-31 bytes)"), + arg_str0("m", "cmode", "", "Communicaton mode: plain/mac/encrypt"), + arg_str0("c", "ccset", "", "Communicaton command set: native/niso/iso"), + arg_str0("s", "schann", "", "Secure channel: d40/ev1/ev2"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + bool APDULogging = arg_get_lit(ctx, 1); + bool verbose = arg_get_lit(ctx, 2); + + DesfireContext dctx; + int securechann = defaultSecureChannel; + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, &securechann); + if (res) { + CLIParserFree(ctx); + return res; + } + + SetAPDULogging(APDULogging); + CLIParserFree(ctx); + + if (verbose) + DesfirePrintContext(&dctx); + + res = DesfireSelectAIDHex(&dctx, 0x000000, false, 0); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire select " _RED_("error") "."); + DropField(); + return PM3_ESOFT; + } + + res = DesfireAuthenticate(&dctx, securechann); //DACd40 DACEV1 + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire authenticate " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + if (DesfireIsAuthenticated(&dctx)) { + if (verbose) + PrintAndLogEx(ERR, "Desfire " _GREEN_("authenticated") , res); + } else { + return PM3_ESOFT; + } + + uint8_t buf[APDU_RES_LEN] = {0}; + size_t buflen = 0; + + res = DesfireGetAIDList(&dctx, buf, &buflen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire GetAIDList command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + if (buflen >= 3) { + PrintAndLogEx(INFO, "---- " _CYAN_("AID list") " ----"); + for (int i = 0; i < buflen; i += 3) + PrintAndLogEx(INFO, "AID: %06x", DesfireAIDByteToUint(&buf[i])); + } + + DropField(); + return PM3_SUCCESS; +} + +static int CmdHF14ADesGetAppNames(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes getappnames", + "Get Application IDs, ISO IDs and DF names from card. Master key needs to be provided.", + "hf mfdes getappnames -n 0 -t des -k 0000000000000000 -f none -> execute with default factory setup"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("a", "apdu", "show APDU requests and responses"), + arg_lit0("v", "verbose", "show technical data"), + arg_int0("n", "keyno", "", "Key number"), + arg_str0("t", "algo", "", "Crypt algo: DES, 2TDEA, 3TDEA, AES"), + arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), + arg_str0("f", "kdf", "", "Key Derivation Function (KDF): None, AN10922, Gallagher"), + arg_str0("i", "kdfi", "", "KDF input (HEX 1-31 bytes)"), + arg_str0("m", "cmode", "", "Communicaton mode: plain/mac/encrypt"), + arg_str0("c", "ccset", "", "Communicaton command set: native/niso/iso"), + arg_str0("s", "schann", "", "Secure channel: d40/ev1/ev2"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + bool APDULogging = arg_get_lit(ctx, 1); + bool verbose = arg_get_lit(ctx, 2); + + DesfireContext dctx; + int securechann = defaultSecureChannel; + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, &securechann); + if (res) { + CLIParserFree(ctx); + return res; + } + + SetAPDULogging(APDULogging); + CLIParserFree(ctx); + + if (verbose) + DesfirePrintContext(&dctx); + + res = DesfireSelectAIDHex(&dctx, 0x000000, false, 0); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire select " _RED_("error") "."); + DropField(); + return PM3_ESOFT; + } + + res = DesfireAuthenticate(&dctx, securechann); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire authenticate " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + if (DesfireIsAuthenticated(&dctx)) { + if (verbose) + PrintAndLogEx(ERR, "Desfire " _GREEN_("authenticated") , res); + } else { + return PM3_ESOFT; + } + + uint8_t buf[APDU_RES_LEN] = {0}; + size_t buflen = 0; + + // result bytes: 3, 2, 1-16. total record size = 24 + res = DesfireGetDFList(&dctx, buf, &buflen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire DesfireGetDFList command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + if (buflen > 0) { + PrintAndLogEx(INFO, "----------------------- " _CYAN_("File list") " -----------------------"); + for (int i = 0; i < buflen; i++) + PrintAndLogEx(INFO, "AID: %06x ISO file id: %02x%02x ISO DF name[%d]: %s", + DesfireAIDByteToUint(&buf[i * 24 + 1]), + buf[i * 24 + 1 + 3], buf[i * 24 + 1 + 4], + strlen((char *)&buf[i * 24 + 1 + 5]), + &buf[i * 24 + 1 + 5]); + } + + DropField(); + return PM3_SUCCESS; +} static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"-----------", CmdHelp, IfPm3Iso14443a, "---------------------- " _CYAN_("general") " ----------------------"}, + {"default", CmdHF14ADesDefault, IfPm3Iso14443a, "[new]Set defaults for all the commands"}, {"auth", CmdHF14ADesAuth, IfPm3Iso14443a, "Tries a MIFARE DesFire Authentication"}, {"changekey", CmdHF14ADesChangeKey, IfPm3Iso14443a, "Change Key"}, {"chk", CmdHF14aDesChk, IfPm3Iso14443a, "Check keys"}, @@ -5134,6 +5327,8 @@ static command_t CommandTable[] = { {"createaid", CmdHF14ADesCreateApp, IfPm3Iso14443a, "Create Application ID"}, {"deleteaid", CmdHF14ADesDeleteApp, IfPm3Iso14443a, "Delete Application ID"}, {"selectaid", CmdHF14ADesSelectApp, IfPm3Iso14443a, "Select Application ID"}, + {"getaids", CmdHF14ADesGetAIDs, IfPm3Iso14443a, "[new]Get Application IDs list"}, + {"getappnames", CmdHF14ADesGetAppNames, IfPm3Iso14443a, "[new]Get Applications list"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("Files") " -----------------------"}, {"changevalue", CmdHF14ADesChangeValue, IfPm3Iso14443a, "Write value of a value file (credit/debit/clear)"}, {"clearfile", CmdHF14ADesClearRecordFile, IfPm3Iso14443a, "Clear record File"}, diff --git a/client/src/cmdhfmfdes.h b/client/src/cmdhfmfdes.h index eb4dd77e0..60ef9c35f 100644 --- a/client/src/cmdhfmfdes.h +++ b/client/src/cmdhfmfdes.h @@ -1,5 +1,6 @@ //----------------------------------------------------------------------------- -// Iceman, 2014 +// Copyright (C) Iceman, 2014 +// Copyright (C) 2021 Merlok // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of diff --git a/client/src/mifare/desfire_crypto.c b/client/src/mifare/desfire_crypto.c index 34f38ef73..2414d83ef 100644 --- a/client/src/mifare/desfire_crypto.c +++ b/client/src/mifare/desfire_crypto.c @@ -1,5 +1,6 @@ /*- * Copyright (C) 2010, Romain Tartiere. + * Copyright (C) 2021 Merlok * * 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 @@ -52,6 +53,16 @@ static inline void update_key_schedules(desfirekey_t key) { // } } +int desfire_get_key_length(enum DESFIRE_CRYPTOALGO key_type) { + switch (key_type) { + case T_DES: return 8; + case T_3DES: return 16; + case T_3K3DES: return 24; + case T_AES: return 16; + } + return 0; +} + /******************************************************************************/ void tdes_nxp_receive(const void *in, void *out, size_t length, const void *key, unsigned char iv[8], int keymode) { @@ -367,12 +378,9 @@ void mifare_kdf_an10922(const desfirekey_t key, const uint8_t *data, size_t len) free(buffer); } -size_t key_block_size(const desfirekey_t key) { - if (key == NULL) { - return 0; - } +size_t desfire_get_key_block_length(enum DESFIRE_CRYPTOALGO key_type) { size_t block_size = 8; - switch (key->type) { + switch (key_type) { case T_DES: case T_3DES: case T_3K3DES: @@ -385,6 +393,13 @@ size_t key_block_size(const desfirekey_t key) { return block_size; } +size_t key_block_size(const desfirekey_t key) { + if (key == NULL) { + return 0; + } + return desfire_get_key_block_length(key->type); +} + /* * Size of MACing produced with the key. */ diff --git a/client/src/mifare/desfire_crypto.h b/client/src/mifare/desfire_crypto.h index b58372004..0d3baa5c3 100644 --- a/client/src/mifare/desfire_crypto.h +++ b/client/src/mifare/desfire_crypto.h @@ -1,12 +1,34 @@ -#ifndef __DESFIRE_CRYPTO_H +/*- + * Copyright (C) 2010, Romain Tartiere. + * Copyright (C) 2021 Merlok + * + * 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. + * + * 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. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + * + * $Id$ + */ + + #ifndef __DESFIRE_CRYPTO_H #define __DESFIRE_CRYPTO_H #include "common.h" #include "mifare.h" // structs #include "crc32.h" +#include "crypto/libpcrypto.h" #define MAX_CRYPTO_BLOCK_SIZE 16 +#define DESFIRE_MAX_KEY_SIZE 24 /* Mifare DESFire EV1 Application crypto operations */ #define APPLICATION_CRYPTO_DES 0x00 #define APPLICATION_CRYPTO_3K3DES 0x40 @@ -60,6 +82,9 @@ enum DESFIRE_CRYPTOALGO { T_AES = 0x03 }; +int desfire_get_key_length(enum DESFIRE_CRYPTOALGO key_type); +size_t desfire_get_key_block_length(enum DESFIRE_CRYPTOALGO key_type); + enum DESFIRE_AUTH_SCHEME { AS_LEGACY, AS_NEW diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c new file mode 100644 index 000000000..9b80ede51 --- /dev/null +++ b/client/src/mifare/desfirecore.c @@ -0,0 +1,1100 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2010 Romain Tartiere. +// Copyright (C) 2014 Iceman +// Copyright (C) 2021 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// High frequency Desfire core functions +//----------------------------------------------------------------------------- +// Info from here and many other sources from the public internet sites +// https://github.com/revk/DESFireAES +// https://github.com/step21/desfire_rfid +// https://github.com/patsys/desfire-python/blob/master/Desfire/DESFire.py +//----------------------------------------------------------------------------- + +#include "desfirecore.h" +#include +#include +#include +#include "aes.h" +#include "ui.h" +#include "crc.h" +#include "crc16.h" // crc16 ccitt +#include "crc32.h" +#include "protocols.h" +#include "commonutil.h" +#include "cmdhf14a.h" +#include "iso7816/apduinfo.h" // APDU manipulation / errorcodes +#include "iso7816/iso7816core.h" // APDU logging +#include "util_posix.h" // msleep +#include "mifare/desfire_crypto.h" + +const CLIParserOption DesfireAlgoOpts[] = { + {T_DES, "des"}, + {T_3DES, "2tdea"}, + {T_3K3DES, "3tdea"}, + {T_AES, "aes"}, + {0, NULL}, +}; +const size_t DesfireAlgoOptsLen = ARRAY_LENGTH(DesfireAlgoOpts); + +const CLIParserOption DesfireKDFAlgoOpts[] = { + {MFDES_KDF_ALGO_NONE, "none"}, + {MFDES_KDF_ALGO_AN10922, "an10922"}, + {MFDES_KDF_ALGO_GALLAGHER, "gallagher"}, + {0, NULL}, +}; +const size_t DesfireKDFAlgoOptsLen = ARRAY_LENGTH(DesfireKDFAlgoOpts); + +const CLIParserOption DesfireCommunicationModeOpts[] = { + {DCMPlain, "plain"}, + {DCMMACed, "mac"}, + {DCMEncrypted, "encrypt"}, + {0, NULL}, +}; +const size_t DesfireCommunicationModeOptsLen = ARRAY_LENGTH(DesfireCommunicationModeOpts); + +const CLIParserOption DesfireCommandSetOpts[] = { + {DCCNative, "native"}, + {DCCNativeISO, "niso"}, + {DCCISO, "iso"}, + {0, NULL}, +}; +const size_t DesfireCommandSetOptsLen = ARRAY_LENGTH(DesfireCommandSetOpts); + +const CLIParserOption DesfireSecureChannelOpts[] = { + {DACd40, "d40"}, + {DACEV1, "ev1"}, + {DACEV2, "ev2"}, + {0, NULL}, +}; +const size_t DesfireSecureChannelOptsLen = ARRAY_LENGTH(DesfireSecureChannelOpts); + +static const char *getstatus(uint16_t *sw) { + if (sw == NULL) return "--> sw argument error. This should never happen !"; + if (((*sw >> 8) & 0xFF) == 0x91) { + switch (*sw & 0xFF) { + case MFDES_E_OUT_OF_EEPROM: + return "Out of Eeprom, insufficient NV-Memory to complete command"; + case MFDES_E_ILLEGAL_COMMAND_CODE: + return "Command code not supported"; + + case MFDES_E_INTEGRITY_ERROR: + return "CRC or MAC does not match data / Padding bytes invalid"; + + case MFDES_E_NO_SUCH_KEY: + return "Invalid key number specified"; + + case MFDES_E_LENGTH: + return "Length of command string invalid"; + + case MFDES_E_PERMISSION_DENIED: + return "Current configuration/status does not allow the requested command"; + + case MFDES_E_PARAMETER_ERROR: + return "Value of the parameter(s) invalid"; + + case MFDES_E_APPLICATION_NOT_FOUND: + return "Requested AID not present on PICC"; + + case MFDES_E_APPL_INTEGRITY: + return "Application integrity error, application will be disabled"; + + case MFDES_E_AUTHENTIFICATION_ERROR: + return "Current authentication status does not allow the requested command"; + + case MFDES_E_BOUNDARY: + return "Attempted to read/write data from/to beyond the file's/record's limit"; + + case MFDES_E_PICC_INTEGRITY: + return "PICC integrity error, PICC will be disabled"; + + case MFDES_E_COMMAND_ABORTED: + return "Previous command was not fully completed / Not all Frames were requested or provided by the PCD"; + + case MFDES_E_PICC_DISABLED: + return "PICC was disabled by an unrecoverable error"; + + case MFDES_E_COUNT: + return "Application count is limited to 28, not addition CreateApplication possible"; + + case MFDES_E_DUPLICATE: + return "Duplicate entry: File/Application/ISO Text does already exist"; + + case MFDES_E_EEPROM: + return "Eeprom error due to loss of power, internal backup/rollback mechanism activated"; + + case MFDES_E_FILE_NOT_FOUND: + return "Specified file number does not exist"; + + case MFDES_E_FILE_INTEGRITY: + return "File integrity error, file will be disabled"; + + default: + return "Unknown error"; + } + } + return "Unknown error"; +} + +const char *DesfireGetErrorString(int res, uint16_t *sw) { + switch (res) { + case PM3_EAPDU_FAIL: + return getstatus(sw); + case PM3_EUNDEF: + return "Undefined error"; + case PM3_EINVARG: + return "Invalid argument(s)"; + case PM3_EDEVNOTSUPP: + return "Operation not supported by device"; + case PM3_ETIMEOUT: + return "Operation timed out"; + case PM3_EOPABORTED: + return "Operation aborted (by user)"; + case PM3_ENOTIMPL: + return "Not (yet) implemented"; + case PM3_ERFTRANS: + return "Error while RF transmission"; + case PM3_EIO: + return "Input / output error"; + case PM3_EOVFLOW: + return "Buffer overflow"; + case PM3_ESOFT: + return "Software error"; + case PM3_EFLASH: + return "Flash error"; + case PM3_EMALLOC: + return "Memory allocation error"; + case PM3_EFILE: + return "File error"; + case PM3_ENOTTY: + return "Generic TTY error"; + case PM3_EINIT: + return "Initialization error"; + case PM3_EWRONGANSWER: + return "Expected a different answer error"; + case PM3_EOUTOFBOUND: + return "Memory out-of-bounds error"; + case PM3_ECARDEXCHANGE: + return "Exchange with card error"; + case PM3_EAPDU_ENCODEFAIL: + return "Failed to create APDU"; + case PM3_ENODATA: + return "No data"; + case PM3_EFATAL: + return "Fatal error"; + default: + break; + } + return ""; +} + +uint32_t DesfireAIDByteToUint(uint8_t *data) { + return data[0] + (data[1] << 8) + (data[2] << 16); +} + +void DesfireAIDUintToByte(uint32_t aid, uint8_t *data) { + data[0] = aid & 0xff; + data[1] = (aid >> 8) & 0xff; + data[2] = (aid >> 16) & 0xff; +} + +void DesfireClearContext(DesfireContext *ctx) { + ctx->keyNum = 0; + ctx->keyType = T_DES; + memset(ctx->key, 0, sizeof(ctx->key)); + + ctx->secureChannel = DACNone; + ctx->cmdSet = DCCNative; + ctx->commMode = DCMNone; + + ctx->kdfAlgo = 0; + ctx->kdfInputLen = 0; + memset(ctx->kdfInput, 0, sizeof(ctx->kdfInput)); + + DesfireClearSession(ctx); +} + +void DesfireClearSession(DesfireContext *ctx) { + ctx->secureChannel = DACNone; // here none - not authenticared + + memset(ctx->IV, 0, sizeof(ctx->IV)); + memset(ctx->sessionKeyMAC, 0, sizeof(ctx->sessionKeyMAC)); + memset(ctx->sessionKeyEnc, 0, sizeof(ctx->sessionKeyEnc)); + memset(ctx->lastIV, 0, sizeof(ctx->lastIV)); + ctx->cntrTx = 0; + ctx->cntrRx = 0; + memset(ctx->TI, 0, sizeof(ctx->TI)); +} + +void DesfirePrintContext(DesfireContext *ctx) { + PrintAndLogEx(INFO, "Key num: %d Key algo: %s Key[%d]: %s", + ctx->keyNum, + CLIGetOptionListStr(DesfireAlgoOpts, ctx->keyType), + desfire_get_key_length(ctx->keyType), + sprint_hex(ctx->key, + desfire_get_key_length(ctx->keyType))); + + if (ctx->kdfAlgo != MFDES_KDF_ALGO_NONE) + PrintAndLogEx(INFO, "KDF algo: %s KDF input[%d]: %s", CLIGetOptionListStr(DesfireKDFAlgoOpts, ctx->kdfAlgo), ctx->kdfInputLen, sprint_hex(ctx->kdfInput, ctx->kdfInputLen)); + + PrintAndLogEx(INFO, "Secure channel: %s Command set: %s Communication mode: %s", + CLIGetOptionListStr(DesfireSecureChannelOpts, ctx->secureChannel), + CLIGetOptionListStr(DesfireCommandSetOpts, ctx->cmdSet), + CLIGetOptionListStr(DesfireCommunicationModeOpts, ctx->commMode)); + + if (DesfireIsAuthenticated(ctx)) { + PrintAndLogEx(INFO, "Session key MAC [%d]: %s ENC: %s IV [%d]: %s", + desfire_get_key_length(ctx->keyType), + sprint_hex(ctx->sessionKeyMAC, desfire_get_key_length(ctx->keyType)), + sprint_hex(ctx->sessionKeyEnc, desfire_get_key_length(ctx->keyType)), + desfire_get_key_block_length(ctx->keyType), + sprint_hex(ctx->sessionKeyEnc, desfire_get_key_block_length(ctx->keyType))); + + } + } + +void DesfireSetKey(DesfireContext *ctx, uint8_t keyNum, enum DESFIRE_CRYPTOALGO keyType, uint8_t *key) { + DesfireClearContext(ctx); + + ctx->keyNum = keyNum; + ctx->keyType = keyType; + memcpy(ctx->key, key, desfire_get_key_length(keyType)); +} + +void DesfireSetCommandSet(DesfireContext *ctx, DesfireCommandSet cmdSet) { + ctx->cmdSet = cmdSet; +} + +void DesfireSetCommMode(DesfireContext *ctx, DesfireCommunicationMode commMode) { + ctx->commMode = commMode; +} + +void DesfireSetKdf(DesfireContext *ctx, uint8_t kdfAlgo, uint8_t *kdfInput, uint8_t kdfInputLen) { + ctx->kdfAlgo = kdfAlgo; + ctx->kdfInputLen = kdfInputLen; + if (kdfInputLen) + memcpy(ctx->kdfInput, kdfInput, kdfInputLen); +} + +static int DESFIRESendApdu(bool activate_field, sAPDU apdu, uint8_t *result, uint32_t max_result_len, uint32_t *result_len, uint16_t *sw) { + if (result_len) *result_len = 0; + if (sw) *sw = 0; + + uint16_t isw = 0; + int res = 0; + + if (activate_field) { + DropField(); + msleep(50); + } + + uint8_t data[APDU_RES_LEN] = {0}; + + // COMPUTE APDU + int datalen = 0; + if (APDUEncodeS(&apdu, false, 0x100, data, &datalen)) { // 100 == with Le + PrintAndLogEx(ERR, "APDU encoding error."); + return PM3_EAPDU_ENCODEFAIL; + } + + if (GetAPDULogging()) + PrintAndLogEx(SUCCESS, ">>>> %s", sprint_hex(data, datalen)); + + res = ExchangeAPDU14a(data, datalen, activate_field, true, result, max_result_len, (int *)result_len); + if (res != PM3_SUCCESS) { + return res; + } + + if (GetAPDULogging()) + PrintAndLogEx(SUCCESS, "<<<< %s", sprint_hex(result, *result_len)); + + if (*result_len < 2) { + return PM3_SUCCESS; + } + + *result_len -= 2; + isw = (result[*result_len] << 8) + result[*result_len + 1]; + if (sw) + *sw = isw; + + if (isw != 0x9000 && + isw != DESFIRE_GET_ISO_STATUS(MFDES_S_OPERATION_OK) && + isw != DESFIRE_GET_ISO_STATUS(MFDES_S_SIGNATURE) && + isw != DESFIRE_GET_ISO_STATUS(MFDES_S_ADDITIONAL_FRAME) && + isw != DESFIRE_GET_ISO_STATUS(MFDES_S_NO_CHANGES)) { + if (GetAPDULogging()) { + if (isw >> 8 == 0x61) { + PrintAndLogEx(ERR, "APDU chaining len: 0x%02x -->", isw & 0xff); + } else { + PrintAndLogEx(ERR, "APDU(%02x%02x) ERROR: [0x%4X] %s", apdu.CLA, apdu.INS, isw, GetAPDUCodeDescription(isw >> 8, isw & 0xff)); + return PM3_EAPDU_FAIL; + } + } + return PM3_EAPDU_FAIL; + } + return PM3_SUCCESS; +} + +static int DESFIRESendRaw(bool activate_field, uint8_t *data, size_t datalen, uint8_t *result, uint32_t max_result_len, uint32_t *result_len, uint8_t *respcode) { + *result_len = 0; + if (respcode) *respcode = 0xff; + + if (activate_field) { + DropField(); + msleep(50); + } + + if (GetAPDULogging()) + PrintAndLogEx(SUCCESS, "raw>> %s", sprint_hex(data, datalen)); + + int res = ExchangeRAW14a(data, datalen, activate_field, true, result, max_result_len, (int *)result_len, true); + if (res != PM3_SUCCESS) { + return res; + } + + if (GetAPDULogging()) + PrintAndLogEx(SUCCESS, "raw<< %s", sprint_hex(result, *result_len)); + + if (*result_len < 1) { + return PM3_SUCCESS; + } + + *result_len -= 1 + 2; + uint8_t rcode = result[0]; + if (respcode) *respcode = rcode; + memmove(&result[0], &result[1], *result_len); + + if (rcode != MFDES_S_OPERATION_OK && + rcode != MFDES_S_SIGNATURE && + rcode != MFDES_S_ADDITIONAL_FRAME && + rcode != MFDES_S_NO_CHANGES) { + if (GetAPDULogging()) + PrintAndLogEx(ERR, "Command (%02x) ERROR: 0x%02x", data[0], rcode); + return PM3_EAPDU_FAIL; + } + return PM3_SUCCESS; +} + +static int DesfireExchangeNative(bool activate_field, DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *respcode, uint8_t *resp, size_t *resplen, bool enable_chaining, size_t splitbysize) { + if (resplen) + *resplen = 0; + if (respcode) + *respcode = 0xff; + + uint8_t buf[255 * 5] = {0x00}; + uint32_t buflen = 0; + uint32_t pos = 0; + uint32_t i = 1; + + uint8_t rcode = 0xff; + uint8_t cdata[255] = {0}; + uint32_t cdatalen = 0; + cdata[0] = cmd; + memcpy(&cdata[1], data, datalen); + cdatalen = datalen + 1; + + int res = DESFIRESendRaw(activate_field, cdata, cdatalen, buf, sizeof(buf), &buflen, &rcode); + if (res != PM3_SUCCESS) { + uint16_t ssw = DESFIRE_GET_ISO_STATUS(rcode); + PrintAndLogEx(DEBUG, "error DESFIRESendRaw %s", DesfireGetErrorString(res, &ssw)); + return res; + } + + if (resp) { + if (splitbysize) { + resp[0] = buflen; + memcpy(&resp[1], buf, buflen); + } else { + memcpy(resp, buf, buflen); + } + } + if (respcode != NULL) + *respcode = rcode; + + pos += buflen; + if (!enable_chaining) { + if (rcode == MFDES_S_OPERATION_OK || + rcode == MFDES_ADDITIONAL_FRAME) { + if (resplen) + *resplen = pos; + } + return PM3_SUCCESS; + } + + while (rcode == MFDES_ADDITIONAL_FRAME) { + cdata[0] = MFDES_ADDITIONAL_FRAME; //0xAF + + res = DESFIRESendRaw(false, cdata, 1, buf, sizeof(buf), &buflen, &rcode); + if (res != PM3_SUCCESS) { + uint16_t ssw = DESFIRE_GET_ISO_STATUS(rcode); + PrintAndLogEx(DEBUG, "error DESFIRESendRaw %s", DesfireGetErrorString(res, &ssw)); + return res; + } + + if (respcode != NULL) + *respcode = rcode; + + if (resp != NULL) { + if (splitbysize) { + resp[i * splitbysize] = buflen; + memcpy(&resp[i * splitbysize + 1], buf, buflen); + i += 1; + } else { + memcpy(&resp[pos], buf, buflen); + } + } + pos += buflen; + + if (rcode != MFDES_ADDITIONAL_FRAME) break; + } + + if (resplen) + *resplen = (splitbysize) ? i : pos; + + return PM3_SUCCESS; +} + +static int DesfireExchangeISO(bool activate_field, DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *respcode, uint8_t *resp, size_t *resplen, bool enable_chaining, size_t splitbysize) { + if (resplen) + *resplen = 0; + if (respcode) + *respcode = 0xff; + + uint16_t sw = 0; + uint8_t buf[255 * 5] = {0x00}; + uint32_t buflen = 0; + uint32_t pos = 0; + uint32_t i = 1; + + sAPDU apdu = {0}; + apdu.CLA = MFDES_NATIVE_ISO7816_WRAP_CLA; //0x90 + apdu.INS = cmd; + apdu.Lc = datalen; + apdu.P1 = 0; + apdu.P2 = 0; + apdu.data = data; + + int res = DESFIRESendApdu(activate_field, apdu, buf, sizeof(buf), &buflen, &sw); + if (res != PM3_SUCCESS) { + PrintAndLogEx(DEBUG, "error DESFIRESendApdu %s", DesfireGetErrorString(res, &sw)); + return res; + } + + if (respcode != NULL && ((sw & 0xff00) == 0x9100)) + *respcode = sw & 0xff; + + if (resp) { + if (splitbysize) { + resp[0] = buflen; + memcpy(&resp[1], buf, buflen); + } else { + memcpy(resp, buf, buflen); + } + } + + pos += buflen; + if (!enable_chaining) { + if (sw == DESFIRE_GET_ISO_STATUS(MFDES_S_OPERATION_OK) || + sw == DESFIRE_GET_ISO_STATUS(MFDES_ADDITIONAL_FRAME)) { + if (resplen) + *resplen = pos; + } + return PM3_SUCCESS; + } + + while (sw == DESFIRE_GET_ISO_STATUS(MFDES_ADDITIONAL_FRAME)) { + apdu.CLA = MFDES_NATIVE_ISO7816_WRAP_CLA; //0x90 + apdu.INS = MFDES_ADDITIONAL_FRAME; //0xAF + apdu.Lc = 0; + apdu.P1 = 0; + apdu.P2 = 0; + apdu.data = NULL; + + res = DESFIRESendApdu(false, apdu, buf, sizeof(buf), &buflen, &sw); + if (res != PM3_SUCCESS) { + PrintAndLogEx(DEBUG, "error DESFIRESendApdu %s", DesfireGetErrorString(res, &sw)); + return res; + } + + if (respcode != NULL && ((sw & 0xff00) == 0x9100)) + *respcode = sw & 0xff; + + if (resp != NULL) { + if (splitbysize) { + resp[i * splitbysize] = buflen; + memcpy(&resp[i * splitbysize + 1], buf, buflen); + i += 1; + } else { + memcpy(&resp[pos], buf, buflen); + } + } + pos += buflen; + + if (sw != DESFIRE_GET_ISO_STATUS(MFDES_ADDITIONAL_FRAME)) break; + } + + if (resplen) + *resplen = (splitbysize) ? i : pos; + + return PM3_SUCCESS; +} + +static void DesfireCryptoEncDec(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, bool encode) { + uint8_t data[1024] = {0}; + + switch(ctx->keyType) { + case T_DES: + if (ctx->secureChannel == DACd40) { + if (encode) + des_encrypt_ecb(data, srcdata, srcdatalen, ctx->key); + else + des_decrypt_ecb(data, srcdata, srcdatalen, ctx->key); + } if (ctx->secureChannel == DACEV1) { + if (encode) + des_encrypt_cbc(data, srcdata, srcdatalen, ctx->key, ctx->IV); + else + des_decrypt_cbc(data, srcdata, srcdatalen, ctx->key, ctx->IV); + } + + if (dstdata) + memcpy(dstdata, data, srcdatalen); + break; + case T_3DES: + break; + case T_3K3DES: + break; + case T_AES: + if (encode) + aes_encode(ctx->IV, ctx->key, srcdata, data, srcdatalen); + else + aes_decode(ctx->IV, ctx->key, srcdata, data, srcdatalen); + if (dstdata) + memcpy(dstdata, data, srcdatalen); + break; + } +} + +static void DesfireSecureChannelEncodeD40(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) { + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + + uint8_t data[1024] = {0}; + size_t rlen = 0; + + switch(ctx->commMode) { + case DCMPlain: + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + break; + case DCMMACed: + if (srcdatalen == 0) + break; + + rlen = padded_data_length(srcdatalen, desfire_get_key_block_length(ctx->keyType)); + memcpy(data, srcdata, srcdatalen); + DesfireCryptoEncDec(ctx, data, rlen, NULL, true); + memcpy(dstdata, srcdata, srcdatalen); + memcpy(&dstdata[srcdatalen], ctx->IV, desfire_get_key_block_length(ctx->keyType)); + *dstdatalen = rlen; + break; + case DCMEncrypted: + rlen = padded_data_length(srcdatalen + 2, desfire_get_key_block_length(ctx->keyType)); // 2 - crc16 + memcpy(data, srcdata, srcdatalen); + compute_crc(CRC_14443_A, data, srcdatalen, &data[srcdatalen], &data[srcdatalen + 1]); + DesfireCryptoEncDec(ctx, data, rlen, dstdata, true); + *dstdatalen = rlen; + break; + case DCMNone:; + } +} + +static void DesfireSecureChannelEncodeEV1(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) { + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + + switch(ctx->commMode) { + case DCMPlain: + case DCMMACed: + + break; + case DCMEncrypted: + break; + case DCMNone:; + } +} + +static void DesfireSecureChannelEncode(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) { + switch(ctx->secureChannel) { + case DACd40: + DesfireSecureChannelEncodeD40(ctx, cmd, srcdata, srcdatalen, dstdata, dstdatalen); + break; + case DACEV1: + DesfireSecureChannelEncodeEV1(ctx, cmd, srcdata, srcdatalen, dstdata, dstdatalen); + break; + case DACEV2: + break; + case DACNone: + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + break; + } +} + +static void DesfireSecureChannelDecodeD40(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t respcode, uint8_t *dstdata, size_t *dstdatalen) { + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + + switch(ctx->commMode) { + case DCMMACed: + + break; + case DCMEncrypted: + break; + case DCMPlain: + case DACNone: + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + break; + } +} + +static void DesfireSecureChannelDecodeEV1(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t respcode, uint8_t *dstdata, size_t *dstdatalen) { + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + + switch(ctx->commMode) { + case DCMPlain: + case DCMMACed: + memcpy(dstdata, srcdata, srcdatalen - 8); + *dstdatalen = srcdatalen - 8; + + break; + case DCMEncrypted: + break; + case DACNone: + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + break; + } +} + +static void DesfireSecureChannelDecode(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t respcode, uint8_t *dstdata, size_t *dstdatalen) { + switch(ctx->secureChannel) { + case DACd40: + DesfireSecureChannelDecodeD40(ctx, srcdata, srcdatalen, respcode, dstdata, dstdatalen); + break; + case DACEV1: + DesfireSecureChannelDecodeEV1(ctx, srcdata, srcdatalen, respcode, dstdata, dstdatalen); + break; + case DACEV2: + break; + case DACNone: + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + break; + } +} + +// move data from blockdata [format: ...] to single data block +static void DesfireJoinBlockToBytes(uint8_t *blockdata, size_t blockdatacount, size_t blockdatasize, uint8_t *dstdata, size_t *dstdatalen) { + *dstdatalen = 0; + for(int i = 0; i < blockdatacount; i++) { + memcpy(&dstdata[*dstdatalen], &blockdata[i * blockdatasize + 1], blockdata[i * blockdatasize]); + *dstdatalen += blockdata[i * blockdatasize]; + } +} + +// move data from single data block to blockdata [format: ...] +// lengths in the blockdata is not changed. result - in the blockdata +static void DesfireSplitBytesToBlock(uint8_t *blockdata, size_t *blockdatacount, size_t blockdatasize, uint8_t *dstdata, size_t dstdatalen) { + size_t len = 0; + for(int i = 0; i < *blockdatacount; i++) { + size_t tlen = len + blockdata[i * blockdatasize]; + if (tlen > dstdatalen) + tlen = dstdatalen; + if (len == tlen) { + *blockdatacount = i; + break; + } + memcpy(&blockdata[i * blockdatasize + 1], &dstdata[len], tlen - len); + len = tlen; + } +} + +int DesfireExchangeEx(bool activate_field, DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *respcode, uint8_t *resp, size_t *resplen, bool enable_chaining, size_t splitbysize) { + int res = PM3_SUCCESS; + + uint8_t databuf[250 * 5] = {0}; + size_t databuflen = 0; + + switch(ctx->cmdSet) { + case DCCNative: + case DCCNativeISO: + DesfireSecureChannelEncode(ctx, cmd, data, datalen, databuf, &databuflen); + + if (ctx->cmdSet == DCCNative) + res = DesfireExchangeNative(activate_field, ctx, cmd, databuf, databuflen, respcode, databuf, &databuflen, enable_chaining, splitbysize); + else + res = DesfireExchangeISO(activate_field, ctx, cmd, databuf, databuflen, respcode, databuf, &databuflen, enable_chaining, splitbysize); + + if (splitbysize) { + uint8_t sdata[250 * 5] = {0}; + size_t sdatalen = 0; + DesfireJoinBlockToBytes(databuf, databuflen, splitbysize, sdata, &sdatalen); + + //PrintAndLogEx(INFO, "block : %s", sprint_hex(sdata, sdatalen)); + DesfireSecureChannelDecode(ctx, sdata, sdatalen, *respcode, resp, resplen); + + DesfireSplitBytesToBlock(databuf, &databuflen, splitbysize, resp, *resplen); + memcpy(resp, databuf, databuflen * splitbysize); + *resplen = databuflen; + } else { + DesfireSecureChannelDecode(ctx, databuf, databuflen, *respcode, resp, resplen); + } + break; + case DCCISO: + return PM3_EAPDU_FAIL; + break; + } + + return res; +} + +int DesfireExchange(DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *respcode, uint8_t *resp, size_t *resplen) { + return DesfireExchangeEx(false, ctx, cmd, data, datalen, respcode, resp, resplen, true, 0); +} + +int DesfireSelectAID(DesfireContext *ctx, uint8_t *aid1, uint8_t *aid2) { + if (aid1 == NULL) + return PM3_EINVARG; + + uint8_t data[6] = {0}; + memcpy(data, aid1, 3); + if (aid2 != NULL) + memcpy(&data[3], aid2, 3); + uint8_t resp[257] = {0}; + size_t resplen = 0; + uint8_t respcode = 0; + + int res = DesfireExchangeEx(true, ctx, MFDES_SELECT_APPLICATION, data, (aid2 == NULL) ? 3 : 6, &respcode, resp, &resplen, true, 0); + if (res == PM3_SUCCESS) { + if (resplen != 0) + return PM3_ECARDEXCHANGE; + + // select operation fail + if (respcode != MFDES_S_OPERATION_OK) + return PM3_EAPDU_FAIL; + + return PM3_SUCCESS; + } + return res; +} + +int DesfireSelectAIDHex(DesfireContext *ctx, uint32_t aid1, bool select_two, uint32_t aid2) { + uint8_t data[6] = {0}; + + DesfireAIDUintToByte(aid1, data); + DesfireAIDUintToByte(aid2, &data[3]); + + return DesfireSelectAID(ctx, data, (select_two) ? &data[3] : NULL); +} + +bool DesfireIsAuthenticated(DesfireContext *dctx) { + return dctx->secureChannel != DACNone; +} + +int DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel) { + // 3 different way to authenticate AUTH (CRC16) , AUTH_ISO (CRC32) , AUTH_AES (CRC32) + // 4 different crypto arg1 DES, 3DES, 3K3DES, AES + // 3 different communication modes, PLAIN,MAC,CRYPTO + + DesfireClearSession(dctx); + + if (secureChannel == DACNone) + return PM3_SUCCESS; + + mbedtls_aes_context ctx; + + uint8_t keybytes[24] = {0}; + // Crypt constants + uint8_t IV[16] = {0}; + uint8_t RndA[16] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}; + uint8_t RndB[16] = {0}; + uint8_t encRndB[16] = {0}; + uint8_t rotRndB[16] = {0}; //RndB' + uint8_t both[32 + 1] = {0}; // ek/dk_keyNo(RndA+RndB') + + // Part 1 + memcpy(keybytes, dctx->key, desfire_get_key_length(dctx->keyType)); + + struct desfire_key dkey = {0}; + desfirekey_t key = &dkey; + + if (dctx->keyType == T_AES) { + mbedtls_aes_init(&ctx); + Desfire_aes_key_new(keybytes, key); + } else if (dctx->keyType == T_3DES) { + Desfire_3des_key_new_with_version(keybytes, key); + } else if (dctx->keyType == T_DES) { + Desfire_des_key_new(keybytes, key); + } else if (dctx->keyType == T_3K3DES) { + Desfire_3k3des_key_new_with_version(keybytes, key); + } + + if (dctx->kdfAlgo == MFDES_KDF_ALGO_AN10922) { + mifare_kdf_an10922(key, dctx->kdfInput, dctx->kdfInputLen); + PrintAndLogEx(DEBUG, " Derrived key: " _GREEN_("%s"), sprint_hex(key->data, key_block_size(key))); + } else if (dctx->kdfAlgo == MFDES_KDF_ALGO_GALLAGHER) { + // We will overrite any provided KDF input since a gallagher specific KDF was requested. + dctx->kdfInputLen = 11; + + /*if (mfdes_kdf_input_gallagher(tag->info.uid, tag->info.uidlen, dctx->keyNum, tag->selected_application, dctx->kdfInput, &dctx->kdfInputLen) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Could not generate Gallagher KDF input"); + }*/ + + mifare_kdf_an10922(key, dctx->kdfInput, dctx->kdfInputLen); + PrintAndLogEx(DEBUG, " KDF Input: " _YELLOW_("%s"), sprint_hex(dctx->kdfInput, dctx->kdfInputLen)); + PrintAndLogEx(DEBUG, " Derrived key: " _GREEN_("%s"), sprint_hex(key->data, key_block_size(key))); + + } + + uint8_t subcommand = MFDES_AUTHENTICATE; + if (secureChannel == DACEV1) { + if (dctx->keyType == T_AES) + subcommand = MFDES_AUTHENTICATE_AES; + else + subcommand = MFDES_AUTHENTICATE_ISO; + } + + size_t recv_len = 0; + uint8_t respcode = 0; + uint8_t recv_data[256] = {0}; + + // Let's send our auth command + int res = DesfireExchangeEx(false, dctx, subcommand, &dctx->keyNum, 1, &respcode, recv_data, &recv_len, false, 0); + if (res != PM3_SUCCESS) { + return 1; + } + + if (!recv_len) { + return 2; + } + + if (respcode != MFDES_ADDITIONAL_FRAME) { + return 3; + } + + uint32_t expectedlen = 8; + if (dctx->keyType == T_AES || dctx->keyType == T_3K3DES) { + expectedlen = 16; + } + + if (recv_len != expectedlen) { + return 4; + } + + // Part 2 + uint32_t rndlen = recv_len; + memcpy(encRndB, recv_data, rndlen); + + + // Part 3 + if (dctx->keyType == T_AES) { + if (mbedtls_aes_setkey_dec(&ctx, key->data, 128) != 0) { + return 5; + } + mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_DECRYPT, rndlen, IV, encRndB, RndB); + } else if (dctx->keyType == T_DES) { + if (secureChannel == DACd40) + des_decrypt(RndB, encRndB, key->data); + if (secureChannel == DACEV1) + des_decrypt_cbc(RndB, encRndB, rndlen, key->data, IV); + } else if (dctx->keyType == T_3DES) + tdes_nxp_receive(encRndB, RndB, rndlen, key->data, IV, 2); + else if (dctx->keyType == T_3K3DES) { + tdes_nxp_receive(encRndB, RndB, rndlen, key->data, IV, 3); + } + + if (g_debugMode > 1) { + PrintAndLogEx(DEBUG, "encRndB: %s", sprint_hex(encRndB, 8)); + PrintAndLogEx(DEBUG, "RndB: %s", sprint_hex(RndB, 8)); + } + + // - Rotate RndB by 8 bits + memcpy(rotRndB, RndB, rndlen); + rol(rotRndB, rndlen); + + uint8_t encRndA[16] = {0x00}; + + // - Encrypt our response + if (secureChannel == DACd40) { + if (dctx->keyType == T_DES) { + des_decrypt(encRndA, RndA, key->data); + memcpy(both, encRndA, rndlen); + + for (uint32_t x = 0; x < rndlen; x++) { + rotRndB[x] = rotRndB[x] ^ encRndA[x]; + } + + des_decrypt(encRndB, rotRndB, key->data); + memcpy(both + rndlen, encRndB, rndlen); + } else if (dctx->keyType == T_3DES) { + //TODO + } + } else if (secureChannel == DACEV1 && dctx->keyType != T_AES) { + if (dctx->keyType == T_DES) { + uint8_t tmp[16] = {0x00}; + memcpy(tmp, RndA, rndlen); + memcpy(tmp + rndlen, rotRndB, rndlen); + if (g_debugMode > 1) { + PrintAndLogEx(DEBUG, "rotRndB: %s", sprint_hex(rotRndB, rndlen)); + PrintAndLogEx(DEBUG, "Both: %s", sprint_hex(tmp, 16)); + } + des_encrypt_cbc(both, tmp, 16, key->data, IV); + if (g_debugMode > 1) { + PrintAndLogEx(DEBUG, "EncBoth: %s", sprint_hex(both, 16)); + } + } else if (dctx->keyType == T_3DES) { + uint8_t tmp[16] = {0x00}; + memcpy(tmp, RndA, rndlen); + memcpy(tmp + rndlen, rotRndB, rndlen); + if (g_debugMode > 1) { + PrintAndLogEx(DEBUG, "rotRndB: %s", sprint_hex(rotRndB, rndlen)); + PrintAndLogEx(DEBUG, "Both: %s", sprint_hex(tmp, 16)); + } + tdes_nxp_send(tmp, both, 16, key->data, IV, 2); + if (g_debugMode > 1) { + PrintAndLogEx(DEBUG, "EncBoth: %s", sprint_hex(both, 16)); + } + } else if (dctx->keyType == T_3K3DES) { + uint8_t tmp[32] = {0x00}; + memcpy(tmp, RndA, rndlen); + memcpy(tmp + rndlen, rotRndB, rndlen); + if (g_debugMode > 1) { + PrintAndLogEx(DEBUG, "rotRndB: %s", sprint_hex(rotRndB, rndlen)); + PrintAndLogEx(DEBUG, "Both3k3: %s", sprint_hex(tmp, 32)); + } + tdes_nxp_send(tmp, both, 32, key->data, IV, 3); + if (g_debugMode > 1) { + PrintAndLogEx(DEBUG, "EncBoth: %s", sprint_hex(both, 32)); + } + } + } else if (secureChannel == DACEV1 && dctx->keyType == T_AES) { + uint8_t tmp[32] = {0x00}; + memcpy(tmp, RndA, rndlen); + memcpy(tmp + rndlen, rotRndB, rndlen); + if (g_debugMode > 1) { + PrintAndLogEx(DEBUG, "rotRndB: %s", sprint_hex(rotRndB, rndlen)); + PrintAndLogEx(DEBUG, "Both3k3: %s", sprint_hex(tmp, 32)); + } + if (dctx->keyType == T_AES) { + if (mbedtls_aes_setkey_enc(&ctx, key->data, 128) != 0) { + return 6; + } + mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_ENCRYPT, 32, IV, tmp, both); + if (g_debugMode > 1) { + PrintAndLogEx(DEBUG, "EncBoth: %s", sprint_hex(both, 32)); + } + } + } + + uint32_t bothlen = 16; + if (dctx->keyType == T_AES || dctx->keyType == T_3K3DES) { + bothlen = 32; + } + + res = DesfireExchangeEx(false, dctx, MFDES_ADDITIONAL_FRAME, both, bothlen, &respcode, recv_data, &recv_len, false, 0); + if (res != PM3_SUCCESS) { + return 7; + } + + if (!recv_len) { + return 8; + } + + if (respcode != MFDES_S_OPERATION_OK) { + return 9; + } + + // Part 4 + memcpy(encRndA, recv_data, rndlen); + + struct desfire_key sesskey = {0}; + + Desfire_session_key_new(RndA, RndB, key, &sesskey); + memcpy(dctx->sessionKeyEnc, sesskey.data, desfire_get_key_length(dctx->keyType)); + +PrintAndLogEx(INFO, "encRndA : %s", sprint_hex(encRndA, rndlen)); +PrintAndLogEx(INFO, "IV : %s", sprint_hex(IV, rndlen)); + if (dctx->keyType == T_DES){ + if (secureChannel == DACd40) + des_decrypt(encRndA, encRndA, key->data); + if (secureChannel == DACEV1) + des_decrypt_cbc(encRndA, encRndA, rndlen, key->data, IV); + } else if (dctx->keyType == T_3DES) + tdes_nxp_receive(encRndA, encRndA, rndlen, key->data, IV, 2); + else if (dctx->keyType == T_3K3DES) + tdes_nxp_receive(encRndA, encRndA, rndlen, key->data, IV, 3); + else if (dctx->keyType == T_AES) { + if (mbedtls_aes_setkey_dec(&ctx, key->data, 128) != 0) { + return 10; + } + mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_DECRYPT, rndlen, IV, encRndA, encRndA); + } + + rol(RndA, rndlen); +PrintAndLogEx(INFO, "Expected_RndA : %s", sprint_hex(RndA, rndlen)); +PrintAndLogEx(INFO, "Generated_RndA : %s", sprint_hex(encRndA, rndlen)); + for (uint32_t x = 0; x < rndlen; x++) { + if (RndA[x] != encRndA[x]) { + if (g_debugMode > 1) { + PrintAndLogEx(DEBUG, "Expected_RndA : %s", sprint_hex(RndA, rndlen)); + PrintAndLogEx(DEBUG, "Generated_RndA : %s", sprint_hex(encRndA, rndlen)); + } + return 11; + } + } + + // If the 3Des key first 8 bytes = 2nd 8 Bytes then we are really using Singe Des + // As such we need to set the session key such that the 2nd 8 bytes = 1st 8 Bytes + if (dctx->keyType == T_3DES) { + if (memcmp(key->data, &key->data[8], 8) == 0) + memcpy(&dctx->sessionKeyEnc[8], dctx->sessionKeyEnc, 8); + } + + if (secureChannel == DACEV1) { + cmac_generate_subkeys(&sesskey, MCD_RECEIVE); + //key->cmac_sk1 and key->cmac_sk2 + //memcpy(dctx->sessionKeyEnc, sesskey.data, desfire_get_key_length(dctx->keyType)); + } + + dctx->secureChannel = secureChannel; + memcpy(dctx->sessionKeyMAC, dctx->sessionKeyEnc, desfire_get_key_length(dctx->keyType)); +PrintAndLogEx(INFO, "sessionKeyEnc : %s", sprint_hex(dctx->sessionKeyEnc, desfire_get_key_length(dctx->keyType))); + + return PM3_SUCCESS; +} + +int DesfireGetAIDList(DesfireContext *dctx, uint8_t *resp, size_t *resplen) { + uint8_t respcode = 0xff; + int res = DesfireExchange(dctx, MFDES_GET_APPLICATION_IDS, NULL, 0, &respcode, resp, resplen); + if (res != PM3_SUCCESS) + return res; + if (respcode != MFDES_S_OPERATION_OK) + return PM3_EAPDU_FAIL; + return PM3_SUCCESS; +} + +int DesfireGetDFList(DesfireContext *dctx, uint8_t *resp, size_t *resplen) { + uint8_t respcode = 0xff; + int res = DesfireExchangeEx(false, dctx, MFDES_GET_DF_NAMES, NULL, 0, &respcode, resp, resplen, true, 24); + if (res != PM3_SUCCESS) + return res; + if (respcode != MFDES_S_OPERATION_OK) + return PM3_EAPDU_FAIL; + return PM3_SUCCESS; +} diff --git a/client/src/mifare/desfirecore.h b/client/src/mifare/desfirecore.h new file mode 100644 index 000000000..873e365a8 --- /dev/null +++ b/client/src/mifare/desfirecore.h @@ -0,0 +1,103 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2010 Romain Tartiere. +// Copyright (C) 2014 Iceman +// Copyright (C) 2021 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// High frequency Desfire core functions +//----------------------------------------------------------------------------- + +#ifndef __DESFIRECORE_H +#define __DESFIRECORE_H + +#include "common.h" +#include "cliparser.h" +#include "mifare/desfire_crypto.h" +#include "mifare/mifare4.h" + +#define DESF_MAX_KEY_LEN 24 + +#define DESFIRE_GET_ISO_STATUS(x) ( ((uint16_t)(0x91<<8)) + (uint16_t)x ) + +typedef enum DESFIRE_CRYPTOALGO DesfireCryptoAlgorythm; + +typedef enum { + DACNone, + DACd40, + DACEV1, + DACEV2 +} DesfireSecureChannel; + +typedef enum { + DCCNative, + DCCNativeISO, + DCCISO +} DesfireCommandSet; + +typedef enum { + DCMNone, + DCMPlain, + DCMMACed, + DCMEncrypted +} DesfireCommunicationMode; + + +typedef struct DesfireContextS { + uint8_t keyNum; + enum DESFIRE_CRYPTOALGO keyType; // des/2tdea/3tdea/aes + uint8_t key[DESF_MAX_KEY_LEN]; + + // KDF finction + uint8_t kdfAlgo; + uint8_t kdfInputLen; + uint8_t kdfInput[31]; + + DesfireSecureChannel secureChannel; // none/d40/ev1/ev2 + DesfireCommandSet cmdSet; // native/nativeiso/iso + DesfireCommunicationMode commMode; // plain/mac/enc + + uint8_t IV[DESF_MAX_KEY_LEN]; + uint8_t sessionKeyMAC[DESF_MAX_KEY_LEN]; + uint8_t sessionKeyEnc[DESF_MAX_KEY_LEN]; // look at mifare4.h - mf4Session_t + uint8_t lastIV[DESF_MAX_KEY_LEN]; + //mf4Session_t AESSession; + uint16_t cntrTx; // for AES + uint16_t cntrRx; // for AES + uint8_t TI[4]; // for AES +} DesfireContext; + +extern const CLIParserOption DesfireAlgoOpts[]; +extern const CLIParserOption DesfireKDFAlgoOpts[]; +extern const CLIParserOption DesfireCommunicationModeOpts[]; +extern const CLIParserOption DesfireCommandSetOpts[]; +extern const CLIParserOption DesfireSecureChannelOpts[]; + +void DesfireClearContext(DesfireContext *ctx); +void DesfirePrintContext(DesfireContext *ctx); +void DesfireClearSession(DesfireContext *ctx); +void DesfireSetKey(DesfireContext *ctx, uint8_t keyNum, enum DESFIRE_CRYPTOALGO keyType, uint8_t *key); +void DesfireSetCommandSet(DesfireContext *ctx, DesfireCommandSet cmdSet); +void DesfireSetCommMode(DesfireContext *ctx, DesfireCommunicationMode commMode); +void DesfireSetKdf(DesfireContext *ctx, uint8_t kdfAlgo,uint8_t *kdfInput, uint8_t kdfInputLen); + +const char *DesfireGetErrorString(int res, uint16_t *sw); +uint32_t DesfireAIDByteToUint(uint8_t *data); +void DesfireAIDUintToByte(uint32_t aid, uint8_t *data); + +int DesfireExchange(DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *respcode, uint8_t *resp, size_t *resplen); +int DesfireExchangeEx(bool activate_field, DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *respcode, uint8_t *resp, size_t *resplen, bool enable_chaining, size_t splitbysize); + +int DesfireSelectAID(DesfireContext *ctx, uint8_t *aid1, uint8_t *aid2); +int DesfireSelectAIDHex(DesfireContext *ctx, uint32_t aid1, bool select_two, uint32_t aid2); + +int DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel); +bool DesfireIsAuthenticated(DesfireContext *dctx); + +int DesfireGetAIDList(DesfireContext *dctx, uint8_t *resp, size_t *resplen); +int DesfireGetDFList(DesfireContext *dctx, uint8_t *resp, size_t *resplen); + + +#endif // __DESFIRECORE_H diff --git a/client/src/util.h b/client/src/util.h index 2730ed44f..9357eb1d0 100644 --- a/client/src/util.h +++ b/client/src/util.h @@ -16,6 +16,8 @@ #include #endif +#define ARRAY_LENGTH( array ) ( sizeof( array ) / sizeof( *( array ) ) ) + // used for save/load files #ifndef FILE_PATH_SIZE # define FILE_PATH_SIZE 1000 diff --git a/include/protocols.h b/include/protocols.h index 4b3cf763f..66385bcb9 100644 --- a/include/protocols.h +++ b/include/protocols.h @@ -459,6 +459,7 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define MFDES_GET_FILE_SETTINGS 0xF5 #define MFDES_FORMAT_PICC 0xFC #define MFDES_VERIFY_PC 0xFD +#define MFDES_NATIVE_ISO7816_WRAP_CLA 0x90 // MIFARE DESFire status & error codes: #define MFDES_S_OPERATION_OK 0x00