Merge branch 'master' into id48

Signed-off-by: Iceman <iceman@iuse.se>
This commit is contained in:
Iceman 2024-03-03 22:06:54 +01:00 committed by GitHub
commit a0b26257db
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 379 additions and 178 deletions

View file

@ -4,6 +4,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
## [unreleased][unreleased]
- Added `lf em 4x70 recover` - recovery the second half of an id48 key (@henrygab)
- Changed `hf emrtd info` - Added EG_DG7 viewing (@iceman1001)
- Changed `hf mf dump` - it now also prints the dumped memory (@franscesco-scar)
- Changed NDEF parsing to show mime images (@iceman1001)
- Fixed `hf mf ndefformat` - now correctly handles MADv2 when formatting (@iceman1001)

View file

@ -92,12 +92,11 @@ static int reply_ng_internal(uint16_t cmd, int16_t status, const uint8_t *data,
// Add the (optional) content to the frame, with a maximum size of PM3_CMD_DATA_SIZE
if (data && len) {
for (size_t i = 0; i < len; i++) {
txBufferNG.data[i] = data[i];
}
memcpy(txBufferNG.data, data, len);
}
PacketResponseNGPostamble *tx_post = (PacketResponseNGPostamble *)((uint8_t *)&txBufferNG + sizeof(PacketResponseNGPreamble) + len);
// Note: if we send to both FPC & USB, we'll set CRC for both if any of them require CRC
if ((g_reply_via_fpc && g_reply_with_crc_on_fpc) || ((g_reply_via_usb) && g_reply_with_crc_on_usb)) {
uint8_t first, second;
@ -125,9 +124,14 @@ static int reply_ng_internal(uint16_t cmd, int16_t status, const uint8_t *data,
#endif
}
// we got two results, let's prioritize the faulty one and USB over FPC.
if (g_reply_via_usb && (resultusb != PM3_SUCCESS)) return resultusb;
if (g_reply_via_usb && (resultusb != PM3_SUCCESS)) {
return resultusb;
}
#ifdef WITH_FPC_USART_HOST
if (g_reply_via_fpc && (resultfpc != PM3_SUCCESS)) return resultfpc;
if (g_reply_via_fpc && (resultfpc != PM3_SUCCESS)) {
return resultfpc;
}
#endif
return PM3_SUCCESS;
}
@ -145,36 +149,42 @@ int reply_mix(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, const v
}
uint8_t cmddata[PM3_CMD_DATA_SIZE];
memcpy(cmddata, arg, sizeof(arg));
if (len && data)
if (len && data) {
memcpy(cmddata + sizeof(arg), data, (int)len);
}
int res = reply_ng_internal((cmd & 0xFFFF), status, cmddata, len + sizeof(arg), false);
return res;
return reply_ng_internal((cmd & 0xFFFF), status, cmddata, len + sizeof(arg), false);
}
static int receive_ng_internal(PacketCommandNG *rx, uint32_t read_ng(uint8_t *data, size_t len), bool usb, bool fpc) {
PacketCommandNGRaw rx_raw;
size_t bytes = read_ng((uint8_t *)&rx_raw.pre, sizeof(PacketCommandNGPreamble));
if (bytes == 0)
if (bytes == 0) {
return PM3_ENODATA;
}
if (bytes != sizeof(PacketCommandNGPreamble))
if (bytes != sizeof(PacketCommandNGPreamble)) {
return PM3_EIO;
}
rx->magic = rx_raw.pre.magic;
rx->ng = rx_raw.pre.ng;
uint16_t length = rx_raw.pre.length;
rx->cmd = rx_raw.pre.cmd;
uint16_t length = rx_raw.pre.length;
if (rx->magic == COMMANDNG_PREAMBLE_MAGIC) { // New style NG command
if (length > PM3_CMD_DATA_SIZE)
if (length > PM3_CMD_DATA_SIZE) {
return PM3_EOVFLOW;
}
// Get the core and variable length payload
bytes = read_ng((uint8_t *)&rx_raw.data, length);
if (bytes != length)
if (bytes != length) {
return PM3_EIO;
}
if (rx->ng) {
memcpy(rx->data.asBytes, rx_raw.data, length);
@ -192,27 +202,33 @@ static int receive_ng_internal(PacketCommandNG *rx, uint32_t read_ng(uint8_t *da
memcpy(rx->data.asBytes, rx_raw.data + sizeof(arg), length - sizeof(arg));
rx->length = length - sizeof(arg);
}
// Get the postamble
bytes = read_ng((uint8_t *)&rx_raw.foopost, sizeof(PacketCommandNGPostamble));
if (bytes != sizeof(PacketCommandNGPostamble))
if (bytes != sizeof(PacketCommandNGPostamble)) {
return PM3_EIO;
}
// Check CRC, accept MAGIC as placeholder
rx->crc = rx_raw.foopost.crc;
if (rx->crc != COMMANDNG_POSTAMBLE_MAGIC) {
uint8_t first, second;
compute_crc(CRC_14443_A, (uint8_t *)&rx_raw, sizeof(PacketCommandNGPreamble) + length, &first, &second);
if ((first << 8) + second != rx->crc)
if ((first << 8) + second != rx->crc) {
return PM3_EIO;
}
}
g_reply_via_usb = usb;
g_reply_via_fpc = fpc;
} else { // Old style command
PacketCommandOLD rx_old;
memcpy(&rx_old, &rx_raw.pre, sizeof(PacketCommandNGPreamble));
bytes = read_ng(((uint8_t *)&rx_old) + sizeof(PacketCommandNGPreamble), sizeof(PacketCommandOLD) - sizeof(PacketCommandNGPreamble));
if (bytes != sizeof(PacketCommandOLD) - sizeof(PacketCommandNGPreamble))
if (bytes != sizeof(PacketCommandOLD) - sizeof(PacketCommandNGPreamble)) {
return PM3_EIO;
}
g_reply_via_usb = usb;
g_reply_via_fpc = fpc;
@ -232,8 +248,9 @@ static int receive_ng_internal(PacketCommandNG *rx, uint32_t read_ng(uint8_t *da
int receive_ng(PacketCommandNG *rx) {
// Check if there is a packet available
if (usb_poll_validate_length())
if (usb_poll_validate_length()) {
return receive_ng_internal(rx, usb_read_ng, true, false);
}
#ifdef WITH_FPC_USART_HOST
// Check if there is a FPC packet available

View file

@ -89,10 +89,11 @@ static void usart_fill_rxfifo(void) {
if (pUS1->US_RNCR == 0) { // One buffer got filled, backup buffer being used
if (us_rxfifo_low > us_rxfifo_high)
if (us_rxfifo_low > us_rxfifo_high) {
rxfifo_free = us_rxfifo_low - us_rxfifo_high;
else
} else {
rxfifo_free = sizeof(us_rxfifo) - us_rxfifo_high + us_rxfifo_low;
}
uint16_t available = USART_BUFFLEN - usart_cur_inbuf_off;
@ -100,19 +101,21 @@ static void usart_fill_rxfifo(void) {
for (uint16_t i = 0; i < available; i++) {
us_rxfifo[us_rxfifo_high++] = usart_cur_inbuf[usart_cur_inbuf_off + i];
if (us_rxfifo_high == sizeof(us_rxfifo))
if (us_rxfifo_high == sizeof(us_rxfifo)) {
us_rxfifo_high = 0;
}
}
// Give next buffer
pUS1->US_RNPR = (uint32_t)usart_cur_inbuf;
pUS1->US_RNCR = USART_BUFFLEN;
// Swap current buff
if (usart_cur_inbuf == us_in_a)
if (usart_cur_inbuf == us_in_a) {
usart_cur_inbuf = us_in_b;
else
} else {
usart_cur_inbuf = us_in_a;
}
usart_cur_inbuf_off = 0;
} else {
@ -133,15 +136,17 @@ static void usart_fill_rxfifo(void) {
if (pUS1->US_RCR < USART_BUFFLEN - usart_cur_inbuf_off) { // Current buffer partially filled
if (us_rxfifo_low > us_rxfifo_high)
if (us_rxfifo_low > us_rxfifo_high) {
rxfifo_free = (us_rxfifo_low - us_rxfifo_high);
else
} else {
rxfifo_free = (sizeof(us_rxfifo) - us_rxfifo_high + us_rxfifo_low);
}
uint16_t available = (USART_BUFFLEN - pUS1->US_RCR - usart_cur_inbuf_off);
if (available > rxfifo_free)
if (available > rxfifo_free) {
available = rxfifo_free;
}
for (uint16_t i = 0; i < available; i++) {
us_rxfifo[us_rxfifo_high++] = usart_cur_inbuf[usart_cur_inbuf_off + i];
@ -155,14 +160,19 @@ static void usart_fill_rxfifo(void) {
uint16_t usart_rxdata_available(void) {
usart_fill_rxfifo();
if (us_rxfifo_low <= us_rxfifo_high)
if (us_rxfifo_low <= us_rxfifo_high) {
return (us_rxfifo_high - us_rxfifo_low);
else
} else {
return (sizeof(us_rxfifo) - us_rxfifo_low + us_rxfifo_high);
}
}
uint32_t usart_read_ng(uint8_t *data, size_t len) {
if (len == 0) return 0;
if (len == 0) {
return 0;
}
uint32_t bytes_rcv = 0;
uint32_t try = 0;
// uint32_t highest_observed_try = 0;
@ -187,13 +197,16 @@ uint32_t usart_read_ng(uint8_t *data, size_t len) {
// highest_observed_try = MAX(highest_observed_try, try);
try = 0;
}
len -= packetSize;
while (packetSize--) {
if (us_rxfifo_low == sizeof(us_rxfifo)) {
us_rxfifo_low = 0;
}
data[bytes_rcv++] = us_rxfifo[us_rxfifo_low++];
}
if (try++ == maxtry) {
// Dbprintf_usb("Dbg USART TIMEOUT");
break;

View file

@ -55,6 +55,61 @@
static const uint8_t KENC_type[4] = {0x00, 0x00, 0x00, 0x01};
static const uint8_t KMAC_type[4] = {0x00, 0x00, 0x00, 0x02};
/*
* BAC = Basic Access Control
* PA = Passive Authentication
* AA = Active Authentication
* EAC = Extended Access Control
* SAC = Suppliment Access Control
File structures
----------------
Mastefile MF
-- EF.ATR/INFO (01)
-- EF.DIR (1E)
-- EF.CardSecurity (1D)
-- EF.CardAccess (1C)
Data Files DF
-- eMRTD Application DF (AID: )
-- Travel Records Application DF (AID: A0 00 00 02 47 20 01)
- EF.Certificates (1A)
- EF.EntryRecords (01)
- EF.ExitRecords (02)
-- Visa Records Application DF (AID: A0 00 00 02 47 20 02)
- EF.Certificates (1A)
- EF.ExitRecords (03)
-- Additional Biometrics Application DF (AID: A0 00 00 02 47 20 03)
- EF.Certificates (011A)
- EF.Biometrics1 (0201)
- EF.Biometrics2 (0202)
- EF.Biometrics64 (0240)
eMRTD Application DF
-----------------------
File names and what they contain
EG.COM = Common data
EG.DG1 = MRZ data
EG.DG2 = Biometric template, Photo
EG.DG3 = Biometric template, Fingerprint (EAC / AA)
EG.DG4 = Biometric template, Iris (EAC / AA)
EG.DG5 = Image template
EG.DG6 = Image template
EG.DG7 = Image template (Signature?)
EG.DG8 = Data Feature
EG.DG9 = Structure Feature
EG.DG10 = Substance Feature
EG.DG11 = Additional personal details
EG.DG12 = Additional Document Detail
EG.DG13 = Optional Details
EG.DG14 = Security Options
EG.DG15 = AA public key
EG.DG16 = Persons to notify
EG.SOD = Security, signatures of all data files
*/
const char *pad = ".....................................";
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, const char *path);
static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length, const char *path);
@ -63,6 +118,7 @@ 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_dg2_info(uint8_t *data, size_t datalen);
static int emrtd_print_ef_dg5_info(uint8_t *data, size_t datalen);
static int emrtd_print_ef_dg7_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_dg12_info(uint8_t *data, size_t datalen);
static int emrtd_print_ef_cardaccess_info(uint8_t *data, size_t datalen);
@ -99,7 +155,7 @@ static emrtd_dg_t dg_table[] = {
{0x76, 4, 0x0104, "EF_DG4", "Encoded Eye(s)", false, true, false, false, NULL, NULL},
{0x65, 5, 0x0105, "EF_DG5", "Displayed Portrait", false, false, false, false, emrtd_print_ef_dg5_info, emrtd_dump_ef_dg5},
{0x66, 6, 0x0106, "EF_DG6", "Reserved for Future Use", false, false, false, false, NULL, NULL},
{0x67, 7, 0x0107, "EF_DG7", "Displayed Signature or Usual Mark", false, false, false, false, NULL, emrtd_dump_ef_dg7},
{0x67, 7, 0x0107, "EF_DG7", "Displayed Signature or Usual Mark", false, false, false, false, emrtd_print_ef_dg7_info, emrtd_dump_ef_dg7},
{0x68, 8, 0x0108, "EF_DG8", "Data Feature(s)", false, false, false, true, NULL, NULL},
{0x69, 9, 0x0109, "EF_DG9", "Structure Feature(s)", false, false, false, true, NULL, NULL},
{0x6a, 10, 0x010A, "EF_DG10", "Substance Feature(s)", false, false, false, true, NULL, NULL},
@ -843,12 +899,12 @@ static bool emrtd_dump_file(uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, uint
strncat(filepath, PATHSEP, 2);
strcat(filepath, name);
PrintAndLogEx(INFO, "Read " _YELLOW_("%s") " , len %zu", name, resplen);
PrintAndLogEx(INFO, "Read " _YELLOW_("%s") ", len %zu", name, resplen);
PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars)");
PrintAndLogEx(DEBUG, "------------------------------------------");
PrintAndLogEx(DEBUG, "%s", sprint_hex_inrow(response, resplen));
PrintAndLogEx(DEBUG, "------------------------------------------");
saveFile(filepath, ".BIN", response, resplen);
saveFile(filepath, ".bin", response, resplen);
emrtd_dg_t *dg = emrtd_fileid_to_dg(file);
if ((dg != NULL) && (dg->dumper != NULL)) {
@ -935,7 +991,7 @@ static bool emrtd_do_bac(char *documentnumber, char *dob, char *expiry, uint8_t
PrintAndLogEx(ERR, "Couldn't do external authentication. Did you supply the correct MRZ info?");
return false;
}
PrintAndLogEx(INFO, "External authentication with BAC successful.");
PrintAndLogEx(INFO, "External authentication with BAC successful");
uint8_t dec_output[32] = { 0x00 };
des3_decrypt_cbc(iv, kenc, response, 32, dec_output);
@ -987,7 +1043,7 @@ static bool emrtd_do_auth(char *documentnumber, char *dob, char *expiry, bool BA
if (emrtd_select_file_by_ef(dg_table[EF_COM].fileid) == false) {
*BAC = true;
PrintAndLogEx(INFO, "Authentication is enforced");
PrintAndLogEx(INFO, "Switching to external authentication...");
PrintAndLogEx(INFO, "Switching to external authentication");
} else {
*BAC = false;
// Select EF_DG1
@ -998,7 +1054,7 @@ static bool emrtd_do_auth(char *documentnumber, char *dob, char *expiry, bool BA
if (emrtd_read_file(response, &resplen, NULL, NULL, NULL, false) == false) {
*BAC = true;
PrintAndLogEx(INFO, "Authentication is enforced");
PrintAndLogEx(INFO, "Switching to external authentication...");
PrintAndLogEx(INFO, "Switching to external authentication");
} else {
*BAC = false;
}
@ -1063,9 +1119,9 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab
strncat(filepath, PATHSEP, 2);
strcat(filepath, dg_table[EF_COM].filename);
PrintAndLogEx(INFO, "Read EF_COM, len: %zu", resplen);
PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen));
saveFile(filepath, ".BIN", response, resplen);
PrintAndLogEx(INFO, "Read EF_COM, len %zu", resplen);
PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars)... %s", sprint_hex_inrow(response, resplen));
saveFile(filepath, ".bin", response, resplen);
free(filepath);
@ -1078,17 +1134,17 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab
return PM3_ESOFT;
}
PrintAndLogEx(DEBUG, "File List: %s", sprint_hex_inrow(filelist, filelistlen));
PrintAndLogEx(DEBUG, "File List... %s", sprint_hex_inrow(filelist, filelistlen));
// Add EF_SOD to the list
filelist[filelistlen++] = 0x77;
// Dump all files in the file list
for (int i = 0; i < filelistlen; i++) {
emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]);
if (dg == NULL) {
PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]);
PrintAndLogEx(INFO, "File tag not found, skipping... %02X", filelist[i]);
continue;
}
PrintAndLogEx(DEBUG, "Current file: %s", dg->filename);
PrintAndLogEx(DEBUG, "Current file... %s", dg->filename);
if (!dg->pace && !dg->eac) {
emrtd_dump_file(ks_enc, ks_mac, ssc, dg->fileid, dg->filename, BAC, path);
}
@ -1129,7 +1185,7 @@ static void emrtd_print_legal_sex(char *legal_sex) {
strncpy(sex, "Unspecified", 12);
break;
}
PrintAndLogEx(SUCCESS, "Legal Sex Marker......: " _YELLOW_("%s"), sex);
PrintAndLogEx(SUCCESS, "Legal Sex Marker......... " _YELLOW_("%s"), sex);
}
static int emrtd_mrz_determine_length(const char *mrz, int offset, int max_length) {
@ -1168,7 +1224,7 @@ static void emrtd_print_optional_elements(char *mrz, int offset, int length, boo
return;
}
PrintAndLogEx(SUCCESS, "Optional elements.....: " _YELLOW_("%.*s"), i, mrz + offset);
PrintAndLogEx(SUCCESS, "Optional elements........ " _YELLOW_("%.*s"), i, mrz + offset);
if (verify_check_digit && !emrtd_mrz_verify_check_digit(mrz, offset, length)) {
PrintAndLogEx(SUCCESS, _RED_("Optional element check digit is invalid."));
@ -1181,7 +1237,7 @@ static void emrtd_print_document_number(char *mrz, int offset) {
return;
}
PrintAndLogEx(SUCCESS, "Document Number.......: " _YELLOW_("%.*s"), i, mrz + offset);
PrintAndLogEx(SUCCESS, "Document Number.......... " _YELLOW_("%.*s"), i, mrz + offset);
if (!emrtd_mrz_verify_check_digit(mrz, offset, 9)) {
PrintAndLogEx(SUCCESS, _RED_("Document number check digit is invalid."));
@ -1211,9 +1267,9 @@ static void emrtd_print_name(char *mrz, int offset, int max_length, bool localiz
emrtd_mrz_replace_pad(final_name, namelen, ' ');
if (localized) {
PrintAndLogEx(SUCCESS, "Legal Name (Localized): " _YELLOW_("%s"), final_name);
PrintAndLogEx(SUCCESS, "Legal Name (Localized)... " _YELLOW_("%s"), final_name);
} else {
PrintAndLogEx(SUCCESS, "Legal Name............: " _YELLOW_("%s"), final_name);
PrintAndLogEx(SUCCESS, "Legal Name............... " _YELLOW_("%s"), final_name);
}
}
@ -1260,7 +1316,7 @@ static void emrtd_print_dob(char *mrz, int offset, bool full, bool ascii) {
char final_date[12] = { 0x00 };
emrtd_mrz_convert_date(mrz, offset, final_date, false, full, ascii);
PrintAndLogEx(SUCCESS, "Date of birth.........: " _YELLOW_("%s"), final_date);
PrintAndLogEx(SUCCESS, "Date of birth............ " _YELLOW_("%s"), final_date);
if (!full && !emrtd_mrz_verify_check_digit(mrz, offset, 6)) {
PrintAndLogEx(SUCCESS, _RED_("Date of Birth check digit is invalid."));
@ -1271,7 +1327,7 @@ static void emrtd_print_expiry(char *mrz, int offset) {
char final_date[12] = { 0x00 };
emrtd_mrz_convert_date(mrz, offset, final_date, true, false, true);
PrintAndLogEx(SUCCESS, "Date of expiry........: " _YELLOW_("%s"), final_date);
PrintAndLogEx(SUCCESS, "Date of expiry........... " _YELLOW_("%s"), final_date);
if (!emrtd_mrz_verify_check_digit(mrz, offset, 6)) {
PrintAndLogEx(SUCCESS, _RED_("Date of expiry check digit is invalid."));
@ -1282,7 +1338,7 @@ static void emrtd_print_issuance(char *data, bool ascii) {
char final_date[12] = { 0x00 };
emrtd_mrz_convert_date(data, 0, final_date, true, true, ascii);
PrintAndLogEx(SUCCESS, "Date of issue.........: " _YELLOW_("%s"), final_date);
PrintAndLogEx(SUCCESS, "Date of issue............ " _YELLOW_("%s"), final_date);
}
static void emrtd_print_personalization_timestamp(uint8_t *data, size_t datalen) {
@ -1303,7 +1359,7 @@ static void emrtd_print_personalization_timestamp(uint8_t *data, size_t datalen)
, str_date + 12
);
PrintAndLogEx(SUCCESS, "Personalization at....: " _YELLOW_("%s"), final_date);
PrintAndLogEx(SUCCESS, "Personalization at....... " _YELLOW_("%s"), final_date);
}
static void emrtd_print_unknown_timestamp_5f85(uint8_t *data, size_t datalen) {
@ -1320,7 +1376,7 @@ static void emrtd_print_unknown_timestamp_5f85(uint8_t *data, size_t datalen) {
, data + 12
);
PrintAndLogEx(SUCCESS, "Unknown timestamp 5F85: " _YELLOW_("%s"), final_date);
PrintAndLogEx(SUCCESS, "Unknown timestamp 5F85... " _YELLOW_("%s"), final_date);
PrintAndLogEx(HINT, "This is very likely the personalization timestamp, but it is using an undocumented tag.");
}
@ -1335,14 +1391,15 @@ static int emrtd_print_ef_com_info(uint8_t *data, size_t datalen) {
// List files in the file list
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_COM") " --------------------");
PrintAndLogEx(INFO, "------------------------ " _CYAN_("EF_COM") " ------------------------");
for (int i = 0; i < filelistlen; i++) {
emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]);
if (dg == NULL) {
PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]);
continue;
}
PrintAndLogEx(SUCCESS, "%-7s...............: " _YELLOW_("%s"), dg->filename, dg->desc);
int n = 25 - strlen(dg->filename);
PrintAndLogEx(SUCCESS, "%s%*.*s " _YELLOW_("%s"), dg->filename, n, n, pad, dg->desc);
}
return PM3_SUCCESS;
}
@ -1351,7 +1408,7 @@ static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen) {
int td_variant = 0;
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_DG1") " --------------------");
PrintAndLogEx(INFO, "------------------------ " _CYAN_("EF_DG1") " ------------------------");
// MRZ on TD1 is 90 characters, 30 on each row.
// MRZ on TD3 is 88 characters, 44 on each row.
@ -1365,15 +1422,15 @@ static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen) {
// Determine and print the document type
if (mrz[0] == 'I' && mrz[1] == 'P') {
PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("Passport Card"));
PrintAndLogEx(SUCCESS, "Document Type............ " _YELLOW_("Passport Card"));
} else if (mrz[0] == 'I') {
PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("ID Card"));
PrintAndLogEx(SUCCESS, "Document Type............ " _YELLOW_("ID Card"));
} else if (mrz[0] == 'P') {
PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("Passport"));
PrintAndLogEx(SUCCESS, "Document Type............ " _YELLOW_("Passport"));
} else if (mrz[0] == 'A') {
PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("Residency Permit"));
PrintAndLogEx(SUCCESS, "Document Type............ " _YELLOW_("Residency Permit"));
} else {
PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("Unknown"));
PrintAndLogEx(SUCCESS, "Document Type............ " _YELLOW_("Unknown"));
}
if (mrzlen == 90) {
@ -1385,23 +1442,23 @@ static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen) {
return PM3_ESOFT;
}
PrintAndLogEx(SUCCESS, "Document Form Factor..: " _YELLOW_("TD%i"), td_variant);
PrintAndLogEx(SUCCESS, "Document Form Factor..... " _YELLOW_("TD%i"), td_variant);
// Print the MRZ
if (td_variant == 1) {
PrintAndLogEx(DEBUG, "MRZ Row 1: " _YELLOW_("%.30s"), mrz);
PrintAndLogEx(DEBUG, "MRZ Row 2: " _YELLOW_("%.30s"), mrz + 30);
PrintAndLogEx(DEBUG, "MRZ Row 3: " _YELLOW_("%.30s"), mrz + 60);
PrintAndLogEx(DEBUG, "MRZ Row 1... " _YELLOW_("%.30s"), mrz);
PrintAndLogEx(DEBUG, "MRZ Row 2... " _YELLOW_("%.30s"), mrz + 30);
PrintAndLogEx(DEBUG, "MRZ Row 3... " _YELLOW_("%.30s"), mrz + 60);
} else if (td_variant == 3) {
PrintAndLogEx(DEBUG, "MRZ Row 1: " _YELLOW_("%.44s"), mrz);
PrintAndLogEx(DEBUG, "MRZ Row 2: " _YELLOW_("%.44s"), mrz + 44);
PrintAndLogEx(DEBUG, "MRZ Row 1... " _YELLOW_("%.44s"), mrz);
PrintAndLogEx(DEBUG, "MRZ Row 2... " _YELLOW_("%.44s"), mrz + 44);
}
PrintAndLogEx(SUCCESS, "Issuing state.........: " _YELLOW_("%.3s"), mrz + 2);
PrintAndLogEx(SUCCESS, "Issuing state............ " _YELLOW_("%.3s"), mrz + 2);
if (td_variant == 3) {
// Passport form factor
PrintAndLogEx(SUCCESS, "Nationality...........: " _YELLOW_("%.3s"), mrz + 44 + 10);
PrintAndLogEx(SUCCESS, "Nationality.............. " _YELLOW_("%.3s"), mrz + 44 + 10);
emrtd_print_name(mrz, 5, 38, false);
emrtd_print_document_number(mrz, 44);
emrtd_print_dob(mrz, 44 + 13, false, true);
@ -1415,12 +1472,12 @@ static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen) {
memcpy(composite_check_data + 10, mrz + 44 + 13, 7);
memcpy(composite_check_data + 17, mrz + 44 + 21, 23);
if (!emrtd_compare_check_digit(composite_check_data, 39, mrz[87])) {
if (emrtd_compare_check_digit(composite_check_data, 39, mrz[87]) == false) {
PrintAndLogEx(SUCCESS, _RED_("Composite check digit is invalid."));
}
} else if (td_variant == 1) {
// ID form factor
PrintAndLogEx(SUCCESS, "Nationality...........: " _YELLOW_("%.3s"), mrz + 30 + 15);
PrintAndLogEx(SUCCESS, "Nationality.............. " _YELLOW_("%.3s"), mrz + 30 + 15);
emrtd_print_name(mrz, 60, 30, false);
emrtd_print_document_number(mrz, 5);
emrtd_print_dob(mrz, 30, false, true);
@ -1430,7 +1487,7 @@ static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen) {
emrtd_print_optional_elements(mrz, 30 + 18, 11, false);
// Calculate and verify composite check digit
if (!emrtd_compare_check_digit(mrz, 59, mrz[59])) {
if (emrtd_compare_check_digit(mrz, 59, mrz[59]) == false) {
PrintAndLogEx(SUCCESS, _RED_("Composite check digit is invalid."));
}
}
@ -1488,6 +1545,31 @@ static int emrtd_print_ef_dg5_info(uint8_t *data, size_t datalen) {
return PM3_SUCCESS;
}
static int emrtd_print_ef_dg7_info(uint8_t *data, size_t datalen) {
int offset = 0;
// This is a hacky impl that just looks for the image header. I'll improve it eventually.
// based on mrpkey.py
// Note: Doing datalen - 6 to account for the longest data we're checking.
// Checks first byte before the rest to reduce overhead
for (offset = 0; offset < datalen - 6; offset++) {
if ((data[offset] == 0xFF && memcmp(jpeg_header, data + offset, 4) == 0) ||
(data[offset] == 0x00 && memcmp(jpeg2k_header, data + offset, 6) == 0)) {
datalen = datalen - offset;
break;
}
}
// If we didn't get any data, return false.
if (datalen == 0) {
return PM3_ESOFT;
}
ShowPictureWindow(data + offset, datalen);
return PM3_SUCCESS;
}
static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen) {
uint8_t taglist[100] = { 0x00 };
size_t taglistlen = 0;
@ -1495,7 +1577,7 @@ static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen) {
size_t tagdatalen = 0;
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_DG11") " -------------------");
PrintAndLogEx(INFO, "------------------------ " _CYAN_("EF_DG11") " -----------------------");
if (emrtd_lds_get_data_by_tag(data, datalen, taglist, &taglistlen, 0x5c, 0x00, false, true, 0) == false) {
PrintAndLogEx(ERR, "Failed to read file list from EF_DG11.");
@ -1519,50 +1601,50 @@ static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen) {
emrtd_print_name((char *) tagdata, 0, tagdatalen, false);
break;
case 0x10:
PrintAndLogEx(SUCCESS, "Personal Number.......: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
PrintAndLogEx(SUCCESS, "Personal Number.......... " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
break;
case 0x11:
// TODO: acc for < separation
PrintAndLogEx(SUCCESS, "Place of Birth........: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
PrintAndLogEx(SUCCESS, "Place of Birth........... " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
break;
case 0x42:
// TODO: acc for < separation
PrintAndLogEx(SUCCESS, "Permanent Address.....: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
PrintAndLogEx(SUCCESS, "Permanent Address........ " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
break;
case 0x12:
PrintAndLogEx(SUCCESS, "Telephone.............: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
PrintAndLogEx(SUCCESS, "Telephone................ " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
break;
case 0x13:
PrintAndLogEx(SUCCESS, "Profession............: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
PrintAndLogEx(SUCCESS, "Profession............... " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
break;
case 0x14:
PrintAndLogEx(SUCCESS, "Title.................: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
PrintAndLogEx(SUCCESS, "Title.................... " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
break;
case 0x15:
PrintAndLogEx(SUCCESS, "Personal Summary......: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
PrintAndLogEx(SUCCESS, "Personal Summary......... " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
break;
case 0x16:
saveFile("ProofOfCitizenship", tagdata[0] == 0xFF ? ".jpg" : ".jp2", tagdata, tagdatalen);
break;
case 0x17:
// TODO: acc for < separation
PrintAndLogEx(SUCCESS, "Other valid TDs nums..: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
PrintAndLogEx(SUCCESS, "Other valid TDs nums..... " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
break;
case 0x18:
PrintAndLogEx(SUCCESS, "Custody Information...: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
PrintAndLogEx(SUCCESS, "Custody Information...... " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
break;
case 0x2b:
emrtd_print_dob((char *) tagdata, 0, true, tagdatalen != 4);
break;
default:
PrintAndLogEx(SUCCESS, "Unknown Field %02X%02X....: %s", taglist[i], taglist[i + 1], sprint_hex_inrow(tagdata, tagdatalen));
PrintAndLogEx(SUCCESS, "Unknown Field %02X%02X....... %s", taglist[i], taglist[i + 1], sprint_hex_inrow(tagdata, tagdatalen));
break;
}
i += 1;
} else {
// TODO: Account for A0
PrintAndLogEx(SUCCESS, "Unknown Field %02X......: %s", taglist[i], sprint_hex_inrow(tagdata, tagdatalen));
PrintAndLogEx(SUCCESS, "Unknown Field %02X......... %s", taglist[i], sprint_hex_inrow(tagdata, tagdatalen));
}
}
return PM3_SUCCESS;
@ -1575,7 +1657,7 @@ static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen) {
size_t tagdatalen = 0;
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_DG12") " -------------------");
PrintAndLogEx(INFO, "------------------------ " _CYAN_("EF_DG12") " -----------------------");
if (emrtd_lds_get_data_by_tag(data, datalen, taglist, &taglistlen, 0x5c, 0x00, false, true, 0) == false) {
PrintAndLogEx(ERR, "Failed to read file list from EF_DG12.");
@ -1595,16 +1677,16 @@ static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen) {
// ...and I doubt many states are using them.
switch (taglist[i + 1]) {
case 0x19:
PrintAndLogEx(SUCCESS, "Issuing Authority.....: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
PrintAndLogEx(SUCCESS, "Issuing Authority........ " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
break;
case 0x26:
emrtd_print_issuance((char *) tagdata, tagdatalen != 4);
break;
case 0x1b:
PrintAndLogEx(SUCCESS, "Endorsements & Observations: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
PrintAndLogEx(SUCCESS, "Endorsements & Observations... " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
break;
case 0x1c:
PrintAndLogEx(SUCCESS, "Tax/Exit Requirements.: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
PrintAndLogEx(SUCCESS, "Tax/Exit Requirements.... " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
break;
case 0x1d:
saveFile("FrontOfDocument", tagdata[0] == 0xFF ? ".jpg" : ".jp2", tagdata, tagdatalen);
@ -1616,20 +1698,20 @@ static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen) {
emrtd_print_personalization_timestamp(tagdata, tagdatalen);
break;
case 0x56:
PrintAndLogEx(SUCCESS, "Serial of Personalization System: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
PrintAndLogEx(SUCCESS, "Serial of Personalization System... " _YELLOW_("%.*s"), (int)tagdatalen, tagdata);
break;
case 0x85:
emrtd_print_unknown_timestamp_5f85(tagdata, tagdatalen);
break;
default:
PrintAndLogEx(SUCCESS, "Unknown Field %02X%02X....: %s", taglist[i], taglist[i + 1], sprint_hex_inrow(tagdata, tagdatalen));
PrintAndLogEx(SUCCESS, "Unknown Field %02X%02X....... %s", taglist[i], taglist[i + 1], sprint_hex_inrow(tagdata, tagdatalen));
break;
}
i += 1;
} else {
// TODO: Account for A0
PrintAndLogEx(SUCCESS, "Unknown Field %02X......: %s", taglist[i], sprint_hex_inrow(tagdata, tagdatalen));
PrintAndLogEx(SUCCESS, "Unknown Field %02X......... %s", taglist[i], sprint_hex_inrow(tagdata, tagdatalen));
}
}
return PM3_SUCCESS;
@ -1737,16 +1819,16 @@ static int emrtd_parse_ef_sod_hashes(uint8_t *data, size_t datalen, uint8_t *has
return false;
}
PrintAndLogEx(DEBUG, "hash data: %s", sprint_hex_inrow(emrtdsig, emrtdsiglen));
PrintAndLogEx(DEBUG, "hash data... %s", sprint_hex_inrow(emrtdsig, emrtdsiglen));
emrtd_parse_ef_sod_hash_algo(emrtdsig, emrtdsiglen, hashalgo);
if (emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, hashlist, &hashlistlen, 0x30, 0x00, false, true, 1) == false) {
PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD.");
PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD");
return false;
}
PrintAndLogEx(DEBUG, "hash list: %s", sprint_hex_inrow(hashlist, hashlistlen));
PrintAndLogEx(DEBUG, "hash list... %s", sprint_hex_inrow(hashlist, hashlistlen));
while (offset < hashlistlen) {
// Get the length of the element
@ -1779,33 +1861,47 @@ static int emrtd_parse_ef_sod_hashes(uint8_t *data, size_t datalen, uint8_t *has
static int emrtd_print_ef_sod_info(uint8_t *dg_hashes_calc, uint8_t *dg_hashes_sod, int hash_algo, bool fastdump) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_SOD") " --------------------");
PrintAndLogEx(INFO, "------------------------ " _CYAN_("EF_SOD") " ------------------------");
PrintAndLogEx(INFO, "Document Security Object, ");
PrintAndLogEx(INFO, "contains the digital signatures of the passport data");
PrintAndLogEx(INFO, "");
if (hash_algo == -1) {
PrintAndLogEx(SUCCESS, "Hash algorithm: " _YELLOW_("Unknown"));
PrintAndLogEx(SUCCESS, "Hash algorithm... " _YELLOW_("Unknown"));
} else {
PrintAndLogEx(SUCCESS, "Hash algorithm: " _YELLOW_("%s"), hashalg_table[hash_algo].name);
PrintAndLogEx(SUCCESS, "Hash algorithm... " _YELLOW_("%s"), hashalg_table[hash_algo].name);
uint8_t all_zeroes[64] = { 0x00 };
for (int i = 1; i <= 16; i++) {
bool calc_all_zero = (memcmp(dg_hashes_calc + (i * 64), all_zeroes, hashalg_table[hash_algo].hashlen) == 0);
bool sod_all_zero = (memcmp(dg_hashes_sod + (i * 64), all_zeroes, hashalg_table[hash_algo].hashlen) == 0);
bool hash_matches = (memcmp(dg_hashes_sod + (i * 64), dg_hashes_calc + (i * 64), hashalg_table[hash_algo].hashlen) == 0);
// Ignore files we don't haven't read and lack hashes to
if (calc_all_zero == true && sod_all_zero == true) {
continue;
} else if (calc_all_zero == true) {
if (fastdump && !dg_table[i].fastdump && !dg_table[i].pace && !dg_table[i].eac) {
PrintAndLogEx(SUCCESS, "EF_DG%i: " _YELLOW_("File was skipped, but is in EF_SOD."), i);
} else {
PrintAndLogEx(SUCCESS, "EF_DG%i: " _YELLOW_("File couldn't be read, but is in EF_SOD."), i);
}
} else if (sod_all_zero == true) {
PrintAndLogEx(SUCCESS, "EF_DG%i: " _YELLOW_("File is not in EF_SOD."), i);
} else if (hash_matches == false) {
PrintAndLogEx(SUCCESS, "EF_DG%i: " _RED_("Invalid"), i);
// silly padding thingy
int n = 40 - strlen(dg_table[i].desc);
if (calc_all_zero == true) {
if (fastdump && !dg_table[i].fastdump && !dg_table[i].pace && !dg_table[i].eac) {
PrintAndLogEx(SUCCESS, "EF_DG%2i %s %*.*s File was skipped, but is in EF_SOD", i, dg_table[i].desc, n, n, pad);
} else {
PrintAndLogEx(SUCCESS, "EF_DG%i: " _GREEN_("Valid"), i);
PrintAndLogEx(SUCCESS, "EF_DG%2i %s %*.*s File couldn't be read, but is in EF_SOD", i, dg_table[i].desc, n, n, pad);
}
} else if (sod_all_zero == true) {
PrintAndLogEx(SUCCESS, "EF_DG%2i %s %*.*s " _RED_("File is not in EF_SOD"), i, dg_table[i].desc, n, n, pad);
} else if (hash_matches == false) {
PrintAndLogEx(SUCCESS, "EF_DG%2i %s %*.*s " _RED_("Invalid"), i, dg_table[i].desc, n, n, pad);
} else {
PrintAndLogEx(SUCCESS, "EF_DG%2i %s %*.*s " _GREEN_("Valid"), i, dg_table[i].desc, n, n, pad);
}
}
}
@ -1821,7 +1917,7 @@ static int emrtd_print_ef_cardaccess_info(uint8_t *data, size_t datalen) {
uint8_t parsednum = 0;
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "----------------- " _CYAN_("EF_CardAccess") " ----------------");
PrintAndLogEx(INFO, "--------------------- " _CYAN_("EF_CardAccess") " --------------------");
if (emrtd_lds_get_data_by_tag(data, datalen, dataset, &datasetlen, 0x30, 0x00, false, true, 0) == false) {
PrintAndLogEx(ERR, "Failed to read set from EF_CardAccess.");
@ -1835,7 +1931,7 @@ static int emrtd_print_ef_cardaccess_info(uint8_t *data, size_t datalen) {
}
// TODO: hack!!!
memcpy(&parsednum, datafromtag, datafromtaglen);
PrintAndLogEx(SUCCESS, "PACE version..........: " _YELLOW_("%i"), parsednum);
PrintAndLogEx(SUCCESS, "PACE version............. " _YELLOW_("%i"), parsednum);
// Get PACE algorithm
if (emrtd_lds_get_data_by_tag(dataset, datasetlen, datafromtag, &datafromtaglen, 0x06, 0x00, false, false, 0) == false) {
@ -1847,7 +1943,7 @@ static int emrtd_print_ef_cardaccess_info(uint8_t *data, size_t datalen) {
PrintAndLogEx(DEBUG, "Trying: %s", pacealg_table[pacei].name);
if (memcmp(pacealg_table[pacei].descriptor, datafromtag, datafromtaglen) == 0) {
PrintAndLogEx(SUCCESS, "PACE algorithm........: " _YELLOW_("%s"), pacealg_table[pacei].name);
PrintAndLogEx(SUCCESS, "PACE algorithm........... " _YELLOW_("%s"), pacealg_table[pacei].name);
}
}
@ -1863,7 +1959,7 @@ static int emrtd_print_ef_cardaccess_info(uint8_t *data, size_t datalen) {
PrintAndLogEx(DEBUG, "Trying: %s", pacesdp_table[pacepari].name);
if (pacesdp_table[pacepari].id == parsednum) {
PrintAndLogEx(SUCCESS, "PACE parameter........: " _YELLOW_("%s"), pacesdp_table[pacepari].name);
PrintAndLogEx(SUCCESS, "PACE parameter........... " _YELLOW_("%s"), pacesdp_table[pacepari].name);
}
// TODO: account for RFU
}
@ -1897,24 +1993,24 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab
bool auth_result = emrtd_do_auth(documentnumber, dob, expiry, BAC_available, &BAC, ssc, ks_enc, ks_mac);
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "------------------ " _CYAN_("Basic Info") " ------------------");
PrintAndLogEx(SUCCESS, "Communication standard: %s", use14b ? _YELLOW_("ISO/IEC 14443(B)") : _YELLOW_("ISO/IEC 14443(A)"));
PrintAndLogEx(SUCCESS, "Authentication........: %s", BAC ? _GREEN_("Enforced") : _RED_("Not enforced"));
PrintAndLogEx(SUCCESS, "PACE..................: %s", PACE_available ? _GREEN_("Available") : _YELLOW_("Not available"));
PrintAndLogEx(SUCCESS, "Authentication result.: %s", auth_result ? _GREEN_("Successful") : _RED_("Failed"));
PrintAndLogEx(INFO, "---------------------- " _CYAN_("Basic Info") " ----------------------");
PrintAndLogEx(SUCCESS, "Communication standard... %s", use14b ? _YELLOW_("ISO/IEC 14443(B)") : _YELLOW_("ISO/IEC 14443(A)"));
PrintAndLogEx(SUCCESS, "Authentication........... %s", BAC ? _GREEN_("Enforced") : _RED_("Not enforced"));
PrintAndLogEx(SUCCESS, "PACE..................... %s", PACE_available ? _GREEN_("Available") : _YELLOW_("Not available"));
PrintAndLogEx(SUCCESS, "Authentication result.... %s", auth_result ? _GREEN_("Successful") : _RED_("Failed"));
if (PACE_available) {
emrtd_print_ef_cardaccess_info(response, resplen);
}
if (!auth_result) {
if (auth_result == false) {
DropField();
return PM3_ESOFT;
}
// Read EF_COM to get file list
if (!emrtd_select_and_read(response, &resplen, dg_table[EF_COM].fileid, ks_enc, ks_mac, ssc, BAC)) {
PrintAndLogEx(ERR, "Failed to read EF_COM.");
if (emrtd_select_and_read(response, &resplen, dg_table[EF_COM].fileid, ks_enc, ks_mac, ssc, BAC) == false) {
PrintAndLogEx(ERR, "Failed to read EF_COM");
DropField();
return PM3_ESOFT;
}
@ -1947,27 +2043,29 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab
res = emrtd_parse_ef_sod_hashes(response, resplen, *dg_hashes_sod, &hash_algo);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD. Hash checks will fail.");
PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD. Hash checks will fail");
}
// Dump all files in the file list
for (int i = 0; i < filelistlen; i++) {
emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]);
if (dg == NULL) {
PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]);
PrintAndLogEx(INFO, "File tag not found, skipping... %02X", filelist[i]);
continue;
}
if (((dg->fastdump && only_fast) || !only_fast) && !dg->pace && !dg->eac) {
if (emrtd_select_and_read(response, &resplen, dg->fileid, ks_enc, ks_mac, ssc, BAC)) {
if (dg->parser != NULL)
dg->parser(response, resplen);
PrintAndLogEx(DEBUG, "EF_DG%i hash algo: %i", dg->dgnum, hash_algo);
PrintAndLogEx(DEBUG, "EF_DG%i hash algo... %i", dg->dgnum, hash_algo);
// Check file hash
if (hash_algo != -1) {
PrintAndLogEx(DEBUG, "EF_DG%i hash on EF_SOD: %s", dg->dgnum, sprint_hex_inrow(dg_hashes_sod[dg->dgnum], hashalg_table[hash_algo].hashlen));
PrintAndLogEx(DEBUG, "EF_DG%i hash on EF_SOD... %s", dg->dgnum, sprint_hex_inrow(dg_hashes_sod[dg->dgnum], hashalg_table[hash_algo].hashlen));
hashalg_table[hash_algo].hasher(response, resplen, dg_hashes_calc[dg->dgnum]);
PrintAndLogEx(DEBUG, "EF_DG%i hash calc: %s", dg->dgnum, sprint_hex_inrow(dg_hashes_calc[dg->dgnum], hashalg_table[hash_algo].hashlen));
PrintAndLogEx(DEBUG, "EF_DG%i hash calc........ %s", dg->dgnum, sprint_hex_inrow(dg_hashes_calc[dg->dgnum], hashalg_table[hash_algo].hashlen));
}
}
}
@ -2008,7 +2106,7 @@ int infoHF_EMRTD_offline(const char *path) {
size_t filelistlen = 0;
res = emrtd_lds_get_data_by_tag(data, datalen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0);
if (res == false) {
PrintAndLogEx(ERR, "Failed to read file list from EF_COM.");
PrintAndLogEx(ERR, "Failed to read file list from EF_COM");
free(data);
free(filepath);
return PM3_ESOFT;
@ -2059,7 +2157,7 @@ int infoHF_EMRTD_offline(const char *path) {
for (int i = 0; i < filelistlen; i++) {
emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]);
if (dg == NULL) {
PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]);
PrintAndLogEx(INFO, "File tag not found, skipping... %02X", filelist[i]);
continue;
}
if (!dg->pace && !dg->eac) {
@ -2073,12 +2171,12 @@ int infoHF_EMRTD_offline(const char *path) {
dg->parser(data, datalen);
}
PrintAndLogEx(DEBUG, "EF_DG%i hash algo: %i", dg->dgnum, hash_algo);
PrintAndLogEx(DEBUG, "EF_DG%i hash algo... %i", dg->dgnum, hash_algo);
// Check file hash
if (hash_algo != -1) {
PrintAndLogEx(DEBUG, "EF_DG%i hash on EF_SOD: %s", dg->dgnum, sprint_hex_inrow(dg_hashes_sod[dg->dgnum], hashalg_table[hash_algo].hashlen));
PrintAndLogEx(DEBUG, "EF_DG%i hash on EF_SOD... %s", dg->dgnum, sprint_hex_inrow(dg_hashes_sod[dg->dgnum], hashalg_table[hash_algo].hashlen));
hashalg_table[hash_algo].hasher(data, datalen, dg_hashes_calc[dg->dgnum]);
PrintAndLogEx(DEBUG, "EF_DG%i hash calc: %s", dg->dgnum, sprint_hex_inrow(dg_hashes_calc[dg->dgnum], hashalg_table[hash_algo].hashlen));
PrintAndLogEx(DEBUG, "EF_DG%i hash calc........ %s", dg->dgnum, sprint_hex_inrow(dg_hashes_calc[dg->dgnum], hashalg_table[hash_algo].hashlen));
}
free(data);
}
@ -2202,7 +2300,13 @@ static int CmdHFeMRTDDump(const char *Cmd) {
if (g_debugMode >= 2) {
SetAPDULogging(true);
}
uint64_t t1 = msclock();
int res = dumpHF_EMRTD((char *)docnum, (char *)dob, (char *)expiry, BAC, (const char *)path);
PrintAndLogEx(SUCCESS, "time: %" PRIu64 " seconds\n", (msclock() - t1) / 1000);
SetAPDULogging(restore_apdu_logging);
return res;
}
@ -2293,13 +2397,16 @@ static int CmdHFeMRTDInfo(const char *Cmd) {
bool is_offline = CLIParamStrToBuf(arg_get_str(ctx, 5), path, sizeof(path), &slen) == 0 && slen > 0;
bool show_images = arg_get_lit(ctx, 6);
CLIParserFree(ctx);
if ((IfPm3Iso14443() == false) && (is_offline == false)) {
PrintAndLogEx(WARNING, "Only offline mode is available");
error = true;
}
if (error) {
return PM3_ESOFT;
}
if (is_offline) {
return infoHF_EMRTD_offline((const char *)path);
} else {

View file

@ -632,12 +632,15 @@ bool usb_check(void) {
}
bool usb_poll(void) {
if (!usb_check()) return false;
if (usb_check() == false) {
return false;
}
return (pUdp->UDP_CSR[AT91C_EP_OUT] & btReceiveBank);
}
inline uint16_t usb_available_length(void) {
return ((pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16);
return (((pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16) & 0x7FF);
}
/**
@ -649,9 +652,16 @@ inline uint16_t usb_available_length(void) {
bug.
**/
bool usb_poll_validate_length(void) {
if (!usb_check()) return false;
if (!(pUdp->UDP_CSR[AT91C_EP_OUT] & btReceiveBank)) return false;
return ((pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16) > 0;
if (usb_check() == false) {
return false;
}
if (!(pUdp->UDP_CSR[AT91C_EP_OUT] & btReceiveBank)) {
return false;
}
return (((pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16) > 0);
}
/*
@ -662,21 +672,25 @@ bool usb_poll_validate_length(void) {
*/
uint32_t usb_read(uint8_t *data, size_t len) {
if (len == 0) return 0;
if (len == 0) {
return 0;
}
uint8_t bank = btReceiveBank;
uint32_t packetSize, nbBytesRcv = 0;
uint32_t time_out = 0;
uint16_t packetSize, nbBytesRcv = 0;
uint16_t time_out = 0;
while (len) {
if (!usb_check())
if (usb_check() == false) {
break;
}
if (pUdp->UDP_CSR[AT91C_EP_OUT] & bank) {
packetSize = ((pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16);
packetSize = (((pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16) & 0x7FF);
packetSize = MIN(packetSize, len);
len -= packetSize;
while (packetSize--) {
data[nbBytesRcv++] = pUdp->UDP_FDR[AT91C_EP_OUT];
}
@ -684,84 +698,109 @@ uint32_t usb_read(uint8_t *data, size_t len) {
// flip bank
UDP_CLEAR_EP_FLAGS(AT91C_EP_OUT, bank)
if (bank == AT91C_UDP_RX_DATA_BK0)
if (bank == AT91C_UDP_RX_DATA_BK0) {
bank = AT91C_UDP_RX_DATA_BK1;
else
} else {
bank = AT91C_UDP_RX_DATA_BK0;
}
}
if (time_out++ == 0x1fff)
if (time_out++ == 0x1FFF) {
break;
}
}
btReceiveBank = bank;
return nbBytesRcv;
}
static uint8_t usb_read_ng_buffer[64] = {0};
static size_t usb_read_ng_bufoff = 0;
static size_t usb_read_ng_buflen = 0;
static uint8_t usb_read_ng_bufoffset = 0;
static uint8_t usb_read_ng_buflen = 0;
uint32_t usb_read_ng(uint8_t *data, size_t len) {
if (len == 0)
if (len == 0) {
return 0;
}
uint8_t bank = btReceiveBank;
uint32_t packetSize, nbBytesRcv = 0;
uint32_t time_out = 0;
uint16_t packetSize, nbBytesRcv = 0;
uint16_t time_out = 0;
// take first from local buffer
if (len <= usb_read_ng_buflen) {
for (uint32_t i = 0; i < len; i++) {
data[nbBytesRcv++] = usb_read_ng_buffer[usb_read_ng_bufoff + i];
// if local buffer has all data
for (uint8_t i = 0; i < len; i++) {
data[nbBytesRcv++] = usb_read_ng_buffer[usb_read_ng_bufoffset + i];
}
usb_read_ng_buflen -= len;
if (usb_read_ng_buflen == 0)
usb_read_ng_bufoff = 0;
else
usb_read_ng_bufoff += len;
if (usb_read_ng_buflen == 0) {
usb_read_ng_bufoffset = 0;
} else {
usb_read_ng_bufoffset += len;
}
return nbBytesRcv;
} else {
for (uint32_t i = 0; i < usb_read_ng_buflen; i++) {
data[nbBytesRcv++] = usb_read_ng_buffer[usb_read_ng_bufoff + i];
}
len -= usb_read_ng_buflen;
usb_read_ng_buflen = 0;
usb_read_ng_bufoff = 0;
// take all data from local buffer, then read from usb
for (uint8_t i = 0; i < usb_read_ng_buflen; i++) {
data[nbBytesRcv++] = usb_read_ng_buffer[usb_read_ng_bufoffset + i];
}
len -= usb_read_ng_buflen;
usb_read_ng_buflen = 0;
usb_read_ng_bufoffset = 0;
}
while (len) {
if (!usb_check())
if (usb_check() == false) {
break;
}
if ((pUdp->UDP_CSR[AT91C_EP_OUT] & bank)) {
uint32_t available = ((pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16);
uint16_t available = (((pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16) & 0x7FF);
packetSize = MIN(available, len);
available -= packetSize;
len -= packetSize;
while (packetSize--) {
data[nbBytesRcv++] = pUdp->UDP_FDR[AT91C_EP_OUT];
}
// fill the local buffer with the remaining bytes
for (uint32_t i = 0; i < available; i++) {
for (uint16_t i = 0; i < available; i++) {
usb_read_ng_buffer[i] = pUdp->UDP_FDR[AT91C_EP_OUT];
}
// update number of available bytes in local bytes
usb_read_ng_buflen = available;
// flip bank
UDP_CLEAR_EP_FLAGS(AT91C_EP_OUT, bank)
if (bank == AT91C_UDP_RX_DATA_BK0)
if (bank == AT91C_UDP_RX_DATA_BK0) {
bank = AT91C_UDP_RX_DATA_BK1;
else
} else {
bank = AT91C_UDP_RX_DATA_BK0;
}
if (time_out++ == 0x1fff)
}
if (time_out++ == 0x1FFF) {
break;
}
}
btReceiveBank = bank;
return nbBytesRcv;
@ -775,16 +814,22 @@ uint32_t usb_read_ng(uint8_t *data, size_t len) {
*/
int usb_write(const uint8_t *data, const size_t len) {
if (!len) return PM3_EINVARG;
if (!usb_check()) return PM3_EIO;
if (len == 0) {
return PM3_EINVARG;
}
if (usb_check() == false) {
return PM3_EIO;
}
// can we write?
if ((pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXPKTRDY) != 0) return PM3_EIO;
if ((pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXPKTRDY) != 0) {
return PM3_EIO;
}
size_t length = len;
uint32_t cpt = 0;
// send first chunk
cpt = MIN(length, AT91C_USB_EP_IN_SIZE);
length -= cpt;
@ -806,7 +851,9 @@ int usb_write(const uint8_t *data, const size_t len) {
// Wait for previous chunk to be sent
// (iceman) when is the bankswapping done?
while (!(pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP)) {
if (!usb_check()) return PM3_EIO;
if (usb_check() == false) {
return PM3_EIO;
}
}
UDP_CLEAR_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXCOMP);
@ -818,7 +865,9 @@ int usb_write(const uint8_t *data, const size_t len) {
// Wait for the end of transfer
while (!(pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP)) {
if (!usb_check()) return PM3_EIO;
if (usb_check() == false) {
return PM3_EIO;
}
}
UDP_CLEAR_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXCOMP);
@ -852,10 +901,14 @@ int usb_write(const uint8_t *data, const size_t len) {
*/
int async_usb_write_start(void) {
if (!usb_check()) return PM3_EIO;
if (usb_check() == false) {
return PM3_EIO;
}
while (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXPKTRDY) {
if (!usb_check()) return PM3_EIO;
if (usb_check() == false) {
return PM3_EIO;
}
}
isAsyncRequestFinished = false;
@ -927,7 +980,9 @@ inline bool async_usb_write_requestWrite(void) {
int async_usb_write_stop(void) {
// Wait for the end of transfer
while (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXPKTRDY) {
if (!usb_check()) return PM3_EIO;
if (usb_check() == false) {
return PM3_EIO;
}
}
// clear transmission completed flag
@ -939,7 +994,9 @@ int async_usb_write_stop(void) {
UDP_SET_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXPKTRDY);
while (!(pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP)) {
if (!usb_check()) return PM3_EIO;
if (usb_check() == false) {
return PM3_EIO;
}
}
UDP_CLEAR_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXCOMP);
@ -961,12 +1018,13 @@ void AT91F_USB_SendData(AT91PS_UDP pudp, const char *pData, uint32_t length) {
uint32_t cpt = MIN(length, AT91C_USB_EP_CONTROL_SIZE);
length -= cpt;
while (cpt--)
while (cpt--) {
pudp->UDP_FDR[AT91C_EP_CONTROL] = *pData++;
}
if (pudp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP) {
UDP_CLEAR_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_TXCOMP);
while (pudp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP);
while (pudp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP) {};
}
UDP_SET_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_TXPKTRDY);
@ -985,7 +1043,7 @@ void AT91F_USB_SendData(AT91PS_UDP pudp, const char *pData, uint32_t length) {
if (pudp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP) {
UDP_CLEAR_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_TXCOMP);
while (pudp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP);
while (pudp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP) {};
}
}
@ -1025,8 +1083,9 @@ void AT91F_CDC_Enumerate(void) {
uint8_t bmRequestType, bRequest;
uint16_t wValue, wIndex, wLength, wStatus;
if (!(pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_RXSETUP))
if (!(pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_RXSETUP)) {
return;
}
bmRequestType = pUdp->UDP_FDR[AT91C_EP_CONTROL];
bRequest = pUdp->UDP_FDR[AT91C_EP_CONTROL];
@ -1066,13 +1125,13 @@ void AT91F_CDC_Enumerate(void) {
switch ((bRequest << 8) | bmRequestType) {
case STD_GET_DESCRIPTOR: {
if (wValue == 0x100) // Return Device Descriptor
if (wValue == 0x100) { // Return Device Descriptor
AT91F_USB_SendData(pUdp, devDescriptor, MIN(sizeof(devDescriptor), wLength));
else if (wValue == 0x200) // Return Configuration Descriptor
} else if (wValue == 0x200) { // Return Configuration Descriptor
AT91F_USB_SendData(pUdp, cfgDescriptor, MIN(sizeof(cfgDescriptor), wLength));
else if ((wValue & 0xF00) == 0xF00) // Return BOS Descriptor
} else if ((wValue & 0xF00) == 0xF00) { // Return BOS Descriptor
AT91F_USB_SendData(pUdp, bosDescriptor, MIN(sizeof(bosDescriptor), wLength));
else if ((wValue & 0x300) == 0x300) { // Return String Descriptor
} else if ((wValue & 0x300) == 0x300) { // Return String Descriptor
const char *strDescriptor = getStringDescriptor(wValue & 0xff);
if (strDescriptor != NULL) {
@ -1164,9 +1223,13 @@ void AT91F_CDC_Enumerate(void) {
wIndex &= 0x0F;
if ((wValue == 0) && (wIndex >= AT91C_EP_OUT) && (wIndex <= AT91C_EP_NOTIFY)) {
if (wIndex == AT91C_EP_OUT) pUdp->UDP_CSR[AT91C_EP_OUT] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_OUT);
else if (wIndex == AT91C_EP_IN) pUdp->UDP_CSR[AT91C_EP_IN] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_IN);
else if (wIndex == AT91C_EP_NOTIFY) pUdp->UDP_CSR[AT91C_EP_NOTIFY] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_INT_IN);
if (wIndex == AT91C_EP_OUT) {
pUdp->UDP_CSR[AT91C_EP_OUT] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_OUT);
} else if (wIndex == AT91C_EP_IN) {
pUdp->UDP_CSR[AT91C_EP_IN] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_IN);
} else if (wIndex == AT91C_EP_NOTIFY) {
pUdp->UDP_CSR[AT91C_EP_NOTIFY] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_INT_IN);
}
AT91F_USB_SendZlp(pUdp);
} else {