Merge pull request #1429 from merlokk/desf_iso

Desfire iso commands
This commit is contained in:
Oleg Moiseenko 2021-08-06 23:20:43 +03:00 committed by GitHub
commit e176993097
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 495 additions and 34 deletions

View file

@ -4922,6 +4922,99 @@ static int CmdHF14ADesClearRecordFile(const char *Cmd) {
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int DesfileReadISOFileAndPrint(DesfireContext *dctx, bool select_current_file, uint8_t fnum, uint16_t fisoid, int filetype, uint32_t offset, uint32_t length, bool noauth, bool verbose) {
if (filetype == RFTAuto) {
PrintAndLogEx(ERR, "ISO mode needs to specify file type");
return PM3_EINVARG;
}
if (filetype == RFTValue) {
PrintAndLogEx(ERR, "ISO mode can't read Value file type");
return PM3_EINVARG;
}
if (filetype == RFTMAC) {
PrintAndLogEx(ERR, "ISO mode can't read Transaction MAC file type");
return PM3_EINVARG;
}
if (select_current_file)
PrintAndLogEx(INFO, "------------------------------- " _CYAN_("File ISO %04x data") " -------------------------------", fisoid);
else
PrintAndLogEx(INFO, "---------------------------- " _CYAN_("File ISO short %02x data") " ----------------------------", fnum);
uint8_t resp[2048] = {0};
size_t resplen = 0;
int res = 0;
if (filetype == RFTData) {
res = DesfireISOReadBinary(dctx, !select_current_file, (select_current_file) ? 0x00 : fnum, offset, length, resp, &resplen);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Desfire ISOReadBinary command " _RED_("error") ". Result: %d", res);
DropField();
return PM3_ESOFT;
}
if (resplen > 0) {
if (select_current_file)
PrintAndLogEx(SUCCESS, "Read %zu bytes from file 0x%04x offset %u", resplen, fisoid, offset);
else
PrintAndLogEx(SUCCESS, "Read %zu bytes from file 0x%02x offset %u", resplen, fnum, offset);
print_buffer_with_offset(resp, resplen, offset, true);
} else {
if (select_current_file)
PrintAndLogEx(SUCCESS, "Read operation returned no data from file %04x", fisoid);
else
PrintAndLogEx(SUCCESS, "Read operation returned no data from file %02x", fnum);
}
}
if (filetype == RFTRecord) {
size_t reclen = 0;
res = DesfireISOReadRecords(dctx, offset, false, (select_current_file) ? 0x00 : fnum, 0, resp, &resplen);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Desfire ISOReadRecords (one record) command " _RED_("error") ". Result: %d", res);
DropField();
return PM3_ESOFT;
}
reclen = resplen;
if (verbose)
PrintAndLogEx(INFO, "Record length %zu", reclen);
if (length != 1) {
res = DesfireISOReadRecords(dctx, offset, true, (select_current_file) ? 0x00 : fnum, 0, resp, &resplen);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Desfire ISOReadRecords (one record) command " _RED_("error") ". Result: %d", res);
DropField();
return PM3_ESOFT;
}
}
if (resplen > 0) {
size_t reccount = resplen / reclen;
if (select_current_file)
PrintAndLogEx(SUCCESS, "Read %zu bytes from file 0x%04x from record %d record count %zu record length %zu", resplen, fisoid, offset, reccount, reclen);
else
PrintAndLogEx(SUCCESS, "Read %zu bytes from file 0x%02x from record %d record count %zu record length %zu", resplen, fnum, offset, reccount, reclen);
if (reccount > 1)
PrintAndLogEx(SUCCESS, "Lastest record at the bottom.");
for (int i = 0; i < reccount; i++) {
if (i != 0)
PrintAndLogEx(SUCCESS, "Record %zu", reccount - (i + offset + 1));
print_buffer_with_offset(&resp[i * reclen], reclen, offset, (i == 0));
}
} else {
if (select_current_file)
PrintAndLogEx(SUCCESS, "Read operation returned no data from file %04x", fisoid);
else
PrintAndLogEx(SUCCESS, "Read operation returned no data from file %02x", fnum);
}
}
return PM3_SUCCESS;
}
static int DesfileReadFileAndPrint(DesfireContext *dctx, uint8_t fnum, int filetype, uint32_t offset, uint32_t length, bool noauth, bool verbose) { static int DesfileReadFileAndPrint(DesfireContext *dctx, uint8_t fnum, int filetype, uint32_t offset, uint32_t length, bool noauth, bool verbose) {
int res = 0; int res = 0;
@ -5085,8 +5178,17 @@ static int CmdHF14ADesReadData(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mfdes read", CLIParserInit(&ctx, "hf mfdes read",
"Read data from file. Key needs to be provided or flag --no-auth set (depend on file settings).", "Read data from file. Key needs to be provided or flag --no-auth set (depend on file settings).",
"It reads file via all command sets. \n"
"For ISO command set it can be read by specifying full 2-byte iso id or 1-byte short iso id (first byte of the full iso id). ISO id lays in the data in BIG ENDIAN format.\n"
"ISO record commands: offset - record number (0-current, 1..ff-number, 1-lastest), length - if 0 - all records, if 1 - one\n"
"\n"
"hf mfdes read --aid 123456 --fid 01 -> read file: app=123456, file=01, offset=0, all the data. use default channel settings from `default` command\n" "hf mfdes read --aid 123456 --fid 01 -> read file: app=123456, file=01, offset=0, all the data. use default channel settings from `default` command\n"
"hf mfdes read --aid 123456 --fid 01 --type record --offset 000000 --length 000001 -> read one last record from record file. use default channel settings from `default` command"); "hf mfdes read --aid 123456 --fid 01 --type record --offset 000000 --length 000001 -> read one last record from record file. use default channel settings from `default` command\n"
"hf mfdes read --aid 123456 --fid 10 --type data -c iso -> read file via ISO channel: app=123456, short iso id=10, offset=0.\n"
"hf mfdes read --aid 123456 --fileisoid 1000 --type data -c iso -> read file via ISO channel: app=123456, iso id=1000, offset=0. Select via native ISO wrapper\n"
"hf mfdes read --appisoid 0102 --fileisoid 1000 --type data -c iso -> read file via ISO channel: app iso id=0102, iso id=1000, offset=0. Select via ISO commands\n"
"hf mfdes read --appisoid 0102 --fileisoid 1100 --type record -c iso --offset 000005 --length 000001 -> get one record (number 5) from file 1100 via iso commands\n"
"hf mfdes read --appisoid 0102 --fileisoid 1100 --type record -c iso --offset 000005 --length 000000 -> get all record (from 5 to 1) from file 1100 via iso commands");
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
@ -5106,6 +5208,8 @@ static int CmdHF14ADesReadData(const char *Cmd) {
arg_str0(NULL, "type", "<auto/data/value/record/mac>", "File Type auto/data(Standard/Backup)/value/record(linear/cyclic)/mac). Auto - check file settings and then read. Default: auto"), arg_str0(NULL, "type", "<auto/data/value/record/mac>", "File Type auto/data(Standard/Backup)/value/record(linear/cyclic)/mac). Auto - check file settings and then read. Default: auto"),
arg_str0("o", "offset", "<hex>", "File Offset (3 hex bytes, big endian). For records - record number (0 - lastest record). Default 0"), arg_str0("o", "offset", "<hex>", "File Offset (3 hex bytes, big endian). For records - record number (0 - lastest record). Default 0"),
arg_str0("l", "length", "<hex>", "Length to read (3 hex bytes, big endian -> 000000 = Read all data). For records - records count (0 - all). Default 0."), arg_str0("l", "length", "<hex>", "Length to read (3 hex bytes, big endian -> 000000 = Read all data). For records - records count (0 - all). Default 0."),
arg_str0(NULL, "appisoid", "<isoid hex>", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian). Works only for ISO read commands."),
arg_str0(NULL, "fileisoid", "<isoid hex>", "File ISO ID (ISO DF ID) (2 hex bytes, big endian). Works only for ISO read commands."),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, false); CLIExecWithReturn(ctx, Cmd, argtable, false);
@ -5153,6 +5257,24 @@ static int CmdHF14ADesReadData(const char *Cmd) {
return PM3_EINVARG; return PM3_EINVARG;
} }
uint32_t appisoid = 0x0000;
res = arg_get_u32_hexstr_def_nlen(ctx, 17, 0x0000, &appisoid, 2, true);
bool isoidpresent = (res == 1);
if (res == 2) {
PrintAndLogEx(ERR, "Application ISO ID (for EF) must have 2 bytes length");
CLIParserFree(ctx);
return PM3_EINVARG;
}
uint32_t fileisoid = 0x0000;
res = arg_get_u32_hexstr_def_nlen(ctx, 18, 0x0000, &fileisoid, 2, true);
bool fileisoidpresent = (res == 1);
if (res == 2) {
PrintAndLogEx(ERR, "File ISO ID (for DF) must have 2 bytes length");
CLIParserFree(ctx);
return PM3_EINVARG;
}
SetAPDULogging(APDULogging); SetAPDULogging(APDULogging);
CLIParserFree(ctx); CLIParserFree(ctx);
@ -5161,18 +5283,80 @@ static int CmdHF14ADesReadData(const char *Cmd) {
return PM3_EINVARG; return PM3_EINVARG;
} }
res = DesfireSelectAndAuthenticateEx(&dctx, securechann, appid, noauth, verbose); if (!isoidpresent) {
if (res != PM3_SUCCESS) { res = DesfireSelectAndAuthenticateEx(&dctx, securechann, appid, noauth, verbose);
DropField(); if (res != PM3_SUCCESS) {
return res; DropField();
return res;
}
} else {
res = DesfireSelectAndAuthenticateISO(&dctx, securechann, (appid != 0), appid, appisoid, fileisoid, noauth, verbose);
if (res != PM3_SUCCESS) {
DropField();
return res;
}
} }
res = DesfileReadFileAndPrint(&dctx, fnum, op, offset, length, noauth, verbose); if (dctx.cmdSet != DCCISO)
res = DesfileReadFileAndPrint(&dctx, fnum, op, offset, length, noauth, verbose);
else
res = DesfileReadISOFileAndPrint(&dctx, fileisoidpresent, fnum, fileisoid, op, offset, length, noauth, verbose);
DropField(); DropField();
return res; return res;
} }
static int DesfileWriteISOFile(DesfireContext *dctx, bool select_current_file, uint8_t fnum, uint16_t fisoid, int filetype, uint32_t offset, uint8_t *data, uint32_t datalen, bool verbose) {
if (filetype == RFTAuto) {
PrintAndLogEx(ERR, "ISO mode needs to specify file type");
return PM3_EINVARG;
}
if (filetype == RFTValue) {
PrintAndLogEx(ERR, "ISO mode can't write Value file type");
return PM3_EINVARG;
}
if (filetype == RFTMAC) {
PrintAndLogEx(ERR, "ISO mode can't write Transaction MAC file type");
return PM3_EINVARG;
}
if (dctx->commMode != DCMPlain) {
PrintAndLogEx(ERR, "ISO mode can write only in plain mode");
return PM3_EINVARG;
}
int res = 0;
if (filetype == RFTData) {
res = DesfireISOUpdateBinary(dctx, !select_current_file, (select_current_file) ? 0x00 : fnum, offset, data, datalen);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Desfire ISOUpdateBinary command " _RED_("error") ". Result: %d", res);
return PM3_ESOFT;
}
if (select_current_file)
PrintAndLogEx(INFO, "Write data file %04x " _GREEN_("success"), fisoid);
else
PrintAndLogEx(INFO, "Write data file %02x " _GREEN_("success"), fnum);
}
if (filetype == RFTRecord) {
res = DesfireISOAppendRecord(dctx, (select_current_file) ? 0x00 : fnum, data, datalen);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Desfire WriteRecord command " _RED_("error") ". Result: %d", res);
return PM3_ESOFT;
}
if (select_current_file)
PrintAndLogEx(INFO, "Write record to file %04x " _GREEN_("success"), fisoid);
else
PrintAndLogEx(INFO, "Write record to file %02x " _GREEN_("success"), fnum);
}
return PM3_SUCCESS;
}
static int CmdHF14ADesWriteData(const char *Cmd) { static int CmdHF14ADesWriteData(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mfdes write", CLIParserInit(&ctx, "hf mfdes write",
@ -5185,7 +5369,9 @@ static int CmdHF14ADesWriteData(const char *Cmd) {
"hf mfdes write --aid 123456 --fid 01 -d 01020304 -> write data to record file with `auto` type\n" "hf mfdes write --aid 123456 --fid 01 -d 01020304 -> write data to record file with `auto` type\n"
"hf mfdes write --aid 123456 --fid 01 --type record -d 01020304 -> write data to record file\n" "hf mfdes write --aid 123456 --fid 01 --type record -d 01020304 -> write data to record file\n"
"hf mfdes write --aid 123456 --fid 01 --type record -d 01020304 --updaterec 0 -> update record in the record file. record 0 - lastest record.\n" "hf mfdes write --aid 123456 --fid 01 --type record -d 01020304 --updaterec 0 -> update record in the record file. record 0 - lastest record.\n"
"hf mfdes write --aid 123456 --fid 01 --type record --offset 000000 -d 11223344 -> write record to record file. use default channel settings from `default` command"); "hf mfdes write --aid 123456 --fid 01 --type record --offset 000000 -d 11223344 -> write record to record file. use default channel settings from `default` command\n"
"hf mfdes write --appisoid 1234 --fileisoid 1000 --type data -c iso -d 01020304 -> write data to std/backup file via iso commandset\n"
"hf mfdes write --appisoid 1234 --fileisoid 2000 --type record -c iso -d 01020304 -> aend record to record file via iso commandset");
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
@ -5208,6 +5394,8 @@ static int CmdHF14ADesWriteData(const char *Cmd) {
arg_lit0(NULL, "debit", "use for value file debit operation instead of credit"), arg_lit0(NULL, "debit", "use for value file debit operation instead of credit"),
arg_lit0(NULL, "commit", "commit needs for backup file only. For the other file types and in the `auto` mode - command set it automatically."), arg_lit0(NULL, "commit", "commit needs for backup file only. For the other file types and in the `auto` mode - command set it automatically."),
arg_int0(NULL, "updaterec", "<record number dec>", "Record number for update record command. Updates record instead of write. Lastest record - 0"), arg_int0(NULL, "updaterec", "<record number dec>", "Record number for update record command. Updates record instead of write. Lastest record - 0"),
arg_str0(NULL, "appisoid", "<isoid hex>", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian). Works only for ISO read commands."),
arg_str0(NULL, "fileisoid", "<isoid hex>", "File ISO ID (ISO DF ID) (2 hex bytes, big endian). Works only for ISO read commands."),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, false); CLIExecWithReturn(ctx, Cmd, argtable, false);
@ -5261,6 +5449,24 @@ static int CmdHF14ADesWriteData(const char *Cmd) {
int updaterecno = arg_get_int_def(ctx, 19, -1); int updaterecno = arg_get_int_def(ctx, 19, -1);
uint32_t appisoid = 0x0000;
res = arg_get_u32_hexstr_def_nlen(ctx, 20, 0x0000, &appisoid, 2, true);
bool isoidpresent = (res == 1);
if (res == 2) {
PrintAndLogEx(ERR, "Application ISO ID (for EF) must have 2 bytes length");
CLIParserFree(ctx);
return PM3_EINVARG;
}
uint32_t fileisoid = 0x0000;
res = arg_get_u32_hexstr_def_nlen(ctx, 21, 0x0000, &fileisoid, 2, true);
bool fileisoidpresent = (res == 1);
if (res == 2) {
PrintAndLogEx(ERR, "File ISO ID (for DF) must have 2 bytes length");
CLIParserFree(ctx);
return PM3_EINVARG;
}
SetAPDULogging(APDULogging); SetAPDULogging(APDULogging);
CLIParserFree(ctx); CLIParserFree(ctx);
@ -5269,8 +5475,29 @@ static int CmdHF14ADesWriteData(const char *Cmd) {
return PM3_EINVARG; return PM3_EINVARG;
} }
res = DesfireSelectAndAuthenticateEx(&dctx, securechann, appid, noauth, verbose); if (!isoidpresent) {
if (res != PM3_SUCCESS) { res = DesfireSelectAndAuthenticateEx(&dctx, securechann, appid, noauth, verbose);
if (res != PM3_SUCCESS) {
DropField();
return res;
}
} else {
res = DesfireSelectAndAuthenticateISO(&dctx, securechann, (appid != 0), appid, appisoid, fileisoid, noauth, verbose);
if (res != PM3_SUCCESS) {
DropField();
return res;
}
}
// ISO command set
if (dctx.cmdSet == DCCISO) {
if (op == RFTRecord && updaterecno >= 0) {
PrintAndLogEx(ERR, "ISO mode can't update record. Only append.");
DropField();
return PM3_EINVARG;
}
res = DesfileWriteISOFile(&dctx, fileisoidpresent, fnum, fileisoid, op, offset, data, datalen, verbose);
DropField(); DropField();
return res; return res;
} }
@ -5705,15 +5932,3 @@ int CmdHFMFDes(const char *Cmd) {
clearCommandBuffer(); clearCommandBuffer();
return CmdsParse(CommandTable, Cmd); return CmdsParse(CommandTable, Cmd);
} }
/*
ToDo:
ISO/IEC 7816 Cmds
-----------------
'B0' Read Binary
'D6' Update Binary
'B2' Read Records
'E2' Append Records
*/

View file

@ -263,6 +263,10 @@ const char *DesfireAuthErrorToStr(int error) {
return "Can't select application."; return "Can't select application.";
case 201: case 201:
return "Authentication retured no error but channel not authenticated."; return "Authentication retured no error but channel not authenticated.";
case 202:
return "Can't select application by ISO ID.";
case 203:
return "Can't select file by ISO ID.";
case 301: case 301:
return "ISO Get challenge error."; return "ISO Get challenge error.";
case 302: case 302:
@ -673,11 +677,12 @@ static int DesfireExchangeISONative(bool activate_field, DesfireContext *ctx, ui
} }
static int DesfireExchangeISO(bool activate_field, DesfireContext *ctx, sAPDU apdu, uint16_t le, uint8_t *resp, size_t *resplen, uint16_t *sw) { static int DesfireExchangeISO(bool activate_field, DesfireContext *ctx, sAPDU apdu, uint16_t le, uint8_t *resp, size_t *resplen, uint16_t *sw) {
uint32_t rlen = 0; uint8_t data[1050] = {0};
int res = DESFIRESendApduEx(activate_field, apdu, le, resp, 255, &rlen, sw); uint32_t datalen = 0;
int res = DESFIRESendApduEx(activate_field, apdu, le, data, sizeof(data), &datalen, sw);
if (res == PM3_SUCCESS) if (res == PM3_SUCCESS)
*resplen = rlen; DesfireSecureChannelDecode(ctx, data, datalen, 0, resp, resplen);
return res; return res;
} }
@ -886,6 +891,62 @@ int DesfireSelectAndAuthenticate(DesfireContext *dctx, DesfireSecureChannel secu
return DesfireSelectAndAuthenticateEx(dctx, secureChannel, aid, false, verbose); return DesfireSelectAndAuthenticateEx(dctx, secureChannel, aid, false, verbose);
} }
int DesfireSelectAndAuthenticateISO(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool useaid, uint32_t aid, uint16_t isoappid, uint16_t isofileid, bool noauth, bool verbose) {
if (verbose)
DesfirePrintContext(dctx);
int res = 0;
if (useaid) {
dctx->cmdSet = DCCNativeISO;
if (verbose)
PrintAndLogEx(INFO, "Select via " _CYAN_("native iso wrapping") " interface");
res = DesfireSelectAIDHex(dctx, aid, false, 0);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Desfire select " _RED_("error") ".");
return 200;
}
if (verbose)
PrintAndLogEx(INFO, "App %06x via native iso channel is " _GREEN_("selected"), aid);
dctx->cmdSet = DCCISO;
} else {
res = DesfireSelectEx(dctx, true, ISWIsoID, isoappid, NULL);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Desfire iso application select " _RED_("error") ".");
return 202;
}
if (verbose)
PrintAndLogEx(INFO, "Application iso id %04x is " _GREEN_("selected"), isoappid);
res = DesfireSelectEx(dctx, false, ISWIsoID, isofileid, NULL);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Desfire iso file select " _RED_("error") ".");
return 203;
}
if (verbose)
PrintAndLogEx(INFO, "Application iso id %04x file iso id %04x is " _GREEN_("selected"), isoappid, isofileid);
}
if (!noauth) {
res = DesfireAuthenticate(dctx, secureChannel, verbose);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Desfire authenticate " _RED_("error") ". Result: [%d] %s", res, DesfireAuthErrorToStr(res));
return res;
}
if (DesfireIsAuthenticated(dctx)) {
if (verbose)
PrintAndLogEx(INFO, "Desfire " _GREEN_("authenticated"));
} else {
return 201;
}
}
return PM3_SUCCESS;
}
static int DesfireAuthenticateEV1(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool verbose) { static int DesfireAuthenticateEV1(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool verbose) {
// 3 different way to authenticate AUTH (CRC16) , AUTH_ISO (CRC32) , AUTH_AES (CRC32) // 3 different way to authenticate AUTH (CRC16) , AUTH_ISO (CRC32) , AUTH_AES (CRC32)
// 4 different crypto arg1 DES, 3DES, 3K3DES, AES // 4 different crypto arg1 DES, 3DES, 3K3DES, AES
@ -2529,7 +2590,6 @@ int DesfireChangeKey(DesfireContext *dctx, bool change_master_key, uint8_t newke
// send command // send command
uint8_t resp[257] = {0}; uint8_t resp[257] = {0};
size_t resplen = 0; size_t resplen = 0;
PrintAndLogEx(SUCCESS, "Change key [%d] %s", cdatalen + 1, sprint_hex(&pckcdata[1], cdatalen + 1));
int res = DesfireChangeKeyCmd(dctx, &pckcdata[1], cdatalen + 1, resp, &resplen); int res = DesfireChangeKeyCmd(dctx, &pckcdata[1], cdatalen + 1, resp, &resplen);
// check response // check response
@ -2567,11 +2627,11 @@ int DesfireSetConfiguration(DesfireContext *dctx, uint8_t paramid, uint8_t *para
return res; return res;
} }
int DesfireISOSelect(DesfireContext *dctx, DesfireISOSelectControl cntr, uint8_t *data, uint8_t datalen, uint8_t *resp, size_t *resplen) { int DesfireISOSelectEx(DesfireContext *dctx, bool fieldon, DesfireISOSelectControl cntr, uint8_t *data, uint8_t datalen, uint8_t *resp, size_t *resplen) {
uint8_t xresp[250] = {0}; uint8_t xresp[250] = {0};
size_t xresplen = 0; size_t xresplen = 0;
uint16_t sw = 0; uint16_t sw = 0;
int res = DesfireExchangeISO(true, dctx, (sAPDU) {0x00, ISO7816_SELECT_FILE, cntr, ((resp == NULL) ? 0x0C : 0x00), datalen, data}, APDU_INCLUDE_LE_00, xresp, &xresplen, &sw); int res = DesfireExchangeISO(fieldon, dctx, (sAPDU) {0x00, ISO7816_SELECT_FILE, cntr, ((resp == NULL) ? 0x0C : 0x00), datalen, data}, APDU_INCLUDE_LE_00, xresp, &xresplen, &sw);
if (res == PM3_SUCCESS && sw != 0x9000) if (res == PM3_SUCCESS && sw != 0x9000)
return PM3_ESOFT; return PM3_ESOFT;
@ -2586,6 +2646,10 @@ int DesfireISOSelect(DesfireContext *dctx, DesfireISOSelectControl cntr, uint8_t
return res; return res;
} }
int DesfireISOSelect(DesfireContext *dctx, DesfireISOSelectControl cntr, uint8_t *data, uint8_t datalen, uint8_t *resp, size_t *resplen) {
return DesfireISOSelectEx(dctx, true, cntr, data, datalen, resp, resplen);
}
int DesfireISOSelectDF(DesfireContext *dctx, char *dfname, uint8_t *resp, size_t *resplen) { int DesfireISOSelectDF(DesfireContext *dctx, char *dfname, uint8_t *resp, size_t *resplen) {
return DesfireISOSelect(dctx, ISSDFName, (uint8_t *)dfname, strnlen(dfname, 16), resp, resplen); return DesfireISOSelect(dctx, ISSDFName, (uint8_t *)dfname, strnlen(dfname, 16), resp, resplen);
} }
@ -2627,3 +2691,87 @@ int DesfireISOInternalAuth(DesfireContext *dctx, bool app_level, uint8_t keynum,
return res; return res;
} }
int DesfireISOReadBinary(DesfireContext *dctx, bool use_file_id, uint8_t fileid, uint16_t offset, uint8_t length, uint8_t *resp, size_t *resplen) {
uint8_t p1 = 0;
if (use_file_id)
p1 = 0x80 | (fileid & 0x1f);
else
p1 = (offset >> 8) & 0x7f;
uint8_t p2 = offset & 0xff;
uint16_t sw = 0;
int res = DesfireExchangeISO(false, dctx, (sAPDU) {0x00, ISO7816_READ_BINARY, p1, p2, 0, NULL}, (length == 0) ? APDU_INCLUDE_LE_00 : length, resp, resplen, &sw);
if (res == PM3_SUCCESS && sw != 0x9000)
return PM3_ESOFT;
return res;
}
int DesfireISOUpdateBinary(DesfireContext *dctx, bool use_file_id, uint8_t fileid, uint16_t offset, uint8_t *data, size_t datalen) {
uint8_t p1 = 0;
if (use_file_id)
p1 = 0x80 | (fileid & 0x1f);
else
p1 = (offset >> 8) & 0x7f;
uint8_t p2 = offset & 0xff;
uint8_t resp[250] = {0};
size_t resplen = 0;
uint16_t sw = 0;
int res = DesfireExchangeISO(false, dctx, (sAPDU) {0x00, ISO7816_UPDATE_BINARY, p1, p2, datalen, data}, 0, resp, &resplen, &sw);
if (res == PM3_SUCCESS && sw != 0x9000)
return PM3_ESOFT;
return res;
}
int DesfireISOReadRecords(DesfireContext *dctx, uint8_t recordnum, bool read_all_records, uint8_t fileid, uint8_t length, uint8_t *resp, size_t *resplen) {
uint8_t p2 = ((fileid & 0x1f) << 3) | ((read_all_records) ? 0x05 : 0x04);
uint16_t sw = 0;
int res = DesfireExchangeISO(false, dctx, (sAPDU) {0x00, ISO7816_READ_RECORDS, recordnum, p2, 0, NULL}, (length == 0) ? APDU_INCLUDE_LE_00 : length, resp, resplen, &sw);
if (res == PM3_SUCCESS && sw != 0x9000)
return PM3_ESOFT;
return res;
}
int DesfireISOAppendRecord(DesfireContext *dctx, uint8_t fileid, uint8_t *data, size_t datalen) {
uint8_t p2 = ((fileid & 0x1f) << 3);
uint8_t resp[250] = {0};
size_t resplen = 0;
uint16_t sw = 0;
int res = DesfireExchangeISO(false, dctx, (sAPDU) {0x00, ISO7816_APPEND_RECORD, 0x00, p2, datalen, data}, 0, resp, &resplen, &sw);
if (res == PM3_SUCCESS && sw != 0x9000)
return PM3_ESOFT;
return res;
}
int DesfireSelectEx(DesfireContext *ctx, bool fieldon, DesfireISOSelectWay way, uint32_t id, char *dfname) {
uint8_t resp[250] = {0};
size_t resplen = 0;
if (way == ISWMF || (way == ISWDFName && dfname == NULL)) {
return DesfireISOSelect(ctx, ISSMFDFEF, NULL, 0, resp, &resplen);
} else if (way == ISW6bAID) {
if (fieldon)
return DesfireSelectAIDHex(ctx, id, false, 0);
else
return DesfireSelectAIDHexNoFieldOn(ctx, id);
} else if (way == ISWIsoID) {
uint8_t data[2] = {0};
Uint2byteToMemBe(data, id);
return DesfireISOSelectEx(ctx, fieldon, ISSMFDFEF, data, 2, resp, &resplen);
} else if (way == ISWDFName) {
return DesfireISOSelect(ctx, ISSMFDFEF, NULL, 0, resp, &resplen);
}
return PM3_ESOFT;
}
int DesfireSelect(DesfireContext *ctx, DesfireISOSelectWay way, uint32_t id, char *dfname) {
return DesfireSelectEx(ctx, true, way, id, dfname);
}

View file

@ -30,6 +30,14 @@ enum DesfireISOSelectControlEnum {
}; };
typedef enum DesfireISOSelectControlEnum DesfireISOSelectControl; typedef enum DesfireISOSelectControlEnum DesfireISOSelectControl;
enum DesfireISOSelectWayEnum {
ISW6bAID,
ISWMF,
ISWIsoID,
ISWDFName
};
typedef enum DesfireISOSelectWayEnum DesfireISOSelectWay;
typedef struct { typedef struct {
const uint8_t id; const uint8_t id;
const char *text; const char *text;
@ -160,9 +168,13 @@ int DesfireSelectAIDHex(DesfireContext *ctx, uint32_t aid1, bool select_two, uin
int DesfireSelectAIDHexNoFieldOn(DesfireContext *ctx, uint32_t aid); int DesfireSelectAIDHexNoFieldOn(DesfireContext *ctx, uint32_t aid);
void DesfirePrintAIDFunctions(uint32_t appid); void DesfirePrintAIDFunctions(uint32_t appid);
int DesfireSelectEx(DesfireContext *ctx, bool fieldon, DesfireISOSelectWay way, uint32_t id, char *dfname);
int DesfireSelect(DesfireContext *ctx, DesfireISOSelectWay way, uint32_t id, char *dfname);
const char *DesfireAuthErrorToStr(int error); const char *DesfireAuthErrorToStr(int error);
int DesfireSelectAndAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel, uint32_t aid, bool verbose); int DesfireSelectAndAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel, uint32_t aid, bool verbose);
int DesfireSelectAndAuthenticateEx(DesfireContext *dctx, DesfireSecureChannel secureChannel, uint32_t aid, bool noauth, bool verbose); int DesfireSelectAndAuthenticateEx(DesfireContext *dctx, DesfireSecureChannel secureChannel, uint32_t aid, bool noauth, bool verbose);
int DesfireSelectAndAuthenticateISO(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool useaid, uint32_t aid, uint16_t isoappid, uint16_t isofileid, bool noauth, bool verbose);
int DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool verbose); int DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool verbose);
void DesfireCheckAuthCommands(uint32_t appAID, char *dfname, uint8_t keyNum, AuthCommandsChk *authCmdCheck); void DesfireCheckAuthCommands(uint32_t appAID, char *dfname, uint8_t keyNum, AuthCommandsChk *authCmdCheck);
void DesfireCheckAuthCommandsPrint(AuthCommandsChk *authCmdCheck); void DesfireCheckAuthCommandsPrint(AuthCommandsChk *authCmdCheck);
@ -230,8 +242,15 @@ int DesfireUpdateRecord(DesfireContext *dctx, uint8_t fnum, uint32_t recnum, uin
int DesfireISOSelectDF(DesfireContext *dctx, char *dfname, uint8_t *resp, size_t *resplen); int DesfireISOSelectDF(DesfireContext *dctx, char *dfname, uint8_t *resp, size_t *resplen);
int DesfireISOSelect(DesfireContext *dctx, DesfireISOSelectControl cntr, uint8_t *data, uint8_t datalen, uint8_t *resp, size_t *resplen); int DesfireISOSelect(DesfireContext *dctx, DesfireISOSelectControl cntr, uint8_t *data, uint8_t datalen, uint8_t *resp, size_t *resplen);
int DesfireISOSelectFile(DesfireContext *dctx, char *appdfname, uint16_t appid, uint16_t fileid);
int DesfireISOSelectEx(DesfireContext *dctx, bool fieldon, DesfireISOSelectControl cntr, uint8_t *data, uint8_t datalen, uint8_t *resp, size_t *resplen);
int DesfireISOGetChallenge(DesfireContext *dctx, DesfireCryptoAlgorythm keytype, uint8_t *resp, size_t *resplen); int DesfireISOGetChallenge(DesfireContext *dctx, DesfireCryptoAlgorythm keytype, uint8_t *resp, size_t *resplen);
int DesfireISOExternalAuth(DesfireContext *dctx, bool app_level, uint8_t keynum, DesfireCryptoAlgorythm keytype, uint8_t *data); int DesfireISOExternalAuth(DesfireContext *dctx, bool app_level, uint8_t keynum, DesfireCryptoAlgorythm keytype, uint8_t *data);
int DesfireISOInternalAuth(DesfireContext *dctx, bool app_level, uint8_t keynum, DesfireCryptoAlgorythm keytype, uint8_t *data, uint8_t *resp, size_t *resplen); int DesfireISOInternalAuth(DesfireContext *dctx, bool app_level, uint8_t keynum, DesfireCryptoAlgorythm keytype, uint8_t *data, uint8_t *resp, size_t *resplen);
int DesfireISOReadBinary(DesfireContext *dctx, bool use_file_id, uint8_t fileid, uint16_t offset, uint8_t length, uint8_t *resp, size_t *resplen);
int DesfireISOUpdateBinary(DesfireContext *dctx, bool use_file_id, uint8_t fileid, uint16_t offset, uint8_t *data, size_t datalen);
int DesfireISOReadRecords(DesfireContext *dctx, uint8_t recordnum, bool read_all_records, uint8_t fileid, uint8_t length, uint8_t *resp, size_t *resplen);
int DesfireISOAppendRecord(DesfireContext *dctx, uint8_t fileid, uint8_t *data, size_t datalen);
#endif // __DESFIRECORE_H #endif // __DESFIRECORE_H

View file

@ -41,6 +41,7 @@ static bool CommandCanUseAnyChannel(uint8_t cmd) {
} }
static const AllowedChannelModesS AllowedChannelModes[] = { static const AllowedChannelModesS AllowedChannelModes[] = {
// D40 channel
{MFDES_SELECT_APPLICATION, DACd40, DCCNative, DCMPlain}, {MFDES_SELECT_APPLICATION, DACd40, DCCNative, DCMPlain},
{MFDES_CREATE_APPLICATION, DACd40, DCCNative, DCMMACed}, {MFDES_CREATE_APPLICATION, DACd40, DCCNative, DCMMACed},
@ -81,6 +82,7 @@ static const AllowedChannelModesS AllowedChannelModes[] = {
{MFDES_CHANGE_KEY, DACd40, DCCNative, DCMEncryptedPlain}, {MFDES_CHANGE_KEY, DACd40, DCCNative, DCMEncryptedPlain},
{MFDES_CHANGE_KEY_EV2, DACd40, DCCNative, DCMEncryptedPlain}, {MFDES_CHANGE_KEY_EV2, DACd40, DCCNative, DCMEncryptedPlain},
// EV1 and EV2 channel
{MFDES_SELECT_APPLICATION, DACEV1, DCCNative, DCMPlain}, {MFDES_SELECT_APPLICATION, DACEV1, DCCNative, DCMPlain},
{MFDES_GET_KEY_VERSION, DACEV1, DCCNative, DCMMACed}, {MFDES_GET_KEY_VERSION, DACEV1, DCCNative, DCMMACed},
@ -114,8 +116,26 @@ static const AllowedChannelModesS AllowedChannelModes[] = {
{MFDES_CHANGE_KEY, DACEV1, DCCNative, DCMEncryptedPlain}, {MFDES_CHANGE_KEY, DACEV1, DCCNative, DCMEncryptedPlain},
{MFDES_CHANGE_KEY_EV2, DACEV1, DCCNative, DCMEncryptedPlain}, {MFDES_CHANGE_KEY_EV2, DACEV1, DCCNative, DCMEncryptedPlain},
// EV2 channel separately
{MFDES_AUTHENTICATE_EV2F, DACEV2, DCCNative, DCMPlain}, {MFDES_AUTHENTICATE_EV2F, DACEV2, DCCNative, DCMPlain},
{MFDES_AUTHENTICATE_EV2NF, DACEV2, DCCNative, DCMPlain}, {MFDES_AUTHENTICATE_EV2NF, DACEV2, DCCNative, DCMPlain},
// ISO channel
{ISO7816_READ_BINARY, DACd40, DCCISO, DCMPlain},
{ISO7816_UPDATE_BINARY, DACd40, DCCISO, DCMPlain},
{ISO7816_READ_RECORDS, DACd40, DCCISO, DCMPlain},
{ISO7816_APPEND_RECORD, DACd40, DCCISO, DCMPlain},
{ISO7816_READ_BINARY, DACd40, DCCISO, DCMMACed},
{ISO7816_READ_RECORDS, DACd40, DCCISO, DCMMACed},
{ISO7816_READ_BINARY, DACEV1, DCCISO, DCMPlain},
{ISO7816_UPDATE_BINARY, DACEV1, DCCISO, DCMPlain},
{ISO7816_READ_RECORDS, DACEV1, DCCISO, DCMPlain},
{ISO7816_APPEND_RECORD, DACEV1, DCCISO, DCMPlain},
{ISO7816_READ_BINARY, DACEV1, DCCISO, DCMMACed},
{ISO7816_READ_RECORDS, DACEV1, DCCISO, DCMMACed},
}; };
#define CMD_HEADER_LEN_ALL 0xffff #define CMD_HEADER_LEN_ALL 0xffff
@ -186,6 +206,25 @@ static bool DesfireEV1D40ReceiveMAC(DesfireContext *ctx, uint8_t cmd) {
return false; return false;
} }
static const uint8_t ISOChannelValidCmd[] = {
ISO7816_SELECT_FILE,
ISO7816_READ_BINARY,
ISO7816_UPDATE_BINARY,
ISO7816_READ_RECORDS,
ISO7816_APPEND_RECORD,
ISO7816_GET_CHALLENGE,
ISO7816_EXTERNAL_AUTHENTICATION,
ISO7816_INTERNAL_AUTHENTICATION
};
static bool DesfireISOChannelValidCmd(uint8_t cmd) {
for (int i = 0; i < ARRAY_LENGTH(ISOChannelValidCmd); i++)
if (ISOChannelValidCmd[i] == cmd)
return true;
return false;
}
static void DesfireSecureChannelEncodeD40(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) { static void DesfireSecureChannelEncodeD40(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) {
uint8_t data[1024] = {0}; uint8_t data[1024] = {0};
size_t rlen = 0; size_t rlen = 0;
@ -432,8 +471,8 @@ static void DesfireSecureChannelDecodeEV1(DesfireContext *ctx, uint8_t *srcdata,
DesfireCryptoCMAC(ctx, data, *dstdatalen + 1, cmac); DesfireCryptoCMAC(ctx, data, *dstdatalen + 1, cmac);
if (memcmp(&srcdata[*dstdatalen], cmac, DesfireGetMACLength(ctx)) != 0) { if (memcmp(&srcdata[*dstdatalen], cmac, DesfireGetMACLength(ctx)) != 0) {
PrintAndLogEx(WARNING, "Received MAC is not match with calculated"); PrintAndLogEx(WARNING, "Received MAC is not match with calculated");
PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[*dstdatalen], desfire_get_key_block_length(ctx->keyType))); PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[*dstdatalen], DesfireGetMACLength(ctx)));
PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, desfire_get_key_block_length(ctx->keyType))); PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, DesfireGetMACLength(ctx)));
} else { } else {
if (GetAPDULogging()) if (GetAPDULogging())
PrintAndLogEx(INFO, "Received MAC OK"); PrintAndLogEx(INFO, "Received MAC OK");
@ -482,8 +521,8 @@ static void DesfireSecureChannelDecodeEV2(DesfireContext *ctx, uint8_t *srcdata,
DesfireEV2CalcCMAC(ctx, 0x00, srcdata, *dstdatalen, cmac); DesfireEV2CalcCMAC(ctx, 0x00, srcdata, *dstdatalen, cmac);
if (memcmp(&srcdata[*dstdatalen], cmac, DesfireGetMACLength(ctx)) != 0) { if (memcmp(&srcdata[*dstdatalen], cmac, DesfireGetMACLength(ctx)) != 0) {
PrintAndLogEx(WARNING, "Received MAC is not match with calculated"); PrintAndLogEx(WARNING, "Received MAC is not match with calculated");
PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[*dstdatalen], desfire_get_key_block_length(ctx->keyType))); PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[*dstdatalen], DesfireGetMACLength(ctx)));
PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, desfire_get_key_block_length(ctx->keyType))); PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, DesfireGetMACLength(ctx)));
} else { } else {
if (GetAPDULogging()) if (GetAPDULogging())
PrintAndLogEx(INFO, "Received MAC OK"); PrintAndLogEx(INFO, "Received MAC OK");
@ -499,8 +538,8 @@ static void DesfireSecureChannelDecodeEV2(DesfireContext *ctx, uint8_t *srcdata,
DesfireEV2CalcCMAC(ctx, 0x00, srcdata, *dstdatalen, cmac); DesfireEV2CalcCMAC(ctx, 0x00, srcdata, *dstdatalen, cmac);
if (memcmp(&srcdata[*dstdatalen], cmac, DesfireGetMACLength(ctx)) != 0) { if (memcmp(&srcdata[*dstdatalen], cmac, DesfireGetMACLength(ctx)) != 0) {
PrintAndLogEx(WARNING, "Received MAC is not match with calculated"); PrintAndLogEx(WARNING, "Received MAC is not match with calculated");
PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[*dstdatalen], desfire_get_key_block_length(ctx->keyType))); PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[*dstdatalen], DesfireGetMACLength(ctx)));
PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, desfire_get_key_block_length(ctx->keyType))); PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, DesfireGetMACLength(ctx)));
} else { } else {
if (GetAPDULogging()) if (GetAPDULogging())
PrintAndLogEx(INFO, "Received MAC OK"); PrintAndLogEx(INFO, "Received MAC OK");
@ -520,7 +559,39 @@ static void DesfireSecureChannelDecodeEV2(DesfireContext *ctx, uint8_t *srcdata,
} }
} }
static void DesfireISODecode(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) {
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
uint8_t data[1050] = {0};
if (srcdatalen < DesfireGetMACLength(ctx))
return;
uint8_t maclen = DesfireGetMACLength(ctx);
if (DesfireIsAuthenticated(ctx)) {
memcpy(data, srcdata, srcdatalen - maclen);
data[*dstdatalen] = 0x00; // respcode
uint8_t cmac[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0};
DesfireCryptoCMAC(ctx, data, srcdatalen - maclen + 1, cmac);
if (memcmp(&srcdata[srcdatalen - maclen], cmac, maclen) != 0) {
PrintAndLogEx(WARNING, "Received MAC is not match with calculated");
PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[srcdatalen - maclen], maclen));
PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, maclen));
} else {
*dstdatalen = srcdatalen - maclen;
if (GetAPDULogging())
PrintAndLogEx(INFO, "Received MAC OK");
}
}
}
void DesfireSecureChannelDecode(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t respcode, uint8_t *dstdata, size_t *dstdatalen) { void DesfireSecureChannelDecode(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t respcode, uint8_t *dstdata, size_t *dstdatalen) {
if (ctx->cmdSet == DCCISO) {
DesfireISODecode(ctx, srcdata, srcdatalen, dstdata, dstdatalen);
return;
}
switch (ctx->secureChannel) { switch (ctx->secureChannel) {
case DACd40: case DACd40:
DesfireSecureChannelDecodeD40(ctx, srcdata, srcdatalen, respcode, dstdata, dstdatalen); DesfireSecureChannelDecodeD40(ctx, srcdata, srcdatalen, respcode, dstdata, dstdatalen);
@ -543,13 +614,21 @@ bool PrintChannelModeWarning(uint8_t cmd, DesfireSecureChannel secureChannel, De
PrintAndLogEx(WARNING, "Communication mode can't be NONE. command: %02x", cmd); PrintAndLogEx(WARNING, "Communication mode can't be NONE. command: %02x", cmd);
return false; return false;
} }
// no security set // no security set
if (secureChannel == DACNone) if (secureChannel == DACNone)
return true; return true;
if (CommandCanUseAnyChannel(cmd)) if (CommandCanUseAnyChannel(cmd))
return true; return true;
// ISO commands
if (cmdSet == DCCISO) {
bool res = DesfireISOChannelValidCmd(cmd);
if (!res)
return false;
}
bool found = false; bool found = false;
for (int i = 0; i < ARRAY_LENGTH(AllowedChannelModes); i++) for (int i = 0; i < ARRAY_LENGTH(AllowedChannelModes); i++)
if (AllowedChannelModes[i].cmd == cmd) { if (AllowedChannelModes[i].cmd == cmd) {