mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-06-13 19:43:34 -07:00
MRTD Read DG1 + small changes
This commit is contained in:
@@ -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
@@ -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) {
|
||||
|
||||
@@ -21,6 +21,7 @@ typedef struct {
|
||||
struct {
|
||||
EF_DIR_contents EF_DIR;
|
||||
EF_COM_contents EF_COM;
|
||||
EF_DG1_contents DG1;
|
||||
} files;
|
||||
} MrtdData;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user