From 5741ed2bd5ef9b8c9cdb07459df034625c624d33 Mon Sep 17 00:00:00 2001 From: gornekich Date: Wed, 4 Aug 2021 21:58:11 +0300 Subject: [PATCH] [FL-1595] Add EMV tags (#625) * nfc: add expiration date tag to emv parser * nfc: add expiration date save and display * nfc: add long apdu test command --- applications/nfc/nfc_device.c | 12 ++++ applications/nfc/nfc_device.h | 2 +- applications/nfc/nfc_worker.c | 68 +++++++++++++++++-- applications/nfc/nfc_worker.h | 2 +- applications/nfc/nfc_worker_i.h | 2 +- .../scenes/nfc_scene_emulate_apdu_sequence.c | 2 +- .../scenes/nfc_scene_read_emv_data_success.c | 6 ++ applications/nfc/views/bank_card.c | 4 +- applications/nfc/views/bank_card.h | 2 +- lib/nfc_protocols/emv_decoder.c | 9 ++- lib/nfc_protocols/emv_decoder.h | 5 ++ 11 files changed, 100 insertions(+), 14 deletions(-) diff --git a/applications/nfc/nfc_device.c b/applications/nfc/nfc_device.c index d0575e1b..bfe0e877 100755 --- a/applications/nfc/nfc_device.c +++ b/applications/nfc/nfc_device.c @@ -203,6 +203,10 @@ uint16_t nfc_device_prepare_bank_card_string(NfcDevice* dev, string_t bank_card_ for(uint8_t i = 0; i < sizeof(data->number); i++) { string_cat_printf(bank_card_string, " %02X", data->number[i]); } + if(data->exp_mon) { + string_cat_printf( + bank_card_string, "\nExp date: %02X/%02X", data->exp_mon, data->exp_year); + } return string_size(bank_card_string); } @@ -236,6 +240,14 @@ bool nfc_device_parse_bank_card_string(NfcDevice* dev, string_t bank_card_string break; } parsed = true; + // Check expiration date presence + ws = string_search_str(bank_card_string, "Exp date: "); + if(ws != STRING_FAILURE) { + // strlen("Exp date: ") = 10 + string_right(bank_card_string, 10); + nfc_device_read_hex(bank_card_string, &data->exp_mon, 1); + nfc_device_read_hex(bank_card_string, &data->exp_year, 1); + } } while(0); return parsed; diff --git a/applications/nfc/nfc_device.h b/applications/nfc/nfc_device.h index ab644615..c2de3120 100644 --- a/applications/nfc/nfc_device.h +++ b/applications/nfc/nfc_device.h @@ -42,7 +42,7 @@ typedef struct { uint16_t aid_len; uint8_t number[8]; uint8_t exp_mon; - uint16_t exp_year; + uint8_t exp_year; char cardholder[32]; } NfcEmvData; diff --git a/applications/nfc/nfc_worker.c b/applications/nfc/nfc_worker.c index c5c9ed76..30aecbfd 100755 --- a/applications/nfc/nfc_worker.c +++ b/applications/nfc/nfc_worker.c @@ -81,8 +81,8 @@ void nfc_worker_task(void* context) { nfc_worker_read_emv_app(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateReadEMV) { nfc_worker_read_emv(nfc_worker); - } else if(nfc_worker->state == NfcWorkerStateEmulateEMV) { - nfc_worker_emulate_emv(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateEmulateApdu) { + nfc_worker_emulate_apdu(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateReadMifareUl) { nfc_worker_read_mifare_ul(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateEmulateMifareUl) { @@ -324,6 +324,10 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) { result->emv_data.number, emv_app.card_number, sizeof(emv_app.card_number)); + if(emv_app.exp_month) { + result->emv_data.exp_mon = emv_app.exp_month; + result->emv_data.exp_year = emv_app.exp_year; + } // Notify caller and exit if(nfc_worker->callback) { nfc_worker->callback(nfc_worker->context); @@ -348,7 +352,7 @@ void nfc_worker_read_emv(NfcWorker* nfc_worker) { } } -void nfc_worker_emulate_emv(NfcWorker* nfc_worker) { +void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { ReturnCode err; uint8_t tx_buff[255] = {}; uint16_t tx_len = 0; @@ -362,8 +366,46 @@ void nfc_worker_emulate_emv(NfcWorker* nfc_worker) { .device = NfcDeviceNfca, .protocol = NfcDeviceProtocolEMV, }; + // Test RX data + const uint8_t debug_rx[] = { + 0xba, 0x0b, 0xba, 0xba, 0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, + 0xca, 0xfe, 0xfa, 0xce, 0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba, + 0x0b, 0xba, 0xba, 0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca, + 0xfe, 0xfa, 0xce, 0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, + 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba, 0x0b, + 0xba, 0xba, 0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca, 0xfe, + 0xfa, 0xce, 0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, + 0xbb, 0xcc, 0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba, 0x0b, 0xba, + 0xba, 0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca, 0xfe, 0xfa, + 0xce, 0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, + 0xcc, 0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba, 0x0b, 0xba, 0xba, + 0x20, 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca, 0xfe, 0xfa, 0xce, + 0x14, 0x88, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, + 0xdd, 0xee, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xba, 0x0b, 0xba, 0xba, 0x20, + 0x00, 0x02, 0x28, 0xde, 0xad, 0xbe, 0xef, 0x00, 0xca, 0xca, 0xca, 0xfe, 0xfa, 0xce, 0x14, + 0x88, 0x00}; + // Test TX data + const uint8_t debug_tx[] = { + 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, + 0x10, 0x14, 0x88, 0x02, 0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad, + 0xbe, 0xef, 0xce, 0xee, 0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12, + 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, + 0x14, 0x88, 0x02, 0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad, 0xbe, + 0xef, 0xce, 0xee, 0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12, 0x34, + 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x14, + 0x88, 0x02, 0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad, 0xbe, 0xef, + 0xce, 0xee, 0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12, 0x34, 0x56, + 0x78, 0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x14, 0x88, + 0x02, 0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad, 0xbe, 0xef, 0xce, + 0xee, 0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12, 0x34, 0x56, 0x78, + 0x9a, 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x14, 0x88, 0x02, + 0x28, 0x00, 0x00, 0xca, 0xca, 0x00, 0xc0, 0xc0, 0x00, 0xde, 0xad, 0xbe, 0xef, 0xce, 0xee, + 0xec, 0xca, 0xfe, 0xba, 0xba, 0xb0, 0xb0, 0xac, 0xdc, 0x11, 0x12, 0x34, 0x56, 0x78, 0x9a, + 0xbc, 0xde, 0xff, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x14, 0x88, 0x02, 0x28, + 0x00, 0x00}; - while(nfc_worker->state == NfcWorkerStateEmulateEMV) { + while(nfc_worker->state == NfcWorkerStateEmulateApdu) { if(api_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, 300)) { FURI_LOG_I(NFC_WORKER_TAG, "POS terminal detected"); // Read data from POS terminal @@ -401,7 +443,23 @@ void nfc_worker_emulate_emv(NfcWorker* nfc_worker) { tx_len = emv_get_proc_opt_ans(tx_buff); err = api_hal_nfc_data_exchange(tx_buff, tx_len, &rx_buff, &rx_len, false); if(err == ERR_NONE) { - FURI_LOG_I(NFC_WORKER_TAG, "Received PDOL"); + FURI_LOG_I(NFC_WORKER_TAG, "Transive PDOL ANS"); + } else { + FURI_LOG_E(NFC_WORKER_TAG, "Error in 4rd data exchange: Transive PDOL ANS"); + api_hal_nfc_deactivate(); + continue; + } + + if(*rx_len != sizeof(debug_rx) || memcmp(rx_buff, debug_rx, sizeof(debug_rx))) { + FURI_LOG_E(NFC_WORKER_TAG, "Failed long message test"); + } else { + FURI_LOG_I(NFC_WORKER_TAG, "Correct debug message received"); + tx_len = sizeof(debug_tx); + err = api_hal_nfc_data_exchange( + (uint8_t*)debug_tx, tx_len, &rx_buff, &rx_len, false); + if(err == ERR_NONE) { + FURI_LOG_I(NFC_WORKER_TAG, "Transive Debug message"); + } } api_hal_nfc_deactivate(); } else { diff --git a/applications/nfc/nfc_worker.h b/applications/nfc/nfc_worker.h index 4719cb21..519ec7a2 100755 --- a/applications/nfc/nfc_worker.h +++ b/applications/nfc/nfc_worker.h @@ -14,7 +14,7 @@ typedef enum { NfcWorkerStateEmulate, NfcWorkerStateReadEMVApp, NfcWorkerStateReadEMV, - NfcWorkerStateEmulateEMV, + NfcWorkerStateEmulateApdu, NfcWorkerStateField, NfcWorkerStateReadMifareUl, NfcWorkerStateEmulateMifareUl, diff --git a/applications/nfc/nfc_worker_i.h b/applications/nfc/nfc_worker_i.h index 9df5707d..79c64225 100755 --- a/applications/nfc/nfc_worker_i.h +++ b/applications/nfc/nfc_worker_i.h @@ -36,7 +36,7 @@ void nfc_worker_read_emv_app(NfcWorker* nfc_worker); void nfc_worker_read_emv(NfcWorker* nfc_worker); -void nfc_worker_emulate_emv(NfcWorker* nfc_worker); +void nfc_worker_emulate_apdu(NfcWorker* nfc_worker); void nfc_worker_detect(NfcWorker* nfc_worker); diff --git a/applications/nfc/scenes/nfc_scene_emulate_apdu_sequence.c b/applications/nfc/scenes/nfc_scene_emulate_apdu_sequence.c index 2f5018c8..70288054 100644 --- a/applications/nfc/scenes/nfc_scene_emulate_apdu_sequence.c +++ b/applications/nfc/scenes/nfc_scene_emulate_apdu_sequence.c @@ -11,7 +11,7 @@ const void nfc_scene_emulate_apdu_sequence_on_enter(void* context) { // Setup and start worker view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); - nfc_worker_start(nfc->worker, NfcWorkerStateEmulateEMV, &nfc->dev.dev_data, NULL, nfc); + nfc_worker_start(nfc->worker, NfcWorkerStateEmulateApdu, &nfc->dev.dev_data, NULL, nfc); } const bool nfc_scene_emulate_apdu_sequence_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/nfc/scenes/nfc_scene_read_emv_data_success.c b/applications/nfc/scenes/nfc_scene_read_emv_data_success.c index 74f15a22..c6a9280c 100755 --- a/applications/nfc/scenes/nfc_scene_read_emv_data_success.c +++ b/applications/nfc/scenes/nfc_scene_read_emv_data_success.c @@ -59,6 +59,12 @@ void nfc_scene_read_emv_data_success_on_enter(void* context) { char sak_str[16]; snprintf(sak_str, sizeof(sak_str), "SAK: %02X", nfc_data->sak); widget_add_string_element(nfc->widget, 121, 42, AlignRight, AlignTop, FontSecondary, sak_str); + if(emv_data->exp_mon) { + char exp_str[16]; + snprintf( + exp_str, sizeof(exp_str), "Exp: %02X/%02X", emv_data->exp_mon, emv_data->exp_year); + widget_add_string_element(nfc->widget, 7, 32, AlignLeft, AlignTop, FontSecondary, exp_str); + } view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); } diff --git a/applications/nfc/views/bank_card.c b/applications/nfc/views/bank_card.c index a483fcff..f4354792 100755 --- a/applications/nfc/views/bank_card.c +++ b/applications/nfc/views/bank_card.c @@ -49,10 +49,10 @@ void bank_card_set_number(BankCard* bank_card, uint8_t* number) { string_clear(num_str); } -void bank_card_set_exp_date(BankCard* bank_card, uint8_t mon, uint16_t year) { +void bank_card_set_exp_date(BankCard* bank_card, uint8_t mon, uint8_t year) { furi_assert(bank_card); char exp_date_str[16]; - snprintf(exp_date_str, sizeof(exp_date_str), "Exp: %02d/%02d", mon, year % 100); + snprintf(exp_date_str, sizeof(exp_date_str), "Exp: %02X/%02X", mon, year); widget_add_string_element( bank_card->widget, 122, 54, AlignRight, AlignBottom, FontSecondary, exp_date_str); } diff --git a/applications/nfc/views/bank_card.h b/applications/nfc/views/bank_card.h index 40472c87..f7252a80 100644 --- a/applications/nfc/views/bank_card.h +++ b/applications/nfc/views/bank_card.h @@ -18,6 +18,6 @@ void bank_card_set_name(BankCard* bank_card, char* name); void bank_card_set_number(BankCard* bank_card, uint8_t* number); -void bank_card_set_exp_date(BankCard* bank_card, uint8_t mon, uint16_t year); +void bank_card_set_exp_date(BankCard* bank_card, uint8_t mon, uint8_t year); void bank_card_set_cardholder_name(BankCard* bank_card, char* name); diff --git a/lib/nfc_protocols/emv_decoder.c b/lib/nfc_protocols/emv_decoder.c index de3dae0d..cab0f91a 100755 --- a/lib/nfc_protocols/emv_decoder.c +++ b/lib/nfc_protocols/emv_decoder.c @@ -214,13 +214,18 @@ uint16_t emv_prepare_read_sfi_record(uint8_t* dest, uint8_t sfi, uint8_t record_ } bool emv_decode_read_sfi_record(uint8_t* buff, uint16_t len, EmvApplication* app) { + bool pan_parsed = false; for(uint16_t i = 0; i < len; i++) { if(buff[i] == EMV_TAG_PAN) { memcpy(app->card_number, &buff[i + 2], 8); - return true; + pan_parsed = true; + } else if((buff[i] << 8 | buff[i + 1]) == EMV_TAG_EXP_DATE) { + i += 3; + app->exp_year = buff[i++]; + app->exp_month = buff[i++]; } } - return false; + return pan_parsed; } uint16_t emv_select_ppse_ans(uint8_t* buff) { diff --git a/lib/nfc_protocols/emv_decoder.h b/lib/nfc_protocols/emv_decoder.h index e19a543c..b01f5325 100755 --- a/lib/nfc_protocols/emv_decoder.h +++ b/lib/nfc_protocols/emv_decoder.h @@ -16,6 +16,8 @@ #define EMV_TAG_CARD_NUM 0x57 #define EMV_TAG_PAN 0x5A #define EMV_TAG_AFL 0x94 +#define EMV_TAG_EXP_DATE 0x5F24 +#define EMV_TAG_CARDHOLDER_NAME 0x5F20 typedef struct { uint16_t tag; @@ -35,6 +37,9 @@ typedef struct { uint8_t aid_len; char name[32]; uint8_t card_number[8]; + uint8_t exp_month; + uint8_t exp_year; + char crdholder_name[32]; APDU pdol; APDU afl; } EmvApplication;