mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-06-15 20:01:54 -07:00
MRTD select and read file basics
This commit is contained in:
@@ -216,16 +216,19 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte
|
||||
|
||||
static bool nfc_worker_read_mrtd(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
|
||||
bool read_success = false;
|
||||
MrtdApplication mrtd_app = {};
|
||||
MrtdApplication* mrtd_app = mrtd_alloc_init(tx_rx);
|
||||
//EmvData* result = &nfc_worker->dev_data->emv_data;
|
||||
|
||||
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false);
|
||||
do {
|
||||
// Read passport
|
||||
if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break;
|
||||
mrtd_select_efcardaccess(tx_rx, &mrtd_app);
|
||||
mrtd_select_efdir(tx_rx, &mrtd_app);
|
||||
if(!mrtd_select_lds1(tx_rx, &mrtd_app)) break;
|
||||
//mrtd_select(mrtd_app, EF.CardAccess);
|
||||
//mrtd_select(mrtd_app, EF.DIR);
|
||||
//mrtd_select_efcardaccess(mrtd_app);
|
||||
//mrtd_select_efdir(mrtd_app);
|
||||
mrtd_test(mrtd_app);
|
||||
if(!mrtd_select_lds1(mrtd_app)) break;
|
||||
|
||||
/*
|
||||
// Copy data
|
||||
|
||||
+177
-26
@@ -11,9 +11,9 @@
|
||||
//- PACE (CONDITIONAL)
|
||||
//- BAC (CONDITIONAL)
|
||||
|
||||
static void mrtd_trace(FuriHalNfcTxRxContext* tx_rx, const char* message) {
|
||||
static void mrtd_trace(MrtdApplication* app) {
|
||||
FuriHalNfcTxRxContext* tx_rx = app->tx_rx;
|
||||
if(furi_log_get_level() == FuriLogLevelTrace) {
|
||||
FURI_LOG_T(TAG, "%s", message);
|
||||
printf("TX: ");
|
||||
for(size_t i = 0; i < tx_rx->tx_bits / 8; i++) {
|
||||
printf("%02X ", tx_rx->tx_data[i]);
|
||||
@@ -27,20 +27,173 @@ static void mrtd_trace(FuriHalNfcTxRxContext* tx_rx, const char* message) {
|
||||
}
|
||||
|
||||
uint16_t mrtd_decode_response(uint8_t* buffer, size_t len) {
|
||||
if(len != 2) {
|
||||
FURI_LOG_E(TAG, "Expecting 2 byte responses only");
|
||||
return 0xffff;
|
||||
}
|
||||
|
||||
return (buffer[0] << 8) | buffer[1];
|
||||
// Last two bytes are return code
|
||||
return (buffer[len-2] << 8) | buffer[len-1];
|
||||
}
|
||||
|
||||
bool mrtd_select_efcardaccess(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app) {
|
||||
UNUSED(mrtd_app);
|
||||
struct EFFormat EF = {
|
||||
.ATR = {.file_id = 0x2F01, .short_id = 0x01 },
|
||||
.DIR = {.file_id = 0x2F00, .short_id = 0x1E },
|
||||
.CardAccess = {.file_id = 0x011C, .short_id = 0x1C },
|
||||
.CardSecurity = {.file_id = 0x011D, .short_id = 0x1D },
|
||||
.COM = {.file_id = 0x011E, .short_id = 0x1E },
|
||||
.SOD = {.file_id = 0X011D, .short_id = 0X1D },
|
||||
.DG1 = {.file_id = 0X0101, .short_id = 0X01 },
|
||||
.DG2 = {.file_id = 0X0102, .short_id = 0X02 },
|
||||
.DG3 = {.file_id = 0X0103, .short_id = 0X03 },
|
||||
.DG4 = {.file_id = 0X0104, .short_id = 0X04 },
|
||||
.DG5 = {.file_id = 0X0105, .short_id = 0X05 },
|
||||
.DG6 = {.file_id = 0X0106, .short_id = 0X06 },
|
||||
.DG7 = {.file_id = 0X0107, .short_id = 0X07 },
|
||||
.DG8 = {.file_id = 0X0108, .short_id = 0X08 },
|
||||
.DG9 = {.file_id = 0X0109, .short_id = 0X09 },
|
||||
.DG10 = {.file_id = 0X010A, .short_id = 0X0A },
|
||||
.DG11 = {.file_id = 0X010B, .short_id = 0X0B },
|
||||
.DG12 = {.file_id = 0X010C, .short_id = 0X0C },
|
||||
.DG13 = {.file_id = 0X010D, .short_id = 0X0D },
|
||||
.DG14 = {.file_id = 0X010E, .short_id = 0X0E },
|
||||
.DG15 = {.file_id = 0X010F, .short_id = 0X0F },
|
||||
.DG16 = {.file_id = 0X0110, .short_id = 0X10 },
|
||||
};
|
||||
|
||||
/*bool mrtd_send_apdu(MrtdApplication* app, uint8_t* buffer, size_t length) {
|
||||
FuriHalNfcTxRxContext* tx_rx = app->tx_rx;
|
||||
|
||||
memcpy(tx_rx->tx_data, buffer, length);
|
||||
tx_rx->tx_bits = length * 8;
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
|
||||
|
||||
//TODO: timeout as param?
|
||||
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
|
||||
mrtd_trace(app);
|
||||
uint16_t ret_code = mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8);
|
||||
if(ret_code == 0x9000) {
|
||||
return true;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "APDU answer is not 0x9000, but 0x%04X", ret_code);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}*/
|
||||
|
||||
bool mrtd_send_apdu(MrtdApplication* app, uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, uint8_t lc, const void* data, int16_t le) {
|
||||
FuriHalNfcTxRxContext* tx_rx = app->tx_rx;
|
||||
|
||||
FURI_LOG_D(TAG, "Send APDU, lc: %d, le: %d", lc, le);
|
||||
|
||||
size_t idx = 0;
|
||||
tx_rx->tx_data[idx++] = cla;
|
||||
tx_rx->tx_data[idx++] = ins;
|
||||
tx_rx->tx_data[idx++] = p1;
|
||||
tx_rx->tx_data[idx++] = p2;
|
||||
if(lc > 0) {
|
||||
tx_rx->tx_data[idx++] = lc;
|
||||
memcpy(tx_rx->tx_data + idx, data, lc);
|
||||
idx += lc;
|
||||
}
|
||||
if(le >= 0) {
|
||||
tx_rx->tx_data[idx++] = le&0xff;
|
||||
}
|
||||
|
||||
tx_rx->tx_bits = idx * 8;
|
||||
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault;
|
||||
|
||||
//TODO: timeout as param?
|
||||
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
|
||||
mrtd_trace(app);
|
||||
uint16_t ret_code = mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8);
|
||||
//TODO: handle other return codes?
|
||||
if(ret_code == 0x9000) {
|
||||
return true;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "APDU answer is not 0x9000, but 0x%04X", ret_code);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mrtd_select(MrtdApplication* app, EFFile file) {
|
||||
uint8_t data[] = {file.file_id >> 8, file.file_id & 0xff};
|
||||
FURI_LOG_D(TAG, "Send select EF: 0x%04X", file.file_id);
|
||||
if(!mrtd_send_apdu(app, 0x00, 0xA4, 0x02, 0x0C, 0x02, data, -1)) {
|
||||
FURI_LOG_E(TAG, "Failed select EF 0x%04X", file.file_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t mrtd_read_binary(MrtdApplication* app, uint8_t* buffer, size_t bufsize, size_t offset) {
|
||||
UNUSED(buffer);
|
||||
UNUSED(bufsize);
|
||||
// 00 B0 offst -
|
||||
FURI_LOG_D(TAG, "Read binary, offset: %d", offset);
|
||||
if(!mrtd_send_apdu(app, 0x00, 0xB0, offset>>8, offset&0xff, 0x00, NULL, 0)) {
|
||||
FURI_LOG_E(TAG, "Failed to read");
|
||||
return 0;
|
||||
}
|
||||
|
||||
//TODO: copy data to buffer
|
||||
//TODO: return read amount
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mrtd_read_dump(MrtdApplication* app, EFFile file, const char* descr) {
|
||||
FURI_LOG_D(TAG, "Read and dump %s:", descr);
|
||||
if(!mrtd_select(app, file)) {
|
||||
return;
|
||||
}
|
||||
uint8_t data[2048];
|
||||
size_t read = 0;
|
||||
do {
|
||||
read = mrtd_read_binary(app, data+read, sizeof(data)-read, read);
|
||||
} while(read > 0);
|
||||
}
|
||||
|
||||
void mrtd_test(MrtdApplication* app) {
|
||||
FURI_LOG_D(TAG, "Mrtd Test");
|
||||
mrtd_read_dump(app, EF.ATR, "EF.ATR");
|
||||
mrtd_read_dump(app, EF.DIR, "EF.DIR");
|
||||
mrtd_read_dump(app, EF.CardAccess, "EF.CardAccess");
|
||||
mrtd_read_dump(app, EF.CardSecurity, "EF.CardSecurity");
|
||||
}
|
||||
|
||||
MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx) {
|
||||
MrtdApplication* app = malloc(sizeof(MrtdApplication));
|
||||
|
||||
app->tx_rx = tx_rx;
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
void mrtd_free(MrtdApplication* app) {
|
||||
furi_assert(app);
|
||||
free(app);
|
||||
}
|
||||
|
||||
/*
|
||||
size_t mrtd_read_data(MrtdApplication* app, uint8_t* buffer, size_t bufsize) {
|
||||
//TODO: fill buffer with data from file
|
||||
|
||||
//TODO: use protected APDU if authenticated
|
||||
|
||||
|
||||
|
||||
return read;
|
||||
}
|
||||
*/
|
||||
|
||||
bool mrtd_select_efcardaccess(MrtdApplication* app) {
|
||||
FuriHalNfcTxRxContext* tx_rx = app->tx_rx;
|
||||
|
||||
// Chris: short: 0x6700, long: yes (0x9000)
|
||||
// Anna: short: 0x6700, long: yes (0x9000)
|
||||
|
||||
EFFile file = EF.CardAccess;
|
||||
|
||||
// ICAO 9303 p10 §3.6.2
|
||||
uint8_t select_efcardaccess_cmd[] = {
|
||||
0x00, // CLA
|
||||
@@ -48,7 +201,8 @@ bool mrtd_select_efcardaccess(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrt
|
||||
0x02, // P1
|
||||
0x0C, // P2
|
||||
0x02, // Lc: Data length
|
||||
0x01, 0x1C, // Data: EF.CardAccess File ID
|
||||
file.file_id >> 8 | (file.file_id & 0xFF),
|
||||
//0x01, 0x1C, // Data: EF.CardAccess File ID
|
||||
0x00, // Le
|
||||
};
|
||||
|
||||
@@ -60,7 +214,7 @@ bool mrtd_select_efcardaccess(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrt
|
||||
|
||||
FURI_LOG_D(TAG, "Send select EF.CardAccess");
|
||||
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
|
||||
mrtd_trace(tx_rx, "Select EF.CardAccess:");
|
||||
mrtd_trace(app);
|
||||
if(mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8) == 0x9000) {
|
||||
efcardaccess_success = true;
|
||||
} else {
|
||||
@@ -72,8 +226,8 @@ bool mrtd_select_efcardaccess(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrt
|
||||
return efcardaccess_success;
|
||||
}
|
||||
|
||||
bool mrtd_select_efdir(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app) {
|
||||
UNUSED(mrtd_app);
|
||||
bool mrtd_select_efdir(MrtdApplication* app) {
|
||||
FuriHalNfcTxRxContext* tx_rx = app->tx_rx;
|
||||
|
||||
// Chris: short: no, long: no (0x6A82)
|
||||
// Anna: short: no, long: yes (0x9000)
|
||||
@@ -96,7 +250,7 @@ bool mrtd_select_efdir(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app)
|
||||
|
||||
FURI_LOG_D(TAG, "Send select EF.DIR");
|
||||
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
|
||||
mrtd_trace(tx_rx, "Select EF.DIR:");
|
||||
mrtd_trace(app);
|
||||
if(mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8) == 0x9000) {
|
||||
efdir_success = true;
|
||||
} else {
|
||||
@@ -108,8 +262,8 @@ bool mrtd_select_efdir(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app)
|
||||
return efdir_success;
|
||||
}
|
||||
|
||||
bool mrtd_select_lds1(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app) {
|
||||
UNUSED(mrtd_app);
|
||||
bool mrtd_select_lds1(MrtdApplication* app) {
|
||||
FuriHalNfcTxRxContext* tx_rx = app->tx_rx;
|
||||
|
||||
uint8_t select_emrtd_cmd[] = {
|
||||
0x00, // CLA
|
||||
@@ -135,7 +289,7 @@ bool mrtd_select_lds1(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app) {
|
||||
|
||||
FURI_LOG_D(TAG, "Send select LDS1 eMRTD");
|
||||
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
|
||||
mrtd_trace(tx_rx, "Select LDS1:");
|
||||
mrtd_trace(app);
|
||||
if(mrtd_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8) == 0x9000) {
|
||||
lds1_success = true;
|
||||
} else {
|
||||
@@ -148,20 +302,17 @@ bool mrtd_select_lds1(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app) {
|
||||
return lds1_success;
|
||||
}
|
||||
|
||||
int mrtd_bac_keyhandshake(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app) {
|
||||
UNUSED(tx_rx);
|
||||
UNUSED(mrtd_app);
|
||||
int mrtd_bac_keyhandshake(MrtdApplication* app) {
|
||||
UNUSED(app);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool mrtd_read(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app) {
|
||||
furi_assert(tx_rx);
|
||||
furi_assert(mrtd_app);
|
||||
bool mrtd_read(MrtdApplication* app) {
|
||||
bool mrtd_read = false;
|
||||
|
||||
memset(mrtd_app, 0, sizeof(MrtdApplication));
|
||||
memset(app, 0, sizeof(MrtdApplication));
|
||||
|
||||
mrtd_read = mrtd_bac_keyhandshake(tx_rx, mrtd_app);
|
||||
mrtd_read = mrtd_bac_keyhandshake(app);
|
||||
return mrtd_read;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <furi_hal_nfc.h>
|
||||
|
||||
typedef struct {
|
||||
FuriHalNfcTxRxContext* tx_rx;
|
||||
uint16_t file_offset;
|
||||
uint8_t* kmrz;
|
||||
uint8_t ksenc[16];
|
||||
uint8_t ksmac[16];
|
||||
@@ -38,16 +40,53 @@ typedef struct {
|
||||
MrtdAuthData auth;
|
||||
} MrtdData;
|
||||
|
||||
typedef struct {
|
||||
const uint8_t short_id;
|
||||
const uint16_t file_id;
|
||||
} EFFile;
|
||||
|
||||
struct EFFormat {
|
||||
// Under Master File (MF)
|
||||
EFFile ATR;
|
||||
EFFile DIR;
|
||||
EFFile CardAccess;
|
||||
EFFile CardSecurity;
|
||||
|
||||
// Under LDS1 eMRTD Application
|
||||
EFFile COM;
|
||||
EFFile SOD;
|
||||
EFFile DG1;
|
||||
EFFile DG2;
|
||||
EFFile DG3;
|
||||
EFFile DG4;
|
||||
EFFile DG5;
|
||||
EFFile DG6;
|
||||
EFFile DG7;
|
||||
EFFile DG8;
|
||||
EFFile DG9;
|
||||
EFFile DG10;
|
||||
EFFile DG11;
|
||||
EFFile DG12;
|
||||
EFFile DG13;
|
||||
EFFile DG14;
|
||||
EFFile DG15;
|
||||
EFFile DG16;
|
||||
};
|
||||
|
||||
extern struct EFFormat EF;
|
||||
|
||||
//TODO: description
|
||||
bool mrtd_select_efcardaccess(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app);
|
||||
bool mrtd_select_efdir(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app);
|
||||
MrtdApplication* mrtd_alloc_init(FuriHalNfcTxRxContext* tx_rx);
|
||||
bool mrtd_select(MrtdApplication* app, EFFile file);
|
||||
bool mrtd_select_efcardaccess(MrtdApplication* mrtd_app);
|
||||
bool mrtd_select_efdir(MrtdApplication* mrtd_app);
|
||||
void mrtd_test(MrtdApplication* app);
|
||||
|
||||
/** Select the LDS1 eMRTD application
|
||||
* @note Can be used to detect presence of Passport/ID-card
|
||||
*
|
||||
* @param tx_rx FuriHalNfcTxRxContext instance
|
||||
* @param emv_app MrtdApplication instance
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool mrtd_select_lds1(FuriHalNfcTxRxContext* tx_rx, MrtdApplication* mrtd_app);
|
||||
bool mrtd_select_lds1(MrtdApplication* mrtd_app);
|
||||
|
||||
Reference in New Issue
Block a user