MRTD Read DG1 + small changes

This commit is contained in:
Chris van Marle
2022-10-11 22:13:41 +02:00
parent 0ab7d91fb4
commit d1a1cbff82
4 changed files with 149 additions and 17 deletions
@@ -48,6 +48,17 @@ void nfc_scene_passport_read_auth_on_enter(void* context) {
}
}
EF_DG1_contents* DG1 = &mrtd_data->files.DG1;
string_cat_printf(temp_str, "\e#DG1\n");
string_cat_printf(temp_str, "Doc Type: %s\n", DG1->doctype);
string_cat_printf(temp_str, "Issuing State: %s\n", DG1->issuing_state);
string_cat_printf(temp_str, "Name: %s\n", DG1->name);
string_cat_printf(temp_str, "DocNr: %s\n", DG1->docnr);
string_cat_printf(temp_str, "Nationality: %s\n", DG1->nationality);
string_cat_printf(temp_str, "Birth Date: %s\n", DG1->birth_date);
string_cat_printf(temp_str, "Sex: %s\n", DG1->sex);
string_cat_printf(temp_str, "Expiry Date: %s\n", DG1->expiry_date);
/*
char iso_type = FURI_BIT(data->sak, 5) ? '4' : '3';
//TODO: NFC-B?
+117 -17
View File
@@ -109,6 +109,14 @@ bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1,
FURI_LOG_I(TAG, "'secure messaging data objects are incorrect'");
app->secure_messaging = false;
break;
case 0xff01:
//CUSTOM ERROR CODE from mrtd_helpers.c
FURI_LOG_I(TAG, "'invalid padding'");
break;
case 0xff02:
//CUSTOM ERROR CODE from mrtd_helpers.c
FURI_LOG_I(TAG, "'verify failed'");
break;
}
return false;
@@ -254,31 +262,118 @@ bool parse_ef_com(EF_COM_contents* EF_COM, const uint8_t* data, size_t length) {
uint16_t tags_tag_path[] = {0x60, 0x5c};
TlvInfo tlv_lds_version = iso7816_tlv_select(data, length, lds_tag_path, num_elements(lds_tag_path));
if(tlv_lds_version.tag) {
EF_COM->lds_version = tlv_number(tlv_lds_version);
} else {
if(!tlv_lds_version.tag) {
FURI_LOG_W(TAG, "EF.COM LDS version not found");
return false;
}
EF_COM->lds_version = tlv_number(tlv_lds_version);
TlvInfo tlv_unicode_version = iso7816_tlv_select(data, length, unicode_tag_path, num_elements(unicode_tag_path));
if(tlv_unicode_version.tag) {
EF_COM->unicode_version = tlv_number(tlv_unicode_version);
} else {
if(!tlv_unicode_version.tag) {
FURI_LOG_W(TAG, "EF.COM Unicode info not found!");
return false;
}
EF_COM->unicode_version = tlv_number(tlv_unicode_version);
TlvInfo tlv_tag_list = iso7816_tlv_select(data, length, tags_tag_path, num_elements(tags_tag_path));
if(tlv_tag_list.tag) {
for(size_t i=0; i<MAX_EFCOM_TAGS; ++i) {
EF_COM->tag_list[i] = (i < tlv_tag_list.length) ? tlv_tag_list.value[i] : 0x00;
}
} else {
if(!tlv_tag_list.tag) {
FURI_LOG_W(TAG, "EF.CO Tag List not found!");
return false;
}
for(size_t i=0; i<MAX_EFCOM_TAGS; ++i) {
EF_COM->tag_list[i] = (i < tlv_tag_list.length) ? tlv_tag_list.value[i] : 0x00;
}
return true;
}
void mrzcpy(uint8_t* dest, const uint8_t* src, size_t* idx, size_t n) {
//FURI_LOG_D(TAG, "mrzcpy %d: %.*s", n, n, src + *idx);
//memcpy(dest, src + *idx, n);
for(size_t i=0; i<n; ++i) {
uint8_t c = src[i + *idx];
if(c == '<') {
c = ' ';
}
dest[i] = c;
}
dest[n] = 0x00;
*idx += n;
}
bool parse_ef_dg1(EF_DG1_contents* DG1, const uint8_t* data, size_t length) {
TlvInfo tlv_mrz = iso7816_tlv_select(data, length, (uint16_t[]){0x61, 0x5f1f}, 2);
if(!tlv_mrz.tag) {
FURI_LOG_W(TAG, "DG1, unexpected content. Could not find tag 0x61, 0x5f1f");
return false;
}
const uint8_t* mrz = tlv_mrz.value;
size_t idx = 0;
switch(tlv_mrz.length) {
case 90:
DG1->type = MrtdTypeTD1;
mrzcpy(DG1->doctype, mrz, &idx, 2);
mrzcpy(DG1->issuing_state, mrz, &idx, 3);
mrzcpy(DG1->docnr, mrz, &idx, 9);
idx += 1; // docnr check digit
idx += 15; // optional data
mrzcpy(DG1->birth_date, mrz, &idx, 6);
idx += 1; // birth date check digit
mrzcpy(DG1->sex, mrz, &idx, 1);
mrzcpy(DG1->expiry_date, mrz, &idx, 6);
idx += 1; // expiry date check digit
mrzcpy(DG1->nationality, mrz, &idx, 3);
idx += 11; // optional data
idx += 1; // check digit
mrzcpy(DG1->name, mrz, &idx, 30);
// 30 + 30 + 30
break;
case 72:
DG1->type = MrtdTypeTD2;
mrzcpy(DG1->doctype, mrz, &idx, 2);
mrzcpy(DG1->issuing_state, mrz, &idx, 3);
mrzcpy(DG1->name, mrz, &idx, 31);
mrzcpy(DG1->docnr, mrz, &idx, 9);
idx += 1; // docnr check digit
mrzcpy(DG1->nationality, mrz, &idx, 3);
mrzcpy(DG1->birth_date, mrz, &idx, 6);
idx += 1; // birth date check digit
mrzcpy(DG1->sex, mrz, &idx, 1);
mrzcpy(DG1->expiry_date, mrz, &idx, 6);
idx += 1; // expiry date check digit
idx += 7; // optional data
idx += 1; // check digit
// 36 + 36
break;
case 88:
DG1->type = MrtdTypeTD3;
mrzcpy(DG1->doctype, mrz, &idx, 2);
mrzcpy(DG1->issuing_state, mrz, &idx, 3);
mrzcpy(DG1->name, mrz, &idx, 39);
mrzcpy(DG1->docnr, mrz, &idx, 9);
idx += 1; // docnr check digit
mrzcpy(DG1->nationality, mrz, &idx, 3);
mrzcpy(DG1->birth_date, mrz, &idx, 6);
idx += 1; // birth date check digit
mrzcpy(DG1->sex, mrz, &idx, 1);
mrzcpy(DG1->expiry_date, mrz, &idx, 6);
idx += 1; // expiry date check digit
idx += 14; // optional data
idx += 1; // check digit
idx += 1; // check digit
// 44 + 44
break;
default:
FURI_LOG_W(TAG, "Unexpected MRZ length in DG1: %d. TD1=90, TD2=72, TD3=88.", tlv_mrz.length);
return false;
}
return true;
}
@@ -312,6 +407,8 @@ bool mrtd_read_parse_file(MrtdApplication* app, MrtdData* mrtd_data, EFFile file
} else if(file.file_id == EF.DIR.file_id) {
result = parse_ef_dir(&mrtd_data->files.EF_DIR, buffer, buf_len);
FURI_LOG_D(TAG, "Parsed EF.DIR");
} else if(file.file_id == EF.DG1.file_id) {
result = parse_ef_dg1(&mrtd_data->files.DG1, buffer, buf_len);
} else {
FURI_LOG_W(TAG, "Don't know how to parse file with id 0x%04X", file.file_id);
}
@@ -322,11 +419,11 @@ bool mrtd_read_parse_file(MrtdApplication* app, MrtdData* mrtd_data, EFFile file
//TODO: remove testing function
void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data) {
FURI_LOG_D(TAG, "Mrtd Test");
mrtd_read_dump(app, EF.ATR);
mrtd_read_dump(app, EF.COM);
mrtd_read_dump(app, EF.DIR);
mrtd_read_dump(app, EF.CardAccess);
mrtd_read_dump(app, EF.CardSecurity);
//mrtd_read_dump(app, EF.ATR);
//mrtd_read_dump(app, EF.COM);
//mrtd_read_dump(app, EF.DIR);
//mrtd_read_dump(app, EF.CardAccess);
//mrtd_read_dump(app, EF.CardSecurity);
mrtd_select_app(app, AID.eMRTDApplication);
@@ -354,8 +451,11 @@ void mrtd_test(MrtdApplication* app, MrtdData* mrtd_data) {
mrtd_read_parse_file(app, mrtd_data, EF.COM);
//mrtd_read_parse_file(app, mrtd_data, EF.DIR);
mrtd_read_dump(app, EF.DG1);
mrtd_read_parse_file(app, mrtd_data, EF.DG1);
mrtd_read_dump(app, EF.DG2);
mrtd_read_dump(app, EF.DG14);
mrtd_read_dump(app, EF.DG15);
}
MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx) {
+1
View File
@@ -21,6 +21,7 @@ typedef struct {
struct {
EF_DIR_contents EF_DIR;
EF_COM_contents EF_COM;
EF_DG1_contents DG1;
} files;
} MrtdData;
+20
View File
@@ -25,6 +25,13 @@ typedef enum {
MrtdAuthMethodPace,
} MrtdAuthMethod;
typedef enum {
MrtdTypeUnknown,
MrtdTypeTD1,
MrtdTypeTD2,
MrtdTypeTD3,
} MrtdType;
typedef struct {
MrtdAuthMethod method;
@@ -109,6 +116,19 @@ typedef struct {
uint8_t tag_list[MAX_EFCOM_TAGS];
} EF_COM_contents;
typedef struct {
MrtdType type;
// ICAO9303 max sizes + 1 for 0-byte
uint8_t doctype[3];
uint8_t issuing_state[4];
uint8_t name[40];
uint8_t docnr[10];
uint8_t nationality[4];
uint8_t birth_date[7];
uint8_t sex[2];
uint8_t expiry_date[7];
} EF_DG1_contents;
uint8_t mrtd_bac_check_digit(const char* input, const uint8_t length);
//TODO: swap order, all other functions have output last