Merge pull request #1150 from aveao/emrtd-dumpdir

eMRTD dump: Allow specifying a custom dump directory
This commit is contained in:
Philippe Teuwen 2020-12-30 09:54:31 +01:00 committed by GitHub
commit 481f8a64a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 83 additions and 22 deletions

View file

@ -50,10 +50,10 @@
const uint8_t KENC_type[4] = {0x00, 0x00, 0x00, 0x01}; const uint8_t KENC_type[4] = {0x00, 0x00, 0x00, 0x01};
const uint8_t KMAC_type[4] = {0x00, 0x00, 0x00, 0x02}; const uint8_t KMAC_type[4] = {0x00, 0x00, 0x00, 0x02};
static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length); static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length, const char *path);
static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length); static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length, const char *path);
static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length); static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length, const char *path);
static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length); static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length, const char *path);
static int emrtd_print_ef_com_info(uint8_t *data, size_t datalen); static int emrtd_print_ef_com_info(uint8_t *data, size_t datalen);
static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen); static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen);
static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen); static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen);
@ -710,7 +710,7 @@ static bool emrtd_select_and_read(uint8_t *dataout, int *dataoutlen, const char
const uint8_t jpeg_header[4] = { 0xFF, 0xD8, 0xFF, 0xE0 }; const uint8_t jpeg_header[4] = { 0xFF, 0xD8, 0xFF, 0xE0 };
const uint8_t jpeg2k_header[6] = { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50 }; const uint8_t jpeg2k_header[6] = { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50 };
static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length) { static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length, const char *path) {
int offset, datalen = 0; int offset, datalen = 0;
// This is a hacky impl that just looks for the image header. I'll improve it eventually. // This is a hacky impl that just looks for the image header. I'll improve it eventually.
@ -730,11 +730,20 @@ static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length) {
return PM3_ESOFT; return PM3_ESOFT;
} }
saveFile(dg_table[EF_DG2].filename, file_contents[offset] == 0xFF ? ".jpg" : ".jp2", file_contents + offset, datalen); char *filepath = calloc(strlen(path) + 100, sizeof(char));
if (filepath == NULL)
return PM3_EMALLOC;
strcpy(filepath, path);
strncat(filepath, PATHSEP, 2);
strcat(filepath, dg_table[EF_DG2].filename);
saveFile(filepath, file_contents[offset] == 0xFF ? ".jpg" : ".jp2", file_contents + offset, datalen);
free(filepath);
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length) { static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length, const char *path) {
uint8_t data[EMRTD_MAX_FILE_SIZE]; uint8_t data[EMRTD_MAX_FILE_SIZE];
size_t datalen = 0; size_t datalen = 0;
@ -744,7 +753,16 @@ static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length) {
} }
if (datalen < EMRTD_MAX_FILE_SIZE) { if (datalen < EMRTD_MAX_FILE_SIZE) {
saveFile(dg_table[EF_DG5].filename, data[0] == 0xFF ? ".jpg" : ".jp2", data, datalen); char *filepath = calloc(strlen(path) + 100, sizeof(char));
if (filepath == NULL)
return PM3_EMALLOC;
strcpy(filepath, path);
strncat(filepath, PATHSEP, 2);
strcat(filepath, dg_table[EF_DG5].filename);
saveFile(filepath, data[0] == 0xFF ? ".jpg" : ".jp2", data, datalen);
free(filepath);
} else { } else {
PrintAndLogEx(ERR, "error (emrtd_dump_ef_dg5) datalen out-of-bounds"); PrintAndLogEx(ERR, "error (emrtd_dump_ef_dg5) datalen out-of-bounds");
return PM3_ESOFT; return PM3_ESOFT;
@ -752,7 +770,7 @@ static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length) {
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length) { static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length, const char *path) {
uint8_t data[EMRTD_MAX_FILE_SIZE]; uint8_t data[EMRTD_MAX_FILE_SIZE];
size_t datalen = 0; size_t datalen = 0;
@ -762,7 +780,16 @@ static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length) {
} }
if (datalen < EMRTD_MAX_FILE_SIZE) { if (datalen < EMRTD_MAX_FILE_SIZE) {
saveFile(dg_table[EF_DG7].filename, data[0] == 0xFF ? ".jpg" : ".jp2", data, datalen); char *filepath = calloc(strlen(path) + 100, sizeof(char));
if (filepath == NULL)
return PM3_EMALLOC;
strcpy(filepath, path);
strncat(filepath, PATHSEP, 2);
strcat(filepath, dg_table[EF_DG7].filename);
saveFile(filepath, data[0] == 0xFF ? ".jpg" : ".jp2", data, datalen);
free(filepath);
} else { } else {
PrintAndLogEx(ERR, "error (emrtd_dump_ef_dg7) datalen out-of-bounds"); PrintAndLogEx(ERR, "error (emrtd_dump_ef_dg7) datalen out-of-bounds");
return PM3_ESOFT; return PM3_ESOFT;
@ -770,7 +797,7 @@ static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length) {
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length) { static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length, const char *path) {
int fieldlen = emrtd_get_asn1_field_length(file_contents, file_length, 1); int fieldlen = emrtd_get_asn1_field_length(file_contents, file_length, 1);
int datalen = emrtd_get_asn1_data_length(file_contents, file_length, 1); int datalen = emrtd_get_asn1_data_length(file_contents, file_length, 1);
@ -779,11 +806,19 @@ static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length) {
return PM3_SUCCESS; return PM3_SUCCESS;
} }
saveFile(dg_table[EF_SOD].filename, ".p7b", file_contents + fieldlen + 1, datalen); char *filepath = calloc(strlen(path) + 100, sizeof(char));
if (filepath == NULL)
return PM3_EMALLOC;
strcpy(filepath, path);
strncat(filepath, PATHSEP, 2);
strcat(filepath, dg_table[EF_SOD].filename);
saveFile(filepath, ".p7b", file_contents + fieldlen + 1, datalen);
free(filepath);
return PM3_ESOFT; return PM3_ESOFT;
} }
static bool emrtd_dump_file(uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, const char *file, const char *name, bool use_secure, bool use_14b) { static bool emrtd_dump_file(uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, const char *file, const char *name, bool use_secure, bool use_14b, const char *path) {
uint8_t response[EMRTD_MAX_FILE_SIZE]; uint8_t response[EMRTD_MAX_FILE_SIZE];
int resplen = 0; int resplen = 0;
@ -791,13 +826,22 @@ static bool emrtd_dump_file(uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, cons
return false; return false;
} }
char *filepath = calloc(strlen(path) + 100, sizeof(char));
if (filepath == NULL)
return PM3_EMALLOC;
strcpy(filepath, path);
strncat(filepath, PATHSEP, 2);
strcat(filepath, name);
PrintAndLogEx(INFO, "Read %s, len: %i.", name, resplen); PrintAndLogEx(INFO, "Read %s, len: %i.", name, resplen);
PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen));
saveFile(name, ".BIN", response, resplen); saveFile(filepath, ".BIN", response, resplen);
emrtd_dg_t *dg = emrtd_fileid_to_dg(file); emrtd_dg_t *dg = emrtd_fileid_to_dg(file);
if ((dg != NULL) && (dg->dumper != NULL)) { if ((dg != NULL) && (dg->dumper != NULL)) {
dg->dumper(response, resplen); dg->dumper(response, resplen, path);
} }
free(filepath);
return true; return true;
} }
@ -983,7 +1027,7 @@ static bool emrtd_do_auth(char *documentnumber, char *dob, char *expiry, bool BA
return true; return true;
} }
int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available) { int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available, const char *path) {
uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 }; uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 };
int resplen = 0; int resplen = 0;
uint8_t ssc[8] = { 0x00 }; uint8_t ssc[8] = { 0x00 };
@ -999,7 +1043,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab
} }
// Dump EF_CardAccess (if available) // Dump EF_CardAccess (if available)
if (!emrtd_dump_file(ks_enc, ks_mac, ssc, dg_table[EF_CardAccess].fileid, dg_table[EF_CardAccess].filename, BAC, use_14b)) { if (!emrtd_dump_file(ks_enc, ks_mac, ssc, dg_table[EF_CardAccess].fileid, dg_table[EF_CardAccess].filename, BAC, use_14b, path)) {
PrintAndLogEx(INFO, "Couldn't dump EF_CardAccess, card does not support PACE."); PrintAndLogEx(INFO, "Couldn't dump EF_CardAccess, card does not support PACE.");
PrintAndLogEx(HINT, "This is expected behavior for cards without PACE, and isn't something to be worried about."); PrintAndLogEx(HINT, "This is expected behavior for cards without PACE, and isn't something to be worried about.");
} }
@ -1016,9 +1060,20 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab
DropField(); DropField();
return PM3_ESOFT; return PM3_ESOFT;
} }
char *filepath = calloc(strlen(path) + 100, sizeof(char));
if (filepath == NULL)
return PM3_EMALLOC;
strcpy(filepath, path);
strncat(filepath, PATHSEP, 2);
strcat(filepath, dg_table[EF_COM].filename);
PrintAndLogEx(INFO, "Read EF_COM, len: %i.", resplen); PrintAndLogEx(INFO, "Read EF_COM, len: %i.", resplen);
PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen));
saveFile(dg_table[EF_COM].filename, ".BIN", response, resplen); saveFile(filepath, ".BIN", response, resplen);
free(filepath);
uint8_t filelist[50]; uint8_t filelist[50];
size_t filelistlen = 0; size_t filelistlen = 0;
@ -1041,7 +1096,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab
} }
PrintAndLogEx(DEBUG, "Current file: %s", dg->filename); PrintAndLogEx(DEBUG, "Current file: %s", dg->filename);
if (!dg->pace && !dg->eac) { if (!dg->pace && !dg->eac) {
emrtd_dump_file(ks_enc, ks_mac, ssc, dg->fileid, dg->filename, BAC, use_14b); emrtd_dump_file(ks_enc, ks_mac, ssc, dg->fileid, dg->filename, BAC, use_14b, path);
} }
} }
DropField(); DropField();
@ -1891,6 +1946,7 @@ static int cmd_hf_emrtd_dump(const char *Cmd) {
arg_str0("d", "dateofbirth", "<YYMMDD>", "date of birth in YYMMDD format"), arg_str0("d", "dateofbirth", "<YYMMDD>", "date of birth in YYMMDD format"),
arg_str0("e", "expiry", "<YYMMDD>", "expiry in YYMMDD format"), arg_str0("e", "expiry", "<YYMMDD>", "expiry in YYMMDD format"),
arg_str0("m", "mrz", "<[0-9A-Z<]>", "2nd line of MRZ, 44 chars"), arg_str0("m", "mrz", "<[0-9A-Z<]>", "2nd line of MRZ, 44 chars"),
arg_str0(NULL, "path", "<dirpath>", "save dump to the given dirpath"),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, true); CLIExecWithReturn(ctx, Cmd, argtable, true);
@ -1957,11 +2013,16 @@ static int cmd_hf_emrtd_dump(const char *Cmd) {
} }
} }
uint8_t path[FILENAME_MAX] = { 0x00 };
if (CLIParamStrToBuf(arg_get_str(ctx, 5), path, sizeof(path), &slen) != 0 || slen == 0) {
path[0] = '.';
}
CLIParserFree(ctx); CLIParserFree(ctx);
if (error) { if (error) {
return PM3_ESOFT; return PM3_ESOFT;
} }
return dumpHF_EMRTD((char *)docnum, (char *)dob, (char *)expiry, BAC); return dumpHF_EMRTD((char *)docnum, (char *)dob, (char *)expiry, BAC, (const char *)path);
} }
static int cmd_hf_emrtd_info(const char *Cmd) { static int cmd_hf_emrtd_info(const char *Cmd) {

View file

@ -24,7 +24,7 @@ typedef struct emrtd_dg_s {
bool required; // some are required only if PACE bool required; // some are required only if PACE
bool fastdump; // fast to dump bool fastdump; // fast to dump
int (*parser)(uint8_t *data, size_t datalen); int (*parser)(uint8_t *data, size_t datalen);
int (*dumper)(uint8_t *data, size_t datalen); int (*dumper)(uint8_t *data, size_t datalen, const char *path);
} emrtd_dg_t; } emrtd_dg_t;
typedef struct emrtd_hashalg_s { typedef struct emrtd_hashalg_s {
@ -37,7 +37,7 @@ typedef struct emrtd_hashalg_s {
int CmdHFeMRTD(const char *Cmd); int CmdHFeMRTD(const char *Cmd);
int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available); int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available, const char *path);
int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available); int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available);
int infoHF_EMRTD_offline(const char *path); int infoHF_EMRTD_offline(const char *path);
#endif #endif