mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
Merge branch 'dev' of https://github.com/DarkFlippers/unleashed-firmware into dev
This commit is contained in:
@@ -173,6 +173,15 @@ App(
|
||||
sources=["plugins/supported_cards/washcity.c"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="emv_parser",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="emv_plugin_ep",
|
||||
targets=["f7"],
|
||||
requires=["nfc", "storage"],
|
||||
sources=["plugins/supported_cards/emv.c", "helpers/nfc_emv_parser.c"],
|
||||
)
|
||||
|
||||
App(
|
||||
appid="ndef_parser",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
|
||||
@@ -34,7 +34,7 @@ static bool nfc_emv_parser_search_data(
|
||||
|
||||
bool nfc_emv_parser_get_aid_name(
|
||||
Storage* storage,
|
||||
uint8_t* aid,
|
||||
const uint8_t* aid,
|
||||
uint8_t aid_len,
|
||||
FuriString* aid_name) {
|
||||
furi_assert(storage);
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
*/
|
||||
bool nfc_emv_parser_get_aid_name(
|
||||
Storage* storage,
|
||||
uint8_t* aid,
|
||||
const uint8_t* aid,
|
||||
uint8_t aid_len,
|
||||
FuriString* aid_name);
|
||||
|
||||
|
||||
115
applications/main/nfc/helpers/protocol_support/emv/emv.c
Normal file
115
applications/main/nfc/helpers/protocol_support/emv/emv.c
Normal file
@@ -0,0 +1,115 @@
|
||||
#include "emv.h"
|
||||
#include "emv_render.h"
|
||||
|
||||
#include <nfc/protocols/emv/emv_poller.h>
|
||||
|
||||
#include "nfc/nfc_app_i.h"
|
||||
|
||||
#include "../nfc_protocol_support_common.h"
|
||||
#include "../nfc_protocol_support_gui_common.h"
|
||||
#include "../iso14443_4a/iso14443_4a_i.h"
|
||||
|
||||
static void nfc_scene_info_on_enter_emv(NfcApp* instance) {
|
||||
const NfcDevice* device = instance->nfc_device;
|
||||
const EmvData* data = nfc_device_get_data(device, NfcProtocolEmv);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
// furi_string_cat_printf(
|
||||
// temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
nfc_render_emv_info(data, NfcProtocolFormatTypeFull, temp_str);
|
||||
|
||||
widget_add_text_scroll_element(
|
||||
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
|
||||
|
||||
furi_string_free(temp_str);
|
||||
}
|
||||
|
||||
static void nfc_scene_more_info_on_enter_emv(NfcApp* instance) {
|
||||
// Jump to advanced scene right away
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneEmvMoreInfo);
|
||||
}
|
||||
|
||||
static NfcCommand nfc_scene_read_poller_callback_emv(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.protocol == NfcProtocolEmv);
|
||||
|
||||
NfcApp* instance = context;
|
||||
const EmvPollerEvent* emv_event = event.event_data;
|
||||
|
||||
if(emv_event->type == EmvPollerEventTypeReadSuccess) {
|
||||
nfc_device_set_data(
|
||||
instance->nfc_device, NfcProtocolEmv, nfc_poller_get_data(instance->poller));
|
||||
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
|
||||
return NfcCommandStop;
|
||||
}
|
||||
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
static void nfc_scene_read_on_enter_emv(NfcApp* instance) {
|
||||
nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_emv, instance);
|
||||
}
|
||||
|
||||
static void nfc_scene_read_success_on_enter_emv(NfcApp* instance) {
|
||||
const NfcDevice* device = instance->nfc_device;
|
||||
const EmvData* data = nfc_device_get_data(device, NfcProtocolEmv);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
// furi_string_cat_printf(
|
||||
// temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
nfc_render_emv_info(data, NfcProtocolFormatTypeShort, temp_str);
|
||||
|
||||
widget_add_text_scroll_element(
|
||||
instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
|
||||
|
||||
furi_string_free(temp_str);
|
||||
}
|
||||
|
||||
// static void nfc_scene_emulate_on_enter_emv(NfcApp* instance) {
|
||||
// const Iso14443_4aData* iso14443_4a_data =
|
||||
// nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_4a);
|
||||
|
||||
// instance->listener =
|
||||
// nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, iso14443_4a_data);
|
||||
// nfc_listener_start(
|
||||
// instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance);
|
||||
// }
|
||||
|
||||
const NfcProtocolSupportBase nfc_protocol_support_emv = {
|
||||
.features = NfcProtocolFeatureMoreInfo,
|
||||
|
||||
.scene_info =
|
||||
{
|
||||
.on_enter = nfc_scene_info_on_enter_emv,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
},
|
||||
.scene_more_info =
|
||||
{
|
||||
.on_enter = nfc_scene_more_info_on_enter_emv,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
},
|
||||
.scene_read =
|
||||
{
|
||||
.on_enter = nfc_scene_read_on_enter_emv,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
},
|
||||
.scene_read_menu =
|
||||
{
|
||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
},
|
||||
.scene_read_success =
|
||||
{
|
||||
.on_enter = nfc_scene_read_success_on_enter_emv,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
},
|
||||
.scene_saved_menu =
|
||||
{
|
||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
},
|
||||
.scene_save_name =
|
||||
{
|
||||
.on_enter = nfc_protocol_support_common_on_enter_empty,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
},
|
||||
};
|
||||
5
applications/main/nfc/helpers/protocol_support/emv/emv.h
Normal file
5
applications/main/nfc/helpers/protocol_support/emv/emv.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "../nfc_protocol_support_base.h"
|
||||
|
||||
extern const NfcProtocolSupportBase nfc_protocol_support_emv;
|
||||
182
applications/main/nfc/helpers/protocol_support/emv/emv_render.c
Normal file
182
applications/main/nfc/helpers/protocol_support/emv/emv_render.c
Normal file
@@ -0,0 +1,182 @@
|
||||
#include "emv_render.h"
|
||||
|
||||
#include "../iso14443_4a/iso14443_4a_render.h"
|
||||
#include "nfc/nfc_app_i.h"
|
||||
|
||||
void nfc_render_emv_info(const EmvData* data, NfcProtocolFormatType format_type, FuriString* str) {
|
||||
nfc_render_emv_header(str);
|
||||
nfc_render_emv_uid(
|
||||
data->iso14443_4a_data->iso14443_3a_data->uid,
|
||||
data->iso14443_4a_data->iso14443_3a_data->uid_len,
|
||||
str);
|
||||
|
||||
if(format_type == NfcProtocolFormatTypeFull) nfc_render_emv_extra(data, str);
|
||||
}
|
||||
|
||||
void nfc_render_emv_header(FuriString* str) {
|
||||
furi_string_cat_printf(str, "\e#%s\n", "EMV");
|
||||
}
|
||||
|
||||
void nfc_render_emv_uid(const uint8_t* uid, const uint8_t uid_len, FuriString* str) {
|
||||
if(uid_len == 0) return;
|
||||
|
||||
furi_string_cat_printf(str, "UID: ");
|
||||
|
||||
for(uint8_t i = 0; i < uid_len; i++) {
|
||||
furi_string_cat_printf(str, "%02X ", uid[i]);
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "\n");
|
||||
}
|
||||
|
||||
void nfc_render_emv_aid(const uint8_t* uid, const uint8_t uid_len, FuriString* str) {
|
||||
if(uid_len == 0) return;
|
||||
|
||||
furi_string_cat_printf(str, "UID: ");
|
||||
|
||||
for(uint8_t i = 0; i < uid_len; i++) {
|
||||
furi_string_cat_printf(str, "%02X ", uid[i]);
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "\n");
|
||||
}
|
||||
|
||||
void nfc_render_emv_data(const EmvData* data, FuriString* str) {
|
||||
nfc_render_emv_pan(data->emv_application.pan, data->emv_application.pan_len, str);
|
||||
nfc_render_emv_name(data->emv_application.name, str);
|
||||
}
|
||||
|
||||
void nfc_render_emv_pan(const uint8_t* data, const uint8_t len, FuriString* str) {
|
||||
if(len == 0) return;
|
||||
|
||||
FuriString* card_number = furi_string_alloc();
|
||||
for(uint8_t i = 0; i < len; i++) {
|
||||
if((i % 2 == 0) && (i != 0)) furi_string_cat_printf(card_number, " ");
|
||||
furi_string_cat_printf(card_number, "%02X", data[i]);
|
||||
}
|
||||
|
||||
// Cut padding 'F' from card number
|
||||
furi_string_trim(card_number, "F");
|
||||
furi_string_cat(str, card_number);
|
||||
furi_string_free(card_number);
|
||||
|
||||
furi_string_cat_printf(str, "\n");
|
||||
}
|
||||
|
||||
void nfc_render_emv_expired(const EmvApplication* apl, FuriString* str) {
|
||||
if(apl->exp_month == 0) return;
|
||||
furi_string_cat_printf(str, "Exp: %02X/%02X\n", apl->exp_month, apl->exp_year);
|
||||
}
|
||||
|
||||
void nfc_render_emv_currency(uint16_t cur_code, FuriString* str) {
|
||||
if(!cur_code) return;
|
||||
|
||||
furi_string_cat_printf(str, "Currency code: %04X\n", cur_code);
|
||||
}
|
||||
|
||||
void nfc_render_emv_country(uint16_t country_code, FuriString* str) {
|
||||
if(!country_code) return;
|
||||
|
||||
furi_string_cat_printf(str, "Country code: %04X\n", country_code);
|
||||
}
|
||||
|
||||
void nfc_render_emv_application(const EmvApplication* apl, FuriString* str) {
|
||||
const uint8_t len = apl->aid_len;
|
||||
|
||||
if(!len) {
|
||||
furi_string_cat_printf(str, "No Pay Application found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "AID: ");
|
||||
|
||||
for(uint8_t i = 0; i < len; i++) furi_string_cat_printf(str, "%02X", apl->aid[i]);
|
||||
|
||||
furi_string_cat_printf(str, "\n");
|
||||
}
|
||||
|
||||
static void nfc_render_emv_pin_try_counter(uint8_t counter, FuriString* str) {
|
||||
if(counter == 0xff) return;
|
||||
furi_string_cat_printf(str, "PIN try left: %d\n", counter);
|
||||
}
|
||||
|
||||
void nfc_render_emv_transactions(const EmvApplication* apl, FuriString* str) {
|
||||
if(apl->transaction_counter)
|
||||
furi_string_cat_printf(str, "Transactions: %d\n", apl->transaction_counter);
|
||||
if(apl->last_online_atc)
|
||||
furi_string_cat_printf(str, "Last Online ATC: %d\n", apl->last_online_atc);
|
||||
|
||||
const uint8_t len = apl->active_tr;
|
||||
if(!len) {
|
||||
furi_string_cat_printf(str, "No transactions info\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
|
||||
//furi_string_cat_printf(str, "Transactions:\n");
|
||||
for(int i = 0; i < len; i++) {
|
||||
if(!apl->trans[i].amount) continue;
|
||||
// transaction counter
|
||||
furi_string_cat_printf(str, "\e#%d: ", apl->trans[i].atc);
|
||||
|
||||
// Print transaction amount
|
||||
uint8_t* a = (uint8_t*)&apl->trans[i].amount;
|
||||
bool top = true;
|
||||
for(int x = 0; x < 6; x++) {
|
||||
// cents
|
||||
if(x == 5) {
|
||||
furi_string_cat_printf(str, ".%02X", a[x]);
|
||||
break;
|
||||
}
|
||||
if(a[x]) {
|
||||
if(top) {
|
||||
furi_string_cat_printf(str, "%X", a[x]);
|
||||
top = false;
|
||||
} else {
|
||||
furi_string_cat_printf(str, "%02X", a[x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(apl->trans[i].currency) {
|
||||
furi_string_set_str(tmp, "UNK");
|
||||
nfc_emv_parser_get_currency_name(storage, apl->trans[i].currency, tmp);
|
||||
furi_string_cat_printf(str, " %s\n", furi_string_get_cstr(tmp));
|
||||
}
|
||||
|
||||
if(apl->trans[i].country) {
|
||||
furi_string_set_str(tmp, "UNK");
|
||||
nfc_emv_parser_get_country_name(storage, apl->trans[i].country, tmp);
|
||||
furi_string_cat_printf(str, "Country: %s\n", furi_string_get_cstr(tmp));
|
||||
}
|
||||
|
||||
if(apl->trans[i].date)
|
||||
furi_string_cat_printf(
|
||||
str,
|
||||
"%02lx/%02lx/%02lx ",
|
||||
apl->trans[i].date >> 16,
|
||||
(apl->trans[i].date >> 8) & 0xff,
|
||||
apl->trans[i].date & 0xff);
|
||||
|
||||
if(apl->trans[i].time)
|
||||
furi_string_cat_printf(
|
||||
str,
|
||||
"%02lx:%02lx:%02lx\n",
|
||||
apl->trans[i].time & 0xff,
|
||||
(apl->trans[i].time >> 8) & 0xff,
|
||||
apl->trans[i].time >> 16);
|
||||
}
|
||||
|
||||
furi_string_free(tmp);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
void nfc_render_emv_extra(const EmvData* data, FuriString* str) {
|
||||
nfc_render_emv_application(&data->emv_application, str);
|
||||
|
||||
nfc_render_emv_currency(data->emv_application.currency_code, str);
|
||||
nfc_render_emv_country(data->emv_application.country_code, str);
|
||||
nfc_render_emv_pin_try_counter(data->emv_application.pin_try_counter, str);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <nfc/protocols/emv/emv.h>
|
||||
|
||||
#include "../nfc_protocol_support_render_common.h"
|
||||
#include <stdint.h>
|
||||
|
||||
void nfc_render_emv_info(const EmvData* data, NfcProtocolFormatType format_type, FuriString* str);
|
||||
|
||||
void nfc_render_emv_data(const EmvData* data, FuriString* str);
|
||||
|
||||
void nfc_render_emv_pan(const uint8_t* data, const uint8_t len, FuriString* str);
|
||||
|
||||
void nfc_render_emv_name(const char* data, FuriString* str);
|
||||
|
||||
void nfc_render_emv_application(const EmvApplication* data, FuriString* str);
|
||||
|
||||
void nfc_render_emv_extra(const EmvData* data, FuriString* str);
|
||||
|
||||
void nfc_render_emv_expired(const EmvApplication* apl, FuriString* str);
|
||||
|
||||
void nfc_render_emv_country(uint16_t country_code, FuriString* str);
|
||||
|
||||
void nfc_render_emv_currency(uint16_t cur_code, FuriString* str);
|
||||
|
||||
void nfc_render_emv_transactions(const EmvApplication* data, FuriString* str);
|
||||
|
||||
void nfc_render_emv_uid(const uint8_t* uid, const uint8_t uid_len, FuriString* str);
|
||||
|
||||
void nfc_render_emv_header(FuriString* str);
|
||||
@@ -13,6 +13,7 @@ static void nfc_scene_info_on_enter_felica(NfcApp* instance) {
|
||||
const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
nfc_append_filename_string_when_present(instance, temp_str);
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
nfc_render_felica_info(data, NfcProtocolFormatTypeFull, temp_str);
|
||||
@@ -58,8 +59,8 @@ static void nfc_scene_read_success_on_enter_felica(NfcApp* instance) {
|
||||
furi_string_free(temp_str);
|
||||
}
|
||||
|
||||
static bool nfc_scene_saved_menu_on_event_felica(NfcApp* instance, uint32_t event) {
|
||||
if(event == SubmenuIndexCommonEdit) {
|
||||
static bool nfc_scene_saved_menu_on_event_felica(NfcApp* instance, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ static void nfc_scene_info_on_enter_iso14443_3a(NfcApp* instance) {
|
||||
const Iso14443_3aData* data = nfc_device_get_data(device, NfcProtocolIso14443_3a);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
nfc_append_filename_string_when_present(instance, temp_str);
|
||||
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
nfc_render_iso14443_3a_info(data, NfcProtocolFormatTypeFull, temp_str);
|
||||
@@ -95,8 +97,8 @@ static void nfc_scene_emulate_on_enter_iso14443_3a(NfcApp* instance) {
|
||||
instance->listener, nfc_scene_emulate_listener_callback_iso14443_3a, instance);
|
||||
}
|
||||
|
||||
static bool nfc_scene_read_menu_on_event_iso14443_3a(NfcApp* instance, uint32_t event) {
|
||||
if(event == SubmenuIndexCommonEmulate) {
|
||||
static bool nfc_scene_read_menu_on_event_iso14443_3a(NfcApp* instance, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -6,13 +6,17 @@ void nfc_render_iso14443_3a_format_bytes(FuriString* str, const uint8_t* const d
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_render_iso14443_tech_type(const Iso14443_3aData* data, FuriString* str) {
|
||||
const char iso_type = iso14443_3a_supports_iso14443_4(data) ? '4' : '3';
|
||||
furi_string_cat_printf(str, "Tech: ISO 14443-%c (NFC-A)\n", iso_type);
|
||||
}
|
||||
|
||||
void nfc_render_iso14443_3a_info(
|
||||
const Iso14443_3aData* data,
|
||||
NfcProtocolFormatType format_type,
|
||||
FuriString* str) {
|
||||
if(format_type == NfcProtocolFormatTypeFull) {
|
||||
const char iso_type = iso14443_3a_supports_iso14443_4(data) ? '4' : '3';
|
||||
furi_string_cat_printf(str, "ISO 14443-%c (NFC-A)\n", iso_type);
|
||||
nfc_render_iso14443_tech_type(data, str);
|
||||
}
|
||||
|
||||
nfc_render_iso14443_3a_brief(data, str);
|
||||
@@ -30,5 +34,5 @@ void nfc_render_iso14443_3a_brief(const Iso14443_3aData* data, FuriString* str)
|
||||
|
||||
void nfc_render_iso14443_3a_extra(const Iso14443_3aData* data, FuriString* str) {
|
||||
furi_string_cat_printf(str, "\nATQA: %02X %02X ", data->atqa[1], data->atqa[0]);
|
||||
furi_string_cat_printf(str, "SAK: %02X", data->sak);
|
||||
furi_string_cat_printf(str, "\nSAK: %02X", data->sak);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ void nfc_render_iso14443_3a_info(
|
||||
NfcProtocolFormatType format_type,
|
||||
FuriString* str);
|
||||
|
||||
void nfc_render_iso14443_tech_type(const Iso14443_3aData* data, FuriString* str);
|
||||
|
||||
void nfc_render_iso14443_3a_format_bytes(FuriString* str, const uint8_t* const data, size_t size);
|
||||
|
||||
void nfc_render_iso14443_3a_brief(const Iso14443_3aData* data, FuriString* str);
|
||||
|
||||
@@ -13,6 +13,7 @@ static void nfc_scene_info_on_enter_iso14443_3b(NfcApp* instance) {
|
||||
const Iso14443_3bData* data = nfc_device_get_data(device, NfcProtocolIso14443_3b);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
nfc_append_filename_string_when_present(instance, temp_str);
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
nfc_render_iso14443_3b_info(data, NfcProtocolFormatTypeFull, temp_str);
|
||||
@@ -59,8 +60,8 @@ static void nfc_scene_read_success_on_enter_iso14443_3b(NfcApp* instance) {
|
||||
furi_string_free(temp_str);
|
||||
}
|
||||
|
||||
bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, uint32_t event) {
|
||||
if(event == SubmenuIndexCommonEdit) {
|
||||
bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
|
||||
return true;
|
||||
}
|
||||
@@ -68,7 +69,7 @@ bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, uint32_t
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool nfc_scene_saved_menu_on_event_iso14443_3b(NfcApp* instance, uint32_t event) {
|
||||
static bool nfc_scene_saved_menu_on_event_iso14443_3b(NfcApp* instance, SceneManagerEvent event) {
|
||||
return nfc_scene_saved_menu_on_event_iso14443_3b_common(instance, event);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
|
||||
#include "iso14443_3b.h"
|
||||
|
||||
bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, uint32_t event);
|
||||
bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, SceneManagerEvent event);
|
||||
|
||||
@@ -6,7 +6,7 @@ void nfc_render_iso14443_3b_info(
|
||||
FuriString* str) {
|
||||
if(format_type == NfcProtocolFormatTypeFull) {
|
||||
const char iso_type = iso14443_3b_supports_iso14443_4(data) ? '4' : '3';
|
||||
furi_string_cat_printf(str, "ISO 14443-%c (NFC-B)\n", iso_type);
|
||||
furi_string_cat_printf(str, "Tech: ISO 14443-%c (NFC-B)\n", iso_type);
|
||||
}
|
||||
|
||||
furi_string_cat_printf(str, "UID:");
|
||||
@@ -20,7 +20,7 @@ void nfc_render_iso14443_3b_info(
|
||||
|
||||
if(format_type != NfcProtocolFormatTypeFull) return;
|
||||
|
||||
furi_string_cat_printf(str, "\n\e#Protocol info\n");
|
||||
furi_string_cat_printf(str, "\n::::::::::::::::[Protocol info]:::::::::::::::\n");
|
||||
|
||||
if(iso14443_3b_supports_bit_rate(data, Iso14443_3bBitRateBoth106Kbit)) {
|
||||
furi_string_cat(str, "Bit rate PICC <-> PCD:\n 106 kBit/s supported\n");
|
||||
@@ -68,7 +68,7 @@ void nfc_render_iso14443_3b_info(
|
||||
iso14443_3b_supports_frame_option(data, Iso14443_3bFrameOptionCid) ? "" : "not ";
|
||||
furi_string_cat_printf(str, "CID: %ssupported", cid_support_str);
|
||||
|
||||
furi_string_cat_printf(str, "\n\e#Application data\nRaw:");
|
||||
furi_string_cat_printf(str, "\n::::::::::::[Application data]::::::::::::\nRaw:");
|
||||
|
||||
size_t app_data_size;
|
||||
const uint8_t* app_data = iso14443_3b_get_application_data(data, &app_data_size);
|
||||
|
||||
@@ -14,6 +14,7 @@ static void nfc_scene_info_on_enter_iso14443_4a(NfcApp* instance) {
|
||||
const Iso14443_4aData* data = nfc_device_get_data(device, NfcProtocolIso14443_4a);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
nfc_append_filename_string_when_present(instance, temp_str);
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
nfc_render_iso14443_4a_info(data, NfcProtocolFormatTypeFull, temp_str);
|
||||
@@ -99,8 +100,8 @@ static void nfc_scene_emulate_on_enter_iso14443_4a(NfcApp* instance) {
|
||||
instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance);
|
||||
}
|
||||
|
||||
static bool nfc_scene_read_menu_on_event_iso14443_4a(NfcApp* instance, uint32_t event) {
|
||||
if(event == SubmenuIndexCommonEmulate) {
|
||||
static bool nfc_scene_read_menu_on_event_iso14443_4a(NfcApp* instance, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -14,11 +14,12 @@ void nfc_render_iso14443_4a_info(
|
||||
}
|
||||
|
||||
void nfc_render_iso14443_4a_brief(const Iso14443_4aData* data, FuriString* str) {
|
||||
nfc_render_iso14443_tech_type(iso14443_4a_get_base_data(data), str);
|
||||
nfc_render_iso14443_3a_brief(iso14443_4a_get_base_data(data), str);
|
||||
}
|
||||
|
||||
void nfc_render_iso14443_4a_extra(const Iso14443_4aData* data, FuriString* str) {
|
||||
furi_string_cat_printf(str, "\n\e#Protocol info\n");
|
||||
furi_string_cat_printf(str, "\n::::::::::::::::[Protocol info]:::::::::::::::\n");
|
||||
|
||||
if(iso14443_4a_supports_bit_rate(data, Iso14443_4aBitRateBoth106Kbit)) {
|
||||
furi_string_cat(str, "Bit rate PICC <-> PCD:\n 106 kBit/s supported\n");
|
||||
@@ -72,7 +73,7 @@ void nfc_render_iso14443_4a_extra(const Iso14443_4aData* data, FuriString* str)
|
||||
const uint8_t* hist_bytes = iso14443_4a_get_historical_bytes(data, &hist_bytes_count);
|
||||
|
||||
if(hist_bytes_count > 0) {
|
||||
furi_string_cat_printf(str, "\n\e#Historical bytes\nRaw:");
|
||||
furi_string_cat_printf(str, "\n:::::::::::::[Historical bytes]:::::::::::::\nRaw:");
|
||||
|
||||
for(size_t i = 0; i < hist_bytes_count; ++i) {
|
||||
furi_string_cat_printf(str, " %02X", hist_bytes[i]);
|
||||
|
||||
@@ -14,6 +14,7 @@ static void nfc_scene_info_on_enter_iso14443_4b(NfcApp* instance) {
|
||||
const Iso14443_4bData* data = nfc_device_get_data(device, NfcProtocolIso14443_4b);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
nfc_append_filename_string_when_present(instance, temp_str);
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
nfc_render_iso14443_4b_info(data, NfcProtocolFormatTypeFull, temp_str);
|
||||
@@ -64,8 +65,8 @@ static void nfc_scene_saved_menu_on_enter_iso14443_4b(NfcApp* instance) {
|
||||
UNUSED(instance);
|
||||
}
|
||||
|
||||
static bool nfc_scene_read_menu_on_event_iso14443_4b(NfcApp* instance, uint32_t event) {
|
||||
if(event == SubmenuIndexCommonEmulate) {
|
||||
static bool nfc_scene_read_menu_on_event_iso14443_4b(NfcApp* instance, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEmulate) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate);
|
||||
return true;
|
||||
}
|
||||
@@ -73,7 +74,7 @@ static bool nfc_scene_read_menu_on_event_iso14443_4b(NfcApp* instance, uint32_t
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool nfc_scene_saved_menu_on_event_iso14443_4b(NfcApp* instance, uint32_t event) {
|
||||
static bool nfc_scene_saved_menu_on_event_iso14443_4b(NfcApp* instance, SceneManagerEvent event) {
|
||||
return nfc_scene_saved_menu_on_event_iso14443_3b_common(instance, event);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,16 +14,30 @@ static void nfc_scene_info_on_enter_iso15693_3(NfcApp* instance) {
|
||||
const Iso15693_3Data* data = nfc_device_get_data(device, NfcProtocolIso15693_3);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
nfc_append_filename_string_when_present(instance, temp_str);
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
nfc_render_iso15693_3_info(data, NfcProtocolFormatTypeFull, temp_str);
|
||||
|
||||
widget_reset(instance->widget);
|
||||
widget_add_text_scroll_element(
|
||||
instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
|
||||
|
||||
furi_string_free(temp_str);
|
||||
}
|
||||
|
||||
static void nfc_scene_more_info_on_enter_iso15693_3(NfcApp* instance) {
|
||||
const NfcDevice* device = instance->nfc_device;
|
||||
const Iso15693_3Data* data = nfc_device_get_data(device, NfcProtocolIso15693_3);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
nfc_render_iso15693_3_system_info(data, temp_str);
|
||||
|
||||
widget_add_text_scroll_element(
|
||||
instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
|
||||
furi_string_free(temp_str);
|
||||
}
|
||||
|
||||
static NfcCommand nfc_scene_read_poller_callback_iso15693_3(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.protocol == NfcProtocolIso15693_3);
|
||||
|
||||
@@ -94,8 +108,8 @@ static void nfc_scene_emulate_on_enter_iso15693_3(NfcApp* instance) {
|
||||
instance->listener, nfc_scene_emulate_listener_callback_iso15693_3, instance);
|
||||
}
|
||||
|
||||
static bool nfc_scene_saved_menu_on_event_iso15693_3(NfcApp* instance, uint32_t event) {
|
||||
if(event == SubmenuIndexCommonEdit) {
|
||||
static bool nfc_scene_saved_menu_on_event_iso15693_3(NfcApp* instance, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
|
||||
return true;
|
||||
}
|
||||
@@ -104,13 +118,19 @@ static bool nfc_scene_saved_menu_on_event_iso15693_3(NfcApp* instance, uint32_t
|
||||
}
|
||||
|
||||
const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = {
|
||||
.features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEditUid,
|
||||
.features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEditUid |
|
||||
NfcProtocolFeatureMoreInfo,
|
||||
|
||||
.scene_info =
|
||||
{
|
||||
.on_enter = nfc_scene_info_on_enter_iso15693_3,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
},
|
||||
.scene_more_info =
|
||||
{
|
||||
.on_enter = nfc_scene_more_info_on_enter_iso15693_3,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
},
|
||||
.scene_read =
|
||||
{
|
||||
.on_enter = nfc_scene_read_on_enter_iso15693_3,
|
||||
|
||||
@@ -36,32 +36,7 @@ void nfc_render_iso15693_3_brief(const Iso15693_3Data* data, FuriString* str) {
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_render_iso15693_3_extra(const Iso15693_3Data* data, FuriString* str) {
|
||||
furi_string_cat(str, "\n\e#General info\n");
|
||||
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) {
|
||||
furi_string_cat_printf(str, "DSFID: %02X\n", data->system_info.ic_ref);
|
||||
}
|
||||
|
||||
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) {
|
||||
furi_string_cat_printf(str, "AFI: %02X\n", data->system_info.afi);
|
||||
}
|
||||
|
||||
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_IC_REF) {
|
||||
furi_string_cat_printf(str, "IC Reference: %02X\n", data->system_info.ic_ref);
|
||||
}
|
||||
|
||||
furi_string_cat(str, "\e#Lock bits\n");
|
||||
|
||||
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) {
|
||||
furi_string_cat_printf(
|
||||
str, "DSFID: %s locked\n", data->settings.lock_bits.dsfid ? "" : "not");
|
||||
}
|
||||
|
||||
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) {
|
||||
furi_string_cat_printf(
|
||||
str, "AFI: %s locked\n", data->settings.lock_bits.dsfid ? "" : "not");
|
||||
}
|
||||
|
||||
void nfc_render_iso15693_3_system_info(const Iso15693_3Data* data, FuriString* str) {
|
||||
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_MEMORY) {
|
||||
furi_string_cat(str, "\e#Memory data\n\e*--------------------\n");
|
||||
|
||||
@@ -88,5 +63,34 @@ void nfc_render_iso15693_3_extra(const Iso15693_3Data* data, FuriString* str) {
|
||||
"(Data is too big. Showing only the first %u bytes.)",
|
||||
display_block_count * block_size);
|
||||
}
|
||||
} else {
|
||||
furi_string_cat(str, "\e#No available data\n");
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_render_iso15693_3_extra(const Iso15693_3Data* data, FuriString* str) {
|
||||
furi_string_cat(str, "\n::::::::::::::::[General info]:::::::::::::::::\n");
|
||||
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) {
|
||||
furi_string_cat_printf(str, "DSFID: %02X\n", data->system_info.ic_ref);
|
||||
}
|
||||
|
||||
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) {
|
||||
furi_string_cat_printf(str, "AFI: %02X\n", data->system_info.afi);
|
||||
}
|
||||
|
||||
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_IC_REF) {
|
||||
furi_string_cat_printf(str, "IC Reference: %02X\n", data->system_info.ic_ref);
|
||||
}
|
||||
|
||||
furi_string_cat(str, ":::::::::::::::::::[Lock bits]::::::::::::::::::::\n");
|
||||
|
||||
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) {
|
||||
furi_string_cat_printf(
|
||||
str, "DSFID: %s locked\n", data->settings.lock_bits.dsfid ? "" : "not");
|
||||
}
|
||||
|
||||
if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) {
|
||||
furi_string_cat_printf(
|
||||
str, "AFI: %s locked\n", data->settings.lock_bits.dsfid ? "" : "not");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,3 +12,5 @@ void nfc_render_iso15693_3_info(
|
||||
void nfc_render_iso15693_3_brief(const Iso15693_3Data* data, FuriString* str);
|
||||
|
||||
void nfc_render_iso15693_3_extra(const Iso15693_3Data* data, FuriString* str);
|
||||
|
||||
void nfc_render_iso15693_3_system_info(const Iso15693_3Data* data, FuriString* str);
|
||||
|
||||
@@ -21,6 +21,7 @@ static void nfc_scene_info_on_enter_mf_classic(NfcApp* instance) {
|
||||
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
nfc_append_filename_string_when_present(instance, temp_str);
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
furi_string_replace(temp_str, "Mifare", "MIFARE");
|
||||
@@ -99,8 +100,9 @@ static void nfc_scene_read_on_enter_mf_classic(NfcApp* instance) {
|
||||
nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_classic, instance);
|
||||
}
|
||||
|
||||
static bool nfc_scene_read_on_event_mf_classic(NfcApp* instance, uint32_t event) {
|
||||
if(event == NfcCustomEventPollerIncomplete) {
|
||||
static bool nfc_scene_read_on_event_mf_classic(NfcApp* instance, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom &&
|
||||
event.event == NfcCustomEventPollerIncomplete) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack);
|
||||
}
|
||||
|
||||
@@ -170,8 +172,8 @@ static void nfc_scene_emulate_on_enter_mf_classic(NfcApp* instance) {
|
||||
nfc_listener_start(instance->listener, NULL, NULL);
|
||||
}
|
||||
|
||||
static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, uint32_t event) {
|
||||
if(event == SubmenuIndexDetectReader) {
|
||||
static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexDetectReader) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneSaveConfirm);
|
||||
dolphin_deed(DolphinDeedNfcDetectReader);
|
||||
return true;
|
||||
@@ -180,27 +182,29 @@ static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, uint32_t e
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool nfc_scene_saved_menu_on_event_mf_classic(NfcApp* instance, uint32_t event) {
|
||||
static bool nfc_scene_saved_menu_on_event_mf_classic(NfcApp* instance, SceneManagerEvent event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event == SubmenuIndexDetectReader) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader);
|
||||
consumed = true;
|
||||
} else if(event == SubmenuIndexWrite) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWriteInitial);
|
||||
consumed = true;
|
||||
} else if(event == SubmenuIndexUpdate) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicUpdateInitial);
|
||||
consumed = true;
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexDetectReader) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexWrite) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWriteInitial);
|
||||
consumed = true;
|
||||
} else if(event.event == SubmenuIndexUpdate) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicUpdateInitial);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
static bool nfc_scene_save_name_on_event_mf_classic(NfcApp* instance, uint32_t event) {
|
||||
static bool nfc_scene_save_name_on_event_mf_classic(NfcApp* instance, SceneManagerEvent event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event == NfcCustomEventTextInputDone) {
|
||||
if(event.type == SceneManagerEventTypeCustom && event.event == NfcCustomEventTextInputDone) {
|
||||
mf_classic_key_cache_save(
|
||||
instance->mfc_key_cache,
|
||||
nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic));
|
||||
|
||||
@@ -18,13 +18,48 @@ void nfc_render_mf_classic_info(
|
||||
furi_string_cat_printf(str, "\nSectors Read: %u/%u", sectors_read, sectors_total);
|
||||
}
|
||||
|
||||
static void
|
||||
mf_classic_render_raw_data(const uint8_t* data, size_t size, bool data_read, FuriString* str) {
|
||||
furi_assert((size % 2) == 0);
|
||||
|
||||
for(size_t i = 0; i < size; i += 2) {
|
||||
if(data_read) {
|
||||
furi_string_cat_printf(str, "%02X%02X ", data[i], data[i + 1]);
|
||||
} else {
|
||||
furi_string_cat_printf(str, "???? ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mf_classic_render_block(const MfClassicData* data, uint8_t block_num, FuriString* str) {
|
||||
if(mf_classic_is_sector_trailer(block_num)) {
|
||||
uint8_t sec_num = mf_classic_get_sector_by_block(block_num);
|
||||
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sec_num);
|
||||
|
||||
// Render key A
|
||||
bool key_read = mf_classic_is_key_found(data, sec_num, MfClassicKeyTypeA);
|
||||
mf_classic_render_raw_data(sec_tr->key_a.data, sizeof(MfClassicKey), key_read, str);
|
||||
|
||||
// Render access bits
|
||||
bool access_bits_read = mf_classic_is_block_read(data, block_num);
|
||||
mf_classic_render_raw_data(
|
||||
sec_tr->access_bits.data, sizeof(MfClassicAccessBits), access_bits_read, str);
|
||||
|
||||
// Render key B
|
||||
key_read = mf_classic_is_key_found(data, sec_num, MfClassicKeyTypeB);
|
||||
mf_classic_render_raw_data(sec_tr->key_b.data, sizeof(MfClassicKey), key_read, str);
|
||||
} else {
|
||||
const uint8_t* block_data = data->block[block_num].data;
|
||||
bool block_read = mf_classic_is_block_read(data, block_num);
|
||||
mf_classic_render_raw_data(block_data, sizeof(MfClassicBlock), block_read, str);
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_render_mf_classic_dump(const MfClassicData* data, FuriString* str) {
|
||||
uint16_t total_blocks = mf_classic_get_total_block_num(data->type);
|
||||
|
||||
for(size_t i = 0; i < total_blocks; i++) {
|
||||
for(size_t j = 0; j < sizeof(MfClassicBlock); j += 2) {
|
||||
furi_string_cat_printf(
|
||||
str, "%02X%02X ", data->block[i].data[j], data->block[i].data[j + 1]);
|
||||
}
|
||||
mf_classic_render_block(data, i, str);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,10 @@ static void nfc_scene_info_on_enter_mf_desfire(NfcApp* instance) {
|
||||
const MfDesfireData* data = nfc_device_get_data(device, NfcProtocolMfDesfire);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
nfc_append_filename_string_when_present(instance, temp_str);
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
furi_string_replace(temp_str, "Mifare", "MIFARE");
|
||||
nfc_render_mf_desfire_info(data, NfcProtocolFormatTypeFull, temp_str);
|
||||
|
||||
widget_add_text_scroll_element(
|
||||
@@ -56,6 +58,7 @@ static void nfc_scene_read_success_on_enter_mf_desfire(NfcApp* instance) {
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
furi_string_replace(temp_str, "Mifare", "MIFARE");
|
||||
nfc_render_mf_desfire_info(data, NfcProtocolFormatTypeShort, temp_str);
|
||||
|
||||
widget_add_text_scroll_element(
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "mf_ultralight_render.h"
|
||||
|
||||
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller.h>
|
||||
#include <toolbox/pretty_format.h>
|
||||
|
||||
#include "nfc/nfc_app_i.h"
|
||||
|
||||
@@ -15,11 +16,18 @@ enum {
|
||||
SubmenuIndexWrite,
|
||||
};
|
||||
|
||||
enum {
|
||||
NfcSceneMoreInfoStateASCII,
|
||||
NfcSceneMoreInfoStateRawData,
|
||||
};
|
||||
|
||||
static void nfc_scene_info_on_enter_mf_ultralight(NfcApp* instance) {
|
||||
const NfcDevice* device = instance->nfc_device;
|
||||
const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
nfc_append_filename_string_when_present(instance, temp_str);
|
||||
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
nfc_render_mf_ultralight_info(data, NfcProtocolFormatTypeFull, temp_str);
|
||||
@@ -35,11 +43,62 @@ static void nfc_scene_more_info_on_enter_mf_ultralight(NfcApp* instance) {
|
||||
const MfUltralightData* mfu = nfc_device_get_data(device, NfcProtocolMfUltralight);
|
||||
|
||||
furi_string_reset(instance->text_box_store);
|
||||
nfc_render_mf_ultralight_dump(mfu, instance->text_box_store);
|
||||
uint32_t scene_state =
|
||||
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMoreInfo);
|
||||
|
||||
text_box_set_font(instance->text_box, TextBoxFontHex);
|
||||
text_box_set_text(instance->text_box, furi_string_get_cstr(instance->text_box_store));
|
||||
view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextBox);
|
||||
if(scene_state == NfcSceneMoreInfoStateASCII) {
|
||||
pretty_format_bytes_hex_canonical(
|
||||
instance->text_box_store,
|
||||
MF_ULTRALIGHT_PAGE_SIZE,
|
||||
PRETTY_FORMAT_FONT_MONOSPACE,
|
||||
(uint8_t*)mfu->page,
|
||||
mfu->pages_read * MF_ULTRALIGHT_PAGE_SIZE);
|
||||
|
||||
widget_add_text_scroll_element(
|
||||
instance->widget, 0, 0, 128, 48, furi_string_get_cstr(instance->text_box_store));
|
||||
widget_add_button_element(
|
||||
instance->widget,
|
||||
GuiButtonTypeRight,
|
||||
"Raw Data",
|
||||
nfc_protocol_support_common_widget_callback,
|
||||
instance);
|
||||
|
||||
widget_add_button_element(
|
||||
instance->widget,
|
||||
GuiButtonTypeLeft,
|
||||
"Info",
|
||||
nfc_protocol_support_common_widget_callback,
|
||||
instance);
|
||||
} else if(scene_state == NfcSceneMoreInfoStateRawData) {
|
||||
nfc_render_mf_ultralight_dump(mfu, instance->text_box_store);
|
||||
widget_add_text_scroll_element(
|
||||
instance->widget, 0, 0, 128, 48, furi_string_get_cstr(instance->text_box_store));
|
||||
|
||||
widget_add_button_element(
|
||||
instance->widget,
|
||||
GuiButtonTypeLeft,
|
||||
"ASCII",
|
||||
nfc_protocol_support_common_widget_callback,
|
||||
instance);
|
||||
}
|
||||
}
|
||||
|
||||
static bool nfc_scene_more_info_on_event_mf_ultralight(NfcApp* instance, SceneManagerEvent event) {
|
||||
bool consumed = false;
|
||||
|
||||
if((event.type == SceneManagerEventTypeCustom && event.event == GuiButtonTypeLeft) ||
|
||||
(event.type == SceneManagerEventTypeBack)) {
|
||||
scene_manager_set_scene_state(
|
||||
instance->scene_manager, NfcSceneMoreInfo, NfcSceneMoreInfoStateASCII);
|
||||
scene_manager_previous_scene(instance->scene_manager);
|
||||
consumed = true;
|
||||
} else if(event.type == SceneManagerEventTypeCustom && event.event == GuiButtonTypeRight) {
|
||||
scene_manager_set_scene_state(
|
||||
instance->scene_manager, NfcSceneMoreInfo, NfcSceneMoreInfoStateRawData);
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMoreInfo);
|
||||
consumed = true;
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
static NfcCommand
|
||||
@@ -132,15 +191,17 @@ static void nfc_scene_read_on_enter_mf_ultralight(NfcApp* instance) {
|
||||
nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_ultralight, instance);
|
||||
}
|
||||
|
||||
bool nfc_scene_read_on_event_mf_ultralight(NfcApp* instance, uint32_t event) {
|
||||
if(event == NfcCustomEventCardDetected) {
|
||||
scene_manager_set_scene_state(
|
||||
instance->scene_manager, NfcSceneRead, NfcSceneMfUltralightReadMenuStateCardFound);
|
||||
nfc_scene_read_setup_view(instance);
|
||||
} else if((event == NfcCustomEventPollerIncomplete)) {
|
||||
notification_message(instance->notifications, &sequence_semi_success);
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
|
||||
dolphin_deed(DolphinDeedNfcReadSuccess);
|
||||
bool nfc_scene_read_on_event_mf_ultralight(NfcApp* instance, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == NfcCustomEventCardDetected) {
|
||||
scene_manager_set_scene_state(
|
||||
instance->scene_manager, NfcSceneRead, NfcSceneMfUltralightReadMenuStateCardFound);
|
||||
nfc_scene_read_setup_view(instance);
|
||||
} else if((event.event == NfcCustomEventPollerIncomplete)) {
|
||||
notification_message(instance->notifications, &sequence_semi_success);
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
|
||||
dolphin_deed(DolphinDeedNfcReadSuccess);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -202,14 +263,17 @@ static void nfc_scene_emulate_on_enter_mf_ultralight(NfcApp* instance) {
|
||||
nfc_listener_start(instance->listener, NULL, NULL);
|
||||
}
|
||||
|
||||
static bool
|
||||
nfc_scene_read_and_saved_menu_on_event_mf_ultralight(NfcApp* instance, uint32_t event) {
|
||||
if(event == SubmenuIndexUnlock) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu);
|
||||
return true;
|
||||
} else if(event == SubmenuIndexWrite) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrite);
|
||||
return true;
|
||||
static bool nfc_scene_read_and_saved_menu_on_event_mf_ultralight(
|
||||
NfcApp* instance,
|
||||
SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexUnlock) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu);
|
||||
return true;
|
||||
} else if(event.event == SubmenuIndexWrite) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrite);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -225,7 +289,7 @@ const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = {
|
||||
.scene_more_info =
|
||||
{
|
||||
.on_enter = nfc_scene_more_info_on_enter_mf_ultralight,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
.on_event = nfc_scene_more_info_on_event_mf_ultralight,
|
||||
},
|
||||
.scene_read =
|
||||
{
|
||||
|
||||
@@ -36,10 +36,11 @@ void nfc_render_mf_ultralight_info(
|
||||
}
|
||||
|
||||
void nfc_render_mf_ultralight_dump(const MfUltralightData* data, FuriString* str) {
|
||||
furi_string_cat_printf(str, "\e*");
|
||||
for(size_t i = 0; i < data->pages_read; i++) {
|
||||
const uint8_t* page_data = data->page[i].data;
|
||||
for(size_t j = 0; j < MF_ULTRALIGHT_PAGE_SIZE; j += 2) {
|
||||
furi_string_cat_printf(str, "%02X%02X ", page_data[j], page_data[j + 1]);
|
||||
furi_string_cat_printf(str, " %02X%02X", page_data[j], page_data[j + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ void nfc_protocol_support_on_exit(NfcProtocolSupportScene scene, void* context)
|
||||
nfc_protocol_support_scenes[scene].on_exit(instance);
|
||||
}
|
||||
|
||||
static bool nfc_protocol_support_has_feature(NfcProtocol protocol, NfcProtocolFeature feature) {
|
||||
bool nfc_protocol_support_has_feature(NfcProtocol protocol, NfcProtocolFeature feature) {
|
||||
return nfc_protocol_support[protocol]->features & feature;
|
||||
}
|
||||
|
||||
@@ -131,16 +131,15 @@ static bool
|
||||
nfc_protocol_support_scene_more_info_on_event(NfcApp* instance, SceneManagerEvent event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
||||
consumed = nfc_protocol_support[protocol]->scene_more_info.on_event(instance, event.event);
|
||||
}
|
||||
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
||||
consumed = nfc_protocol_support[protocol]->scene_more_info.on_event(instance, event);
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
static void nfc_protocol_support_scene_more_info_on_exit(NfcApp* instance) {
|
||||
text_box_reset(instance->text_box);
|
||||
widget_reset(instance->widget);
|
||||
furi_string_reset(instance->text_box_store);
|
||||
}
|
||||
|
||||
@@ -188,8 +187,7 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana
|
||||
} else {
|
||||
const NfcProtocol protocol =
|
||||
instance->protocols_detected[instance->protocols_detected_selected_idx];
|
||||
consumed =
|
||||
nfc_protocol_support[protocol]->scene_read.on_event(instance, event.event);
|
||||
consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event);
|
||||
}
|
||||
} else if(event.event == NfcCustomEventPollerFailure) {
|
||||
nfc_poller_stop(instance->poller);
|
||||
@@ -202,7 +200,7 @@ static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneMana
|
||||
} else if(event.event == NfcCustomEventCardDetected) {
|
||||
const NfcProtocol protocol =
|
||||
instance->protocols_detected[instance->protocols_detected_selected_idx];
|
||||
consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event.event);
|
||||
consumed = nfc_protocol_support[protocol]->scene_read.on_event(instance, event);
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
nfc_poller_stop(instance->poller);
|
||||
@@ -287,8 +285,7 @@ static bool
|
||||
consumed = true;
|
||||
} else {
|
||||
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
||||
consumed =
|
||||
nfc_protocol_support[protocol]->scene_read_menu.on_event(instance, event.event);
|
||||
consumed = nfc_protocol_support[protocol]->scene_read_menu.on_event(instance, event);
|
||||
}
|
||||
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
@@ -456,8 +453,7 @@ static bool
|
||||
consumed = true;
|
||||
} else {
|
||||
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
||||
consumed =
|
||||
nfc_protocol_support[protocol]->scene_saved_menu.on_event(instance, event.event);
|
||||
consumed = nfc_protocol_support[protocol]->scene_saved_menu.on_event(instance, event);
|
||||
}
|
||||
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
@@ -530,8 +526,8 @@ static bool
|
||||
DolphinDeedNfcSave);
|
||||
const NfcProtocol protocol =
|
||||
instance->protocols_detected[instance->protocols_detected_selected_idx];
|
||||
consumed = nfc_protocol_support[protocol]->scene_save_name.on_event(
|
||||
instance, event.event);
|
||||
consumed =
|
||||
nfc_protocol_support[protocol]->scene_save_name.on_event(instance, event);
|
||||
} else {
|
||||
consumed = scene_manager_search_and_switch_to_previous_scene(
|
||||
instance->scene_manager, NfcSceneStart);
|
||||
@@ -572,11 +568,11 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) {
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device);
|
||||
|
||||
widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_47x61);
|
||||
widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_51x64);
|
||||
|
||||
if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) {
|
||||
widget_add_string_element(
|
||||
widget, 90, 13, AlignCenter, AlignTop, FontPrimary, "Emulating UID");
|
||||
widget, 90, 26, AlignCenter, AlignCenter, FontPrimary, "Emulating UID");
|
||||
|
||||
size_t uid_len;
|
||||
const uint8_t* uid = nfc_device_get_uid(instance->nfc_device, &uid_len);
|
||||
@@ -588,7 +584,8 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) {
|
||||
furi_string_trim(temp_str);
|
||||
|
||||
} else {
|
||||
widget_add_string_element(widget, 90, 13, AlignCenter, AlignTop, FontPrimary, "Emulating");
|
||||
widget_add_string_element(
|
||||
widget, 90, 26, AlignCenter, AlignCenter, FontPrimary, "Emulating");
|
||||
if(!furi_string_empty(instance->file_name)) {
|
||||
furi_string_printf(
|
||||
temp_str,
|
||||
@@ -600,11 +597,12 @@ static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) {
|
||||
temp_str,
|
||||
"Unsaved\n%s",
|
||||
nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull));
|
||||
furi_string_replace_str(temp_str, "Mifare", "MIFARE");
|
||||
}
|
||||
}
|
||||
|
||||
widget_add_text_box_element(
|
||||
widget, 56, 28, 71, 25, AlignCenter, AlignTop, furi_string_get_cstr(temp_str), false);
|
||||
widget, 56, 33, 71, 25, AlignCenter, AlignTop, furi_string_get_cstr(temp_str), false);
|
||||
|
||||
furi_string_free(temp_str);
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/scene_manager.h>
|
||||
#include <lib/nfc/protocols/nfc_protocol.h>
|
||||
|
||||
#include "nfc_protocol_support_common.h"
|
||||
|
||||
@@ -111,3 +112,5 @@ bool nfc_protocol_support_on_event(
|
||||
* @param[in,out] context pointer to a user-specified context (will be passed to concrete handler).
|
||||
*/
|
||||
void nfc_protocol_support_on_exit(NfcProtocolSupportScene scene, void* context);
|
||||
|
||||
bool nfc_protocol_support_has_feature(NfcProtocol protocol, NfcProtocolFeature feature);
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <core/string.h>
|
||||
|
||||
#include "../../nfc_app.h"
|
||||
#include "../../nfc_app_i.h"
|
||||
|
||||
/**
|
||||
* @brief Scene entry handler.
|
||||
@@ -19,10 +20,10 @@ typedef void (*NfcProtocolSupportOnEnter)(NfcApp* instance);
|
||||
* @brief Scene event handler.
|
||||
*
|
||||
* @param[in,out] instance pointer to the NFC application instance.
|
||||
* @param[in] event custom event that has occurred.
|
||||
* @param[in] event scene manager event that has occurred.
|
||||
* @returns true if the event was handled, false otherwise.
|
||||
*/
|
||||
typedef bool (*NfcProtocolSupportOnEvent)(NfcApp* instance, uint32_t event);
|
||||
typedef bool (*NfcProtocolSupportOnEvent)(NfcApp* instance, SceneManagerEvent event);
|
||||
|
||||
/**
|
||||
* @brief Abstract scene interface.
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "mf_ultralight/mf_ultralight.h"
|
||||
#include "mf_classic/mf_classic.h"
|
||||
#include "mf_desfire/mf_desfire.h"
|
||||
#include "emv/emv.h"
|
||||
#include "slix/slix.h"
|
||||
#include "st25tb/st25tb.h"
|
||||
|
||||
@@ -41,5 +42,6 @@ const NfcProtocolSupportBase* nfc_protocol_support[NfcProtocolNum] = {
|
||||
[NfcProtocolMfDesfire] = &nfc_protocol_support_mf_desfire,
|
||||
[NfcProtocolSlix] = &nfc_protocol_support_slix,
|
||||
[NfcProtocolSt25tb] = &nfc_protocol_support_st25tb,
|
||||
[NfcProtocolEmv] = &nfc_protocol_support_emv,
|
||||
/* Add new protocol support implementations here */
|
||||
};
|
||||
|
||||
@@ -35,8 +35,8 @@ void nfc_protocol_support_common_on_enter_empty(NfcApp* instance) {
|
||||
UNUSED(instance);
|
||||
}
|
||||
|
||||
bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, uint32_t event) {
|
||||
bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, SceneManagerEvent event) {
|
||||
UNUSED(instance);
|
||||
UNUSED(event);
|
||||
return true;
|
||||
return event.type != SceneManagerEventTypeBack;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <gui/modules/widget.h>
|
||||
|
||||
#include "nfc/nfc_app.h"
|
||||
#include "nfc/nfc_app_i.h"
|
||||
|
||||
/**
|
||||
* @brief Common submenu indices.
|
||||
@@ -82,4 +83,4 @@ void nfc_protocol_support_common_on_enter_empty(NfcApp* instance);
|
||||
* @param[in] event custom event type that has occurred.
|
||||
* @returns always true.
|
||||
*/
|
||||
bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, uint32_t event);
|
||||
bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, SceneManagerEvent event);
|
||||
|
||||
@@ -14,16 +14,30 @@ static void nfc_scene_info_on_enter_slix(NfcApp* instance) {
|
||||
const SlixData* data = nfc_device_get_data(device, NfcProtocolSlix);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
nfc_append_filename_string_when_present(instance, temp_str);
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
nfc_render_slix_info(data, NfcProtocolFormatTypeFull, temp_str);
|
||||
|
||||
widget_reset(instance->widget);
|
||||
widget_add_text_scroll_element(
|
||||
instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
|
||||
|
||||
furi_string_free(temp_str);
|
||||
}
|
||||
|
||||
static void nfc_scene_more_info_on_enter_slix(NfcApp* instance) {
|
||||
const NfcDevice* device = instance->nfc_device;
|
||||
const SlixData* data = nfc_device_get_data(device, NfcProtocolSlix);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
nfc_render_iso15693_3_system_info(slix_get_base_data(data), temp_str);
|
||||
|
||||
widget_add_text_scroll_element(
|
||||
instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str));
|
||||
furi_string_free(temp_str);
|
||||
}
|
||||
|
||||
static NfcCommand nfc_scene_read_poller_callback_slix(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.protocol == NfcProtocolSlix);
|
||||
|
||||
@@ -91,8 +105,8 @@ static void nfc_scene_emulate_on_enter_slix(NfcApp* instance) {
|
||||
nfc_listener_start(instance->listener, nfc_scene_emulate_listener_callback_slix, instance);
|
||||
}
|
||||
|
||||
static bool nfc_scene_saved_menu_on_event_slix(NfcApp* instance, uint32_t event) {
|
||||
if(event == SubmenuIndexCommonEdit) {
|
||||
static bool nfc_scene_saved_menu_on_event_slix(NfcApp* instance, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
|
||||
return true;
|
||||
}
|
||||
@@ -101,13 +115,18 @@ static bool nfc_scene_saved_menu_on_event_slix(NfcApp* instance, uint32_t event)
|
||||
}
|
||||
|
||||
const NfcProtocolSupportBase nfc_protocol_support_slix = {
|
||||
.features = NfcProtocolFeatureEmulateFull,
|
||||
.features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo,
|
||||
|
||||
.scene_info =
|
||||
{
|
||||
.on_enter = nfc_scene_info_on_enter_slix,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
},
|
||||
.scene_more_info =
|
||||
{
|
||||
.on_enter = nfc_scene_more_info_on_enter_slix,
|
||||
.on_event = nfc_protocol_support_common_on_event_empty,
|
||||
},
|
||||
.scene_read =
|
||||
{
|
||||
.on_enter = nfc_scene_read_on_enter_slix,
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
#include "slix_render.h"
|
||||
|
||||
#include "../iso15693_3/iso15693_3_render.h"
|
||||
|
||||
void nfc_render_slix_info(const SlixData* data, NfcProtocolFormatType format_type, FuriString* str) {
|
||||
nfc_render_iso15693_3_brief(slix_get_base_data(data), str);
|
||||
|
||||
if(format_type != NfcProtocolFormatTypeFull) return;
|
||||
const SlixType slix_type = slix_get_type(data);
|
||||
|
||||
furi_string_cat(str, "\n\e#Passwords\n");
|
||||
furi_string_cat(str, "\n::::::::::::::::::[Passwords]:::::::::::::::::\n");
|
||||
|
||||
static const char* slix_password_names[] = {
|
||||
"Read",
|
||||
@@ -25,7 +23,7 @@ void nfc_render_slix_info(const SlixData* data, NfcProtocolFormatType format_typ
|
||||
}
|
||||
}
|
||||
|
||||
furi_string_cat(str, "\e#Lock bits\n");
|
||||
furi_string_cat(str, ":::::::::::::::::::[Lock bits]::::::::::::::::::::\n");
|
||||
|
||||
if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_EAS)) {
|
||||
furi_string_cat_printf(
|
||||
@@ -38,7 +36,7 @@ void nfc_render_slix_info(const SlixData* data, NfcProtocolFormatType format_typ
|
||||
|
||||
const SlixProtection protection = data->system_info.protection;
|
||||
|
||||
furi_string_cat(str, "\e#Page protection\n");
|
||||
furi_string_cat(str, "::::::::::::[Page protection]::::::::::::\n");
|
||||
furi_string_cat_printf(str, "Pointer: H >= %02X\n", protection.pointer);
|
||||
|
||||
const char* rh = (protection.condition & SLIX_PP_CONDITION_RH) ? "" : "un";
|
||||
@@ -52,12 +50,12 @@ void nfc_render_slix_info(const SlixData* data, NfcProtocolFormatType format_typ
|
||||
}
|
||||
|
||||
if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PRIVACY)) {
|
||||
furi_string_cat(str, "\e#Privacy\n");
|
||||
furi_string_cat(str, "::::::::::::::::::::[Privacy]::::::::::::::::::::::\n");
|
||||
furi_string_cat_printf(str, "Privacy mode: %sabled\n", data->privacy ? "en" : "dis");
|
||||
}
|
||||
|
||||
if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_SIGNATURE)) {
|
||||
furi_string_cat(str, "\e#Signature\n");
|
||||
furi_string_cat(str, ":::::::::::::::::::[Signature]::::::::::::::::::\n");
|
||||
for(uint32_t i = 0; i < 4; ++i) {
|
||||
furi_string_cat_printf(str, "%02X ", data->signature[i]);
|
||||
}
|
||||
|
||||
@@ -3,5 +3,6 @@
|
||||
#include <nfc/protocols/slix/slix.h>
|
||||
|
||||
#include "../nfc_protocol_support_render_common.h"
|
||||
#include "../iso15693_3/iso15693_3_render.h"
|
||||
|
||||
void nfc_render_slix_info(const SlixData* data, NfcProtocolFormatType format_type, FuriString* str);
|
||||
|
||||
@@ -13,6 +13,7 @@ static void nfc_scene_info_on_enter_st25tb(NfcApp* instance) {
|
||||
const St25tbData* data = nfc_device_get_data(device, NfcProtocolSt25tb);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
nfc_append_filename_string_when_present(instance, temp_str);
|
||||
furi_string_cat_printf(
|
||||
temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull));
|
||||
nfc_render_st25tb_info(data, NfcProtocolFormatTypeFull, temp_str);
|
||||
@@ -60,8 +61,8 @@ static void nfc_scene_read_success_on_enter_st25tb(NfcApp* instance) {
|
||||
furi_string_free(temp_str);
|
||||
}
|
||||
|
||||
static bool nfc_scene_saved_menu_on_event_st25tb(NfcApp* instance, uint32_t event) {
|
||||
if(event == SubmenuIndexCommonEdit) {
|
||||
static bool nfc_scene_saved_menu_on_event_st25tb(NfcApp* instance, SceneManagerEvent event) {
|
||||
if(event.type == SceneManagerEventTypeCustom && event.event == SubmenuIndexCommonEdit) {
|
||||
scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "nfc_app_i.h"
|
||||
#include "helpers/protocol_support/nfc_protocol_support.h"
|
||||
|
||||
#include <dolphin/dolphin.h>
|
||||
#include <applications/main/archive/helpers/archive_helpers_ext.h>
|
||||
@@ -446,6 +447,15 @@ void nfc_app_reset_detected_protocols(NfcApp* instance) {
|
||||
instance->protocols_detected_num = 0;
|
||||
}
|
||||
|
||||
void nfc_append_filename_string_when_present(NfcApp* instance, FuriString* string) {
|
||||
furi_assert(instance);
|
||||
furi_assert(string);
|
||||
|
||||
if(!furi_string_empty(instance->file_name)) {
|
||||
furi_string_cat_printf(string, "Name:%s\n", furi_string_get_cstr(instance->file_name));
|
||||
}
|
||||
}
|
||||
|
||||
static bool nfc_is_hal_ready() {
|
||||
if(furi_hal_nfc_is_hal_ready() != FuriHalNfcErrorNone) {
|
||||
// No connection to the chip, show an error screen
|
||||
@@ -467,6 +477,15 @@ static bool nfc_is_hal_ready() {
|
||||
}
|
||||
}
|
||||
|
||||
static void nfc_show_initial_scene_for_device(NfcApp* nfc) {
|
||||
NfcProtocol prot = nfc_device_get_protocol(nfc->nfc_device);
|
||||
uint32_t scene = nfc_protocol_support_has_feature(
|
||||
prot, NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEmulateUid) ?
|
||||
NfcSceneEmulate :
|
||||
NfcSceneSavedMenu;
|
||||
scene_manager_next_scene(nfc->scene_manager, scene);
|
||||
}
|
||||
|
||||
int32_t nfc_app(void* p) {
|
||||
if(!nfc_is_hal_ready()) return 0;
|
||||
|
||||
@@ -487,7 +506,7 @@ int32_t nfc_app(void* p) {
|
||||
|
||||
furi_string_set(nfc->file_path, args);
|
||||
if(nfc_load_file(nfc, nfc->file_path, false)) {
|
||||
scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulate);
|
||||
nfc_show_initial_scene_for_device(nfc);
|
||||
} else {
|
||||
view_dispatcher_stop(nfc->view_dispatcher);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "helpers/mf_ultralight_auth.h"
|
||||
#include "helpers/mf_user_dict.h"
|
||||
#include "helpers/mfkey32_logger.h"
|
||||
#include "helpers/nfc_emv_parser.h"
|
||||
#include "helpers/mf_classic_key_cache.h"
|
||||
#include "helpers/nfc_supported_cards.h"
|
||||
|
||||
@@ -192,3 +193,5 @@ void nfc_make_app_folder(NfcApp* instance);
|
||||
void nfc_app_set_detected_protocols(NfcApp* instance, const NfcProtocol* types, uint32_t count);
|
||||
|
||||
void nfc_app_reset_detected_protocols(NfcApp* instance);
|
||||
|
||||
void nfc_append_filename_string_when_present(NfcApp* instance, FuriString* string);
|
||||
|
||||
134
applications/main/nfc/plugins/supported_cards/emv.c
Normal file
134
applications/main/nfc/plugins/supported_cards/emv.c
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Parser for EMV cards.
|
||||
*
|
||||
* Copyright 2023 Leptoptilos <leptoptilos@icloud.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "core/string.h"
|
||||
#include "furi_hal_rtc.h"
|
||||
#include "helpers/nfc_emv_parser.h"
|
||||
#include "nfc_supported_card_plugin.h"
|
||||
|
||||
#include "protocols/emv/emv.h"
|
||||
#include "protocols/nfc_protocol.h"
|
||||
#include <flipper_application/flipper_application.h>
|
||||
|
||||
#include <nfc/nfc_device.h>
|
||||
#include <nfc/helpers/nfc_util.h>
|
||||
|
||||
#define TAG "EMV"
|
||||
|
||||
bool emv_get_currency_name(uint16_t cur_code, FuriString* currency_name) {
|
||||
if(!cur_code) return false;
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
bool succsess = nfc_emv_parser_get_currency_name(storage, cur_code, currency_name);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
return succsess;
|
||||
}
|
||||
|
||||
bool emv_get_country_name(uint16_t country_code, FuriString* country_name) {
|
||||
if(!country_code) return false;
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
bool succsess = nfc_emv_parser_get_country_name(storage, country_code, country_name);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
return succsess;
|
||||
}
|
||||
|
||||
bool emv_get_aid_name(const EmvApplication* apl, FuriString* aid_name) {
|
||||
const uint8_t len = apl->aid_len;
|
||||
|
||||
if(!len) return false;
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
bool succsess = nfc_emv_parser_get_aid_name(storage, apl->aid, len, aid_name);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
return succsess;
|
||||
}
|
||||
|
||||
static bool emv_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||
furi_assert(device);
|
||||
bool parsed = false;
|
||||
|
||||
const EmvData* data = nfc_device_get_data(device, NfcProtocolEmv);
|
||||
const EmvApplication app = data->emv_application;
|
||||
|
||||
do {
|
||||
if(app.name_found)
|
||||
furi_string_cat_printf(parsed_data, "\e#%s\n", app.name);
|
||||
else
|
||||
furi_string_cat_printf(parsed_data, "\e#%s\n", "EMV");
|
||||
|
||||
if(app.pan_len) {
|
||||
FuriString* pan = furi_string_alloc();
|
||||
for(uint8_t i = 0; i < app.pan_len; i += 2) {
|
||||
furi_string_cat_printf(pan, "%02X%02X ", app.pan[i], app.pan[i + 1]);
|
||||
}
|
||||
|
||||
// Cut padding 'F' from card number
|
||||
size_t end = furi_string_search_rchar(pan, 'F');
|
||||
if(end) furi_string_left(pan, end);
|
||||
furi_string_cat(parsed_data, pan);
|
||||
furi_string_free(pan);
|
||||
}
|
||||
|
||||
if(app.exp_month | app.exp_year)
|
||||
furi_string_cat_printf(parsed_data, "\nExp: %02X/%02X\n", app.exp_month, app.exp_year);
|
||||
|
||||
FuriString* str = furi_string_alloc();
|
||||
bool storage_readed = emv_get_country_name(app.country_code, str);
|
||||
|
||||
if(storage_readed)
|
||||
furi_string_cat_printf(parsed_data, "Country: %s\n", furi_string_get_cstr(str));
|
||||
|
||||
storage_readed = emv_get_currency_name(app.currency_code, str);
|
||||
if(storage_readed)
|
||||
furi_string_cat_printf(parsed_data, "Currency: %s\n", furi_string_get_cstr(str));
|
||||
|
||||
if(app.pin_try_counter != 0xFF)
|
||||
furi_string_cat_printf(parsed_data, "PIN try left: %d\n", app.pin_try_counter);
|
||||
|
||||
parsed = true;
|
||||
} while(false);
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
/* Actual implementation of app<>plugin interface */
|
||||
static const NfcSupportedCardsPlugin emv_plugin = {
|
||||
.protocol = NfcProtocolEmv,
|
||||
.verify = NULL,
|
||||
.read = NULL,
|
||||
.parse = emv_parse,
|
||||
};
|
||||
|
||||
/* Plugin descriptor to comply with basic plugin specification */
|
||||
static const FlipperAppPluginDescriptor emv_plugin_descriptor = {
|
||||
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID,
|
||||
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION,
|
||||
.entry_point = &emv_plugin,
|
||||
};
|
||||
|
||||
/* Plugin entry point - must return a pointer to const descriptor */
|
||||
const FlipperAppPluginDescriptor* emv_plugin_ep() {
|
||||
return &emv_plugin_descriptor;
|
||||
}
|
||||
@@ -147,7 +147,7 @@ static bool metromoney_parse(const NfcDevice* device, FuriString* parsed_data) {
|
||||
const uint8_t* block_start_ptr =
|
||||
&data->block[start_block_num + ticket_block_number].data[0];
|
||||
|
||||
uint32_t balance = nfc_util_bytes2num_little_endian(block_start_ptr, 4);
|
||||
uint32_t balance = nfc_util_bytes2num_little_endian(block_start_ptr, 4) - 100;
|
||||
|
||||
uint32_t balance_lari = balance / 100;
|
||||
uint8_t balance_tetri = balance % 100;
|
||||
|
||||
@@ -37,6 +37,8 @@ ADD_SCENE(nfc, mf_ultralight_capture_pass, MfUltralightCapturePass)
|
||||
ADD_SCENE(nfc, mf_desfire_more_info, MfDesfireMoreInfo)
|
||||
ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp)
|
||||
|
||||
ADD_SCENE(nfc, emv_more_info, EmvMoreInfo)
|
||||
|
||||
ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack)
|
||||
ADD_SCENE(nfc, mf_classic_detect_reader, MfClassicDetectReader)
|
||||
ADD_SCENE(nfc, mf_classic_mfkey_nonces_info, MfClassicMfkeyNoncesInfo)
|
||||
|
||||
74
applications/main/nfc/scenes/nfc_scene_emv_more_info.c
Normal file
74
applications/main/nfc/scenes/nfc_scene_emv_more_info.c
Normal file
@@ -0,0 +1,74 @@
|
||||
#include "../nfc_app_i.h"
|
||||
|
||||
#include "../helpers/protocol_support/nfc_protocol_support_gui_common.h"
|
||||
#include "../helpers/protocol_support/emv/emv_render.h"
|
||||
|
||||
enum {
|
||||
EmvMoreInfoStateMenu,
|
||||
EmvMoreInfoStateItem, // MUST be last, states >= this correspond with submenu index
|
||||
};
|
||||
|
||||
enum SubmenuIndex {
|
||||
SubmenuIndexTransactions,
|
||||
SubmenuIndexDynamic, // dynamic indices start here
|
||||
};
|
||||
|
||||
void nfc_scene_emv_more_info_on_enter(void* context) {
|
||||
NfcApp* nfc = context;
|
||||
Submenu* submenu = nfc->submenu;
|
||||
|
||||
text_box_set_font(nfc->text_box, TextBoxFontHex);
|
||||
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Transactions",
|
||||
SubmenuIndexTransactions,
|
||||
nfc_protocol_support_common_submenu_callback,
|
||||
nfc);
|
||||
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
|
||||
}
|
||||
|
||||
bool nfc_scene_emv_more_info_on_event(void* context, SceneManagerEvent event) {
|
||||
NfcApp* nfc = context;
|
||||
bool consumed = false;
|
||||
|
||||
const uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmvMoreInfo);
|
||||
const EmvData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolEmv);
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexTransactions) {
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
nfc_render_emv_transactions(&data->emv_application, temp_str);
|
||||
widget_add_text_scroll_element(
|
||||
nfc->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
|
||||
furi_string_free(temp_str);
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager,
|
||||
NfcSceneEmvMoreInfo,
|
||||
EmvMoreInfoStateItem + SubmenuIndexTransactions);
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event.type == SceneManagerEventTypeBack) {
|
||||
if(state >= EmvMoreInfoStateItem) {
|
||||
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
|
||||
scene_manager_set_scene_state(
|
||||
nfc->scene_manager, NfcSceneEmvMoreInfo, EmvMoreInfoStateMenu);
|
||||
} else {
|
||||
// Return directly to the Info scene
|
||||
scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneInfo);
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void nfc_scene_emv_more_info_on_exit(void* context) {
|
||||
NfcApp* nfc = context;
|
||||
|
||||
// Clear views
|
||||
widget_reset(nfc->widget);
|
||||
submenu_reset(nfc->submenu);
|
||||
}
|
||||
@@ -29,6 +29,8 @@ void nfc_scene_select_protocol_on_enter(void* context) {
|
||||
"%s %s",
|
||||
prefix,
|
||||
nfc_device_get_protocol_name(instance->protocols_detected[i]));
|
||||
|
||||
furi_string_replace_str(temp_str, "Mifare", "MIFARE");
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
furi_string_get_cstr(temp_str),
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 KiB |
BIN
assets/icons/NFC/NFC_dolphin_emulation_51x64.png
Normal file
BIN
assets/icons/NFC/NFC_dolphin_emulation_51x64.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
@@ -22,6 +22,7 @@ env.Append(
|
||||
File("protocols/mf_ultralight/mf_ultralight.h"),
|
||||
File("protocols/mf_classic/mf_classic.h"),
|
||||
File("protocols/mf_desfire/mf_desfire.h"),
|
||||
File("protocols/emv/emv.h"),
|
||||
File("protocols/slix/slix.h"),
|
||||
File("protocols/st25tb/st25tb.h"),
|
||||
# Pollers
|
||||
@@ -32,6 +33,7 @@ env.Append(
|
||||
File("protocols/mf_ultralight/mf_ultralight_poller.h"),
|
||||
File("protocols/mf_classic/mf_classic_poller.h"),
|
||||
File("protocols/mf_desfire/mf_desfire_poller.h"),
|
||||
File("protocols/emv/emv_poller.h"),
|
||||
File("protocols/st25tb/st25tb_poller.h"),
|
||||
# Listeners
|
||||
File("protocols/iso14443_3a/iso14443_3a_listener.h"),
|
||||
|
||||
@@ -7,6 +7,18 @@
|
||||
#define ISO14443_4_BLOCK_PCB_R (5U << 5)
|
||||
#define ISO14443_4_BLOCK_PCB_S (3U << 6)
|
||||
|
||||
#define ISO14443_4_BLOCK_PCB_I_ (0U << 6)
|
||||
#define ISO14443_4_BLOCK_PCB_R_ (2U << 6)
|
||||
#define ISO14443_4_BLOCK_PCB_TYPE_MASK (3U << 6)
|
||||
|
||||
#define ISO14443_4_BLOCK_PCB_S_DESELECT (0U << 4)
|
||||
#define ISO14443_4_BLOCK_PCB_S_WTX (3U << 4)
|
||||
#define ISO14443_4_BLOCK_PCB_BLOCK_NUMBER (1U << 0)
|
||||
|
||||
#define ISO14443_4_BLOCK_PCB_NAD (1U << 2)
|
||||
#define ISO14443_4_BLOCK_PCB_CID (1U << 3)
|
||||
#define ISO14443_4_BLOCK_PCB_CHAINING (1U << 4)
|
||||
|
||||
struct Iso14443_4Layer {
|
||||
uint8_t pcb;
|
||||
uint8_t pcb_prev;
|
||||
@@ -62,3 +74,56 @@ bool iso14443_4_layer_decode_block(
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Iso14443_4aError iso14443_4_layer_decode_block_pwt_ext(
|
||||
Iso14443_4Layer* instance,
|
||||
BitBuffer* output_data,
|
||||
const BitBuffer* block_data) {
|
||||
furi_assert(instance);
|
||||
|
||||
Iso14443_4aError ret = Iso14443_4aErrorProtocol;
|
||||
bit_buffer_reset(output_data);
|
||||
|
||||
do {
|
||||
const uint8_t pcb_field = bit_buffer_get_byte(block_data, 0);
|
||||
const uint8_t block_type = pcb_field & ISO14443_4_BLOCK_PCB_TYPE_MASK;
|
||||
switch(block_type) {
|
||||
case ISO14443_4_BLOCK_PCB_I_:
|
||||
if(pcb_field == instance->pcb_prev) {
|
||||
bit_buffer_copy_right(output_data, block_data, 1);
|
||||
ret = Iso14443_4aErrorNone;
|
||||
} else {
|
||||
// send original request again
|
||||
ret = Iso14443_4aErrorSendExtra;
|
||||
}
|
||||
break;
|
||||
case ISO14443_4_BLOCK_PCB_R_:
|
||||
// TODO
|
||||
break;
|
||||
case ISO14443_4_BLOCK_PCB_S:
|
||||
if((pcb_field & ISO14443_4_BLOCK_PCB_S_WTX) == ISO14443_4_BLOCK_PCB_S_WTX) {
|
||||
const uint8_t inf_field = bit_buffer_get_byte(block_data, 1);
|
||||
//const uint8_t power_level = inf_field >> 6;
|
||||
const uint8_t wtxm = inf_field & 0b111111;
|
||||
//uint32_t fwt_temp = MIN((fwt * wtxm), fwt_max);
|
||||
|
||||
bit_buffer_append_byte(
|
||||
output_data,
|
||||
ISO14443_4_BLOCK_PCB_S | ISO14443_4_BLOCK_PCB_S_WTX | ISO14443_4_BLOCK_PCB);
|
||||
bit_buffer_append_byte(output_data, wtxm);
|
||||
ret = Iso14443_4aErrorSendExtra;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
if(ret != Iso14443_4aErrorNone) {
|
||||
FURI_LOG_RAW_T("RAW RX:");
|
||||
for(size_t x = 0; x < bit_buffer_get_size_bytes(block_data); x++) {
|
||||
FURI_LOG_RAW_T("%02X ", bit_buffer_get_byte(block_data, x));
|
||||
}
|
||||
FURI_LOG_RAW_T("\r\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "protocols/iso14443_4a/iso14443_4a.h"
|
||||
#include <toolbox/bit_buffer.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -24,6 +25,11 @@ bool iso14443_4_layer_decode_block(
|
||||
BitBuffer* output_data,
|
||||
const BitBuffer* block_data);
|
||||
|
||||
Iso14443_4aError iso14443_4_layer_decode_block_pwt_ext(
|
||||
Iso14443_4Layer* instance,
|
||||
BitBuffer* output_data,
|
||||
const BitBuffer* block_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
188
lib/nfc/protocols/emv/emv.c
Normal file
188
lib/nfc/protocols/emv/emv.c
Normal file
@@ -0,0 +1,188 @@
|
||||
//#include "emv_i.h"
|
||||
|
||||
#include "flipper_format.h"
|
||||
#include <core/common_defines.h>
|
||||
#include "protocols/emv/emv.h"
|
||||
#include <furi.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define EMV_PROTOCOL_NAME "EMV"
|
||||
|
||||
const NfcDeviceBase nfc_device_emv = {
|
||||
.protocol_name = EMV_PROTOCOL_NAME,
|
||||
.alloc = (NfcDeviceAlloc)emv_alloc,
|
||||
.free = (NfcDeviceFree)emv_free,
|
||||
.reset = (NfcDeviceReset)emv_reset,
|
||||
.copy = (NfcDeviceCopy)emv_copy,
|
||||
.verify = (NfcDeviceVerify)emv_verify,
|
||||
.load = (NfcDeviceLoad)emv_load,
|
||||
.save = (NfcDeviceSave)emv_save,
|
||||
.is_equal = (NfcDeviceEqual)emv_is_equal,
|
||||
.get_name = (NfcDeviceGetName)emv_get_device_name,
|
||||
.get_uid = (NfcDeviceGetUid)emv_get_uid,
|
||||
.set_uid = (NfcDeviceSetUid)emv_set_uid,
|
||||
.get_base_data = (NfcDeviceGetBaseData)emv_get_base_data,
|
||||
};
|
||||
|
||||
EmvData* emv_alloc() {
|
||||
EmvData* data = malloc(sizeof(EmvData));
|
||||
data->iso14443_4a_data = iso14443_4a_alloc();
|
||||
data->emv_application.pin_try_counter = 0xff;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void emv_free(EmvData* data) {
|
||||
furi_assert(data);
|
||||
|
||||
emv_reset(data);
|
||||
iso14443_4a_free(data->iso14443_4a_data);
|
||||
free(data);
|
||||
}
|
||||
|
||||
void emv_reset(EmvData* data) {
|
||||
furi_assert(data);
|
||||
|
||||
iso14443_4a_reset(data->iso14443_4a_data);
|
||||
|
||||
memset(&data->emv_application, 0, sizeof(EmvApplication));
|
||||
}
|
||||
|
||||
void emv_copy(EmvData* destination, const EmvData* source) {
|
||||
furi_assert(destination);
|
||||
furi_assert(source);
|
||||
|
||||
emv_reset(destination);
|
||||
|
||||
iso14443_4a_copy(destination->iso14443_4a_data, source->iso14443_4a_data);
|
||||
destination->emv_application = source->emv_application;
|
||||
}
|
||||
|
||||
bool emv_verify(EmvData* data, const FuriString* device_type) {
|
||||
UNUSED(data);
|
||||
return furi_string_equal_str(device_type, EMV_PROTOCOL_NAME);
|
||||
}
|
||||
|
||||
bool emv_load(EmvData* data, FlipperFormat* ff, uint32_t version) {
|
||||
furi_assert(data);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
bool parsed = false;
|
||||
|
||||
do {
|
||||
// Read ISO14443_4A data
|
||||
if(!iso14443_4a_load(data->iso14443_4a_data, ff, version)) break;
|
||||
|
||||
EmvApplication* app = &data->emv_application;
|
||||
|
||||
//Read name
|
||||
if(!flipper_format_read_string(ff, "Name", temp_str)) break;
|
||||
strcpy(app->name, furi_string_get_cstr(temp_str));
|
||||
if(app->name[0] != '\0') app->name_found = true;
|
||||
|
||||
uint32_t pan_len;
|
||||
if(!flipper_format_read_uint32(ff, "PAN length", &pan_len, 1)) break;
|
||||
app->pan_len = pan_len;
|
||||
|
||||
if(!flipper_format_read_hex(ff, "PAN", app->pan, pan_len)) break;
|
||||
|
||||
uint32_t aid_len;
|
||||
if(!flipper_format_read_uint32(ff, "AID length", &aid_len, 1)) break;
|
||||
app->aid_len = aid_len;
|
||||
|
||||
if(!flipper_format_read_hex(ff, "AID", app->aid, aid_len)) break;
|
||||
|
||||
if(!flipper_format_read_hex(ff, "Country code", (uint8_t*)&app->country_code, 2)) break;
|
||||
|
||||
if(!flipper_format_read_hex(ff, "Currency code", (uint8_t*)&app->currency_code, 2)) break;
|
||||
|
||||
if(!flipper_format_read_hex(ff, "Expiration year", &app->exp_year, 1)) break;
|
||||
if(!flipper_format_read_hex(ff, "Expiration month", &app->exp_month, 1)) break;
|
||||
|
||||
uint32_t pin_try_counter;
|
||||
if(!flipper_format_read_uint32(ff, "PIN counter", &pin_try_counter, 1)) break;
|
||||
app->pin_try_counter = pin_try_counter;
|
||||
|
||||
parsed = true;
|
||||
} while(false);
|
||||
|
||||
furi_string_free(temp_str);
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
bool emv_save(const EmvData* data, FlipperFormat* ff) {
|
||||
furi_assert(data);
|
||||
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
bool saved = false;
|
||||
|
||||
do {
|
||||
EmvApplication app = data->emv_application;
|
||||
if(!iso14443_4a_save(data->iso14443_4a_data, ff)) break;
|
||||
|
||||
if(!flipper_format_write_comment_cstr(ff, "EMV specific data:\n")) break;
|
||||
|
||||
if(!flipper_format_write_string_cstr(ff, "Name", app.name)) break;
|
||||
|
||||
uint32_t pan_len = app.pan_len;
|
||||
if(!flipper_format_write_uint32(ff, "PAN length", &pan_len, 1)) break;
|
||||
|
||||
if(!flipper_format_write_hex(ff, "PAN", app.pan, pan_len)) break;
|
||||
|
||||
uint32_t aid_len = app.aid_len;
|
||||
if(!flipper_format_write_uint32(ff, "AID length", &aid_len, 1)) break;
|
||||
|
||||
if(!flipper_format_write_hex(ff, "AID", app.aid, aid_len)) break;
|
||||
|
||||
if(!flipper_format_write_hex(ff, "Country code", (uint8_t*)&app.country_code, 2)) break;
|
||||
|
||||
if(!flipper_format_write_hex(ff, "Currency code", (uint8_t*)&app.currency_code, 2)) break;
|
||||
|
||||
if(!flipper_format_write_hex(ff, "Expiration year", (uint8_t*)&app.exp_year, 1)) break;
|
||||
|
||||
if(!flipper_format_write_hex(ff, "Expiration month", (uint8_t*)&app.exp_month, 1)) break;
|
||||
|
||||
if(!flipper_format_write_uint32(ff, "PIN counter", (uint32_t*)&app.pin_try_counter, 1))
|
||||
break;
|
||||
|
||||
saved = true;
|
||||
} while(false);
|
||||
|
||||
furi_string_free(temp_str);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
bool emv_is_equal(const EmvData* data, const EmvData* other) {
|
||||
furi_assert(data);
|
||||
furi_assert(other);
|
||||
|
||||
return iso14443_4a_is_equal(data->iso14443_4a_data, other->iso14443_4a_data) &&
|
||||
memcmp(&data->emv_application, &other->emv_application, sizeof(EmvApplication)) == 0;
|
||||
}
|
||||
|
||||
const char* emv_get_device_name(const EmvData* data, NfcDeviceNameType name_type) {
|
||||
UNUSED(data);
|
||||
UNUSED(name_type);
|
||||
return EMV_PROTOCOL_NAME;
|
||||
}
|
||||
|
||||
const uint8_t* emv_get_uid(const EmvData* data, size_t* uid_len) {
|
||||
furi_assert(data);
|
||||
|
||||
return iso14443_4a_get_uid(data->iso14443_4a_data, uid_len);
|
||||
}
|
||||
|
||||
bool emv_set_uid(EmvData* data, const uint8_t* uid, size_t uid_len) {
|
||||
furi_assert(data);
|
||||
|
||||
return iso14443_4a_set_uid(data->iso14443_4a_data, uid, uid_len);
|
||||
}
|
||||
|
||||
Iso14443_4aData* emv_get_base_data(const EmvData* data) {
|
||||
furi_assert(data);
|
||||
|
||||
return data->iso14443_4a_data;
|
||||
}
|
||||
136
lib/nfc/protocols/emv/emv.h
Normal file
136
lib/nfc/protocols/emv/emv.h
Normal file
@@ -0,0 +1,136 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MAX_APDU_LEN 255
|
||||
|
||||
#define EMV_REQ_GET_DATA 0x80CA
|
||||
|
||||
#define EMV_TAG_APP_TEMPLATE 0x61
|
||||
#define EMV_TAG_AID 0x4F
|
||||
#define EMV_TAG_PRIORITY 0x87
|
||||
#define EMV_TAG_PDOL 0x9F38
|
||||
#define EMV_TAG_CARD_NAME 0x50
|
||||
#define EMV_TAG_FCI 0xBF0C
|
||||
#define EMV_TAG_PIN_TRY_COUNTER 0x9F17
|
||||
#define EMV_TAG_LOG_ENTRY 0x9F4D
|
||||
#define EMV_TAG_LOG_FMT 0x9F4F
|
||||
|
||||
#define EMV_TAG_LAST_ONLINE_ATC 0x9F13
|
||||
#define EMV_TAG_ATC 0x9F36
|
||||
#define EMV_TAG_LOG_AMOUNT 0x9F02
|
||||
#define EMV_TAG_LOG_COUNTRY 0x9F1A
|
||||
#define EMV_TAG_LOG_CURRENCY 0x5F2A
|
||||
#define EMV_TAG_LOG_DATE 0x9A
|
||||
#define EMV_TAG_LOG_TIME 0x9F21
|
||||
|
||||
#define EMV_TAG_TRACK_1_EQUIV 0x56
|
||||
#define EMV_TAG_TRACK_2_EQUIV 0x57
|
||||
#define EMV_TAG_PAN 0x5A
|
||||
#define EMV_TAG_AFL 0x94
|
||||
#define EMV_TAG_EXP_DATE 0x5F24
|
||||
#define EMV_TAG_COUNTRY_CODE 0x5F28
|
||||
#define EMV_TAG_CURRENCY_CODE 0x9F42
|
||||
#define EMV_TAG_CARDHOLDER_NAME 0x5F20
|
||||
#define EMV_TAG_TRACK_2_DATA 0x9F6B
|
||||
#define EMV_TAG_GPO_FMT1 0x80
|
||||
|
||||
#define EMV_TAG_RESP_BUF_SIZE 0x6C
|
||||
#define EMV_TAG_RESP_BYTES_AVAILABLE 0x61
|
||||
|
||||
typedef struct {
|
||||
uint16_t tag;
|
||||
uint8_t data[];
|
||||
} PDOLValue;
|
||||
|
||||
typedef struct {
|
||||
uint8_t size;
|
||||
uint8_t data[MAX_APDU_LEN];
|
||||
} APDU;
|
||||
|
||||
typedef struct {
|
||||
uint16_t atc;
|
||||
uint64_t amount;
|
||||
uint16_t country;
|
||||
uint16_t currency;
|
||||
uint32_t date;
|
||||
uint32_t time;
|
||||
} Transaction;
|
||||
|
||||
typedef struct {
|
||||
uint8_t log_sfi;
|
||||
uint8_t log_records;
|
||||
uint8_t log_fmt[50];
|
||||
uint8_t log_fmt_len;
|
||||
uint8_t active_tr;
|
||||
bool saving_trans_list;
|
||||
Transaction trans[16];
|
||||
uint8_t priority;
|
||||
uint8_t aid[16];
|
||||
uint8_t aid_len;
|
||||
char name[32];
|
||||
bool name_found;
|
||||
uint8_t pan[10]; // card_number
|
||||
uint8_t pan_len;
|
||||
uint8_t exp_month;
|
||||
uint8_t exp_year;
|
||||
uint16_t country_code;
|
||||
uint16_t currency_code;
|
||||
uint8_t pin_try_counter;
|
||||
uint16_t transaction_counter;
|
||||
uint16_t last_online_atc;
|
||||
APDU pdol;
|
||||
APDU afl;
|
||||
} EmvApplication;
|
||||
|
||||
typedef enum {
|
||||
EmvErrorNone = 0,
|
||||
EmvErrorNotPresent,
|
||||
EmvErrorProtocol,
|
||||
EmvErrorTimeout,
|
||||
} EmvError;
|
||||
|
||||
typedef struct {
|
||||
Iso14443_4aData* iso14443_4a_data;
|
||||
EmvApplication emv_application;
|
||||
} EmvData;
|
||||
|
||||
extern const NfcDeviceBase nfc_device_emv;
|
||||
|
||||
// Virtual methods
|
||||
|
||||
EmvData* emv_alloc();
|
||||
|
||||
void emv_free(EmvData* data);
|
||||
|
||||
void emv_reset(EmvData* data);
|
||||
|
||||
void emv_copy(EmvData* data, const EmvData* other);
|
||||
|
||||
bool emv_verify(EmvData* data, const FuriString* device_type);
|
||||
|
||||
bool emv_load(EmvData* data, FlipperFormat* ff, uint32_t version);
|
||||
|
||||
bool emv_save(const EmvData* data, FlipperFormat* ff);
|
||||
|
||||
bool emv_is_equal(const EmvData* data, const EmvData* other);
|
||||
|
||||
const char* emv_get_device_name(const EmvData* data, NfcDeviceNameType name_type);
|
||||
|
||||
const uint8_t* emv_get_uid(const EmvData* data, size_t* uid_len);
|
||||
|
||||
bool emv_set_uid(EmvData* data, const uint8_t* uid, size_t uid_len);
|
||||
|
||||
Iso14443_4aData* emv_get_base_data(const EmvData* data);
|
||||
|
||||
// Getters and tests
|
||||
|
||||
//const EmvApplication* emv_get_application(const EmvData* data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
202
lib/nfc/protocols/emv/emv_poller.c
Normal file
202
lib/nfc/protocols/emv/emv_poller.c
Normal file
@@ -0,0 +1,202 @@
|
||||
#include "emv_poller_i.h"
|
||||
|
||||
#include <nfc/protocols/nfc_poller_base.h>
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define TAG "EMVPoller"
|
||||
|
||||
// MAX Le is 255 bytes + 2 for CRC
|
||||
#define EMV_BUF_SIZE (512U)
|
||||
|
||||
typedef NfcCommand (*EmvPollerReadHandler)(EmvPoller* instance);
|
||||
|
||||
const EmvData* emv_poller_get_data(EmvPoller* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
return instance->data;
|
||||
}
|
||||
|
||||
static EmvPoller* emv_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller) {
|
||||
EmvPoller* instance = malloc(sizeof(EmvPoller));
|
||||
instance->iso14443_4a_poller = iso14443_4a_poller;
|
||||
instance->data = emv_alloc();
|
||||
instance->tx_buffer = bit_buffer_alloc(EMV_BUF_SIZE);
|
||||
instance->rx_buffer = bit_buffer_alloc(EMV_BUF_SIZE);
|
||||
|
||||
instance->state = EmvPollerStateIdle;
|
||||
|
||||
instance->emv_event.data = &instance->emv_event_data;
|
||||
|
||||
instance->general_event.protocol = NfcProtocolEmv;
|
||||
instance->general_event.event_data = &instance->emv_event;
|
||||
instance->general_event.instance = instance;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void emv_poller_free(EmvPoller* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
emv_free(instance->data);
|
||||
bit_buffer_free(instance->tx_buffer);
|
||||
bit_buffer_free(instance->rx_buffer);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static NfcCommand emv_poller_handler_idle(EmvPoller* instance) {
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_reset(instance->rx_buffer);
|
||||
|
||||
iso14443_4a_copy(
|
||||
instance->data->iso14443_4a_data,
|
||||
iso14443_4a_poller_get_data(instance->iso14443_4a_poller));
|
||||
|
||||
instance->state = EmvPollerStateSelectPPSE;
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
static NfcCommand emv_poller_handler_select_ppse(EmvPoller* instance) {
|
||||
instance->error = emv_poller_select_ppse(instance);
|
||||
|
||||
if(instance->error == EmvErrorNone) {
|
||||
FURI_LOG_D(TAG, "Select PPSE success");
|
||||
instance->state = EmvPollerStateSelectApplication;
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Failed to select PPSE");
|
||||
instance->state = EmvPollerStateReadFailed;
|
||||
}
|
||||
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
static NfcCommand emv_poller_handler_select_application(EmvPoller* instance) {
|
||||
instance->error = emv_poller_select_application(instance);
|
||||
|
||||
if(instance->error == EmvErrorNone) {
|
||||
FURI_LOG_D(TAG, "Select application success");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Failed to select application");
|
||||
// We have to try GPO request with empty tag
|
||||
}
|
||||
instance->state = EmvPollerStateGetProcessingOptions;
|
||||
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
static NfcCommand emv_poller_handler_get_processing_options(EmvPoller* instance) {
|
||||
instance->error = emv_poller_get_processing_options(instance);
|
||||
|
||||
if(instance->error == EmvErrorNone) {
|
||||
FURI_LOG_D(TAG, "Get processing options success");
|
||||
} else {
|
||||
FURI_LOG_E(TAG, "Failed to get processing options");
|
||||
}
|
||||
|
||||
// Read another informations
|
||||
instance->state = EmvPollerStateReadFiles;
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
static NfcCommand emv_poller_handler_read_files(EmvPoller* instance) {
|
||||
emv_poller_read_afl(instance);
|
||||
emv_poller_read_log_entry(instance);
|
||||
|
||||
instance->state = EmvPollerStateReadExtra;
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
static NfcCommand emv_poller_handler_read_extra_data(EmvPoller* instance) {
|
||||
emv_poller_get_last_online_atc(instance);
|
||||
emv_poller_get_pin_try_counter(instance);
|
||||
|
||||
instance->state = EmvPollerStateReadSuccess;
|
||||
return NfcCommandContinue;
|
||||
}
|
||||
|
||||
static NfcCommand emv_poller_handler_read_fail(EmvPoller* instance) {
|
||||
FURI_LOG_D(TAG, "Read failed");
|
||||
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
|
||||
instance->emv_event.data->error = instance->error;
|
||||
NfcCommand command = instance->callback(instance->general_event, instance->context);
|
||||
instance->state = EmvPollerStateIdle;
|
||||
return command;
|
||||
}
|
||||
|
||||
static NfcCommand emv_poller_handler_read_success(EmvPoller* instance) {
|
||||
FURI_LOG_D(TAG, "Read success");
|
||||
iso14443_4a_poller_halt(instance->iso14443_4a_poller);
|
||||
instance->emv_event.type = EmvPollerEventTypeReadSuccess;
|
||||
NfcCommand command = instance->callback(instance->general_event, instance->context);
|
||||
return command;
|
||||
}
|
||||
|
||||
static const EmvPollerReadHandler emv_poller_read_handler[EmvPollerStateNum] = {
|
||||
[EmvPollerStateIdle] = emv_poller_handler_idle,
|
||||
[EmvPollerStateSelectPPSE] = emv_poller_handler_select_ppse,
|
||||
[EmvPollerStateSelectApplication] = emv_poller_handler_select_application,
|
||||
[EmvPollerStateGetProcessingOptions] = emv_poller_handler_get_processing_options,
|
||||
[EmvPollerStateReadFiles] = emv_poller_handler_read_files,
|
||||
[EmvPollerStateReadExtra] = emv_poller_handler_read_extra_data,
|
||||
[EmvPollerStateReadFailed] = emv_poller_handler_read_fail,
|
||||
[EmvPollerStateReadSuccess] = emv_poller_handler_read_success,
|
||||
};
|
||||
|
||||
static void
|
||||
emv_poller_set_callback(EmvPoller* instance, NfcGenericCallback callback, void* context) {
|
||||
furi_assert(instance);
|
||||
furi_assert(callback);
|
||||
|
||||
instance->callback = callback;
|
||||
instance->context = context;
|
||||
}
|
||||
|
||||
static NfcCommand emv_poller_run(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.protocol == NfcProtocolIso14443_4a);
|
||||
|
||||
EmvPoller* instance = context;
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->callback);
|
||||
|
||||
const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;
|
||||
furi_assert(iso14443_4a_event);
|
||||
|
||||
NfcCommand command = NfcCommandContinue;
|
||||
|
||||
if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {
|
||||
command = emv_poller_read_handler[instance->state](instance);
|
||||
} else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) {
|
||||
instance->emv_event.type = EmvPollerEventTypeReadFailed;
|
||||
command = instance->callback(instance->general_event, instance->context);
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
static bool emv_poller_detect(NfcGenericEvent event, void* context) {
|
||||
furi_assert(event.protocol == NfcProtocolIso14443_4a);
|
||||
|
||||
EmvPoller* instance = context;
|
||||
furi_assert(instance);
|
||||
|
||||
const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data;
|
||||
furi_assert(iso14443_4a_event);
|
||||
|
||||
bool protocol_detected = false;
|
||||
|
||||
if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) {
|
||||
const EmvError error = emv_poller_select_ppse(instance);
|
||||
protocol_detected = (error == EmvErrorNone);
|
||||
}
|
||||
|
||||
return protocol_detected;
|
||||
}
|
||||
|
||||
const NfcPollerBase emv_poller = {
|
||||
.alloc = (NfcPollerAlloc)emv_poller_alloc,
|
||||
.free = (NfcPollerFree)emv_poller_free,
|
||||
.set_callback = (NfcPollerSetCallback)emv_poller_set_callback,
|
||||
.run = (NfcPollerRun)emv_poller_run,
|
||||
.detect = (NfcPollerDetect)emv_poller_detect,
|
||||
.get_data = (NfcPollerGetData)emv_poller_get_data,
|
||||
};
|
||||
59
lib/nfc/protocols/emv/emv_poller.h
Normal file
59
lib/nfc/protocols/emv/emv_poller.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include "emv.h"
|
||||
|
||||
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief EmvPoller opaque type definition.
|
||||
*/
|
||||
typedef struct EmvPoller EmvPoller;
|
||||
|
||||
/**
|
||||
* @brief Enumeration of possible Emv poller event types.
|
||||
*/
|
||||
typedef enum {
|
||||
EmvPollerEventTypeReadSuccess, /**< Card was read successfully. */
|
||||
EmvPollerEventTypeReadFailed, /**< Poller failed to read card. */
|
||||
} EmvPollerEventType;
|
||||
|
||||
/**
|
||||
* @brief Emv poller event data.
|
||||
*/
|
||||
typedef union {
|
||||
EmvError error; /**< Error code indicating card reading fail reason. */
|
||||
} EmvPollerEventData;
|
||||
|
||||
/**
|
||||
* @brief Emv poller event structure.
|
||||
*
|
||||
* Upon emission of an event, an instance of this struct will be passed to the callback.
|
||||
*/
|
||||
typedef struct {
|
||||
EmvPollerEventType type; /**< Type of emmitted event. */
|
||||
EmvPollerEventData* data; /**< Pointer to event specific data. */
|
||||
} EmvPollerEvent;
|
||||
|
||||
EmvError emv_poller_select_ppse(EmvPoller* instance);
|
||||
|
||||
EmvError emv_poller_select_application(EmvPoller* instance);
|
||||
|
||||
EmvError emv_poller_get_processing_options(EmvPoller* instance);
|
||||
|
||||
EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t record_num);
|
||||
|
||||
EmvError emv_poller_read_afl(EmvPoller* instance);
|
||||
|
||||
EmvError emv_poller_read_log_entry(EmvPoller* instance);
|
||||
|
||||
EmvError emv_poller_get_pin_try_counter(EmvPoller* instance);
|
||||
|
||||
EmvError emv_poller_get_last_online_atc(EmvPoller* instance);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
5
lib/nfc/protocols/emv/emv_poller_defs.h
Normal file
5
lib/nfc/protocols/emv/emv_poller_defs.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <nfc/protocols/nfc_poller_base.h>
|
||||
|
||||
extern const NfcPollerBase emv_poller;
|
||||
727
lib/nfc/protocols/emv/emv_poller_i.c
Normal file
727
lib/nfc/protocols/emv/emv_poller_i.c
Normal file
@@ -0,0 +1,727 @@
|
||||
#include "emv_poller_i.h"
|
||||
#include "protocols/emv/emv.h"
|
||||
|
||||
#define TAG "EMVPoller"
|
||||
|
||||
const PDOLValue pdol_term_info = {0x9F59, {0xC8, 0x80, 0x00}}; // Terminal transaction information
|
||||
const PDOLValue pdol_term_type = {0x9F5A, {0x00}}; // Terminal transaction type
|
||||
const PDOLValue pdol_merchant_type = {0x9F58, {0x01}}; // Merchant type indicator
|
||||
const PDOLValue pdol_term_trans_qualifies = {
|
||||
0x9F66,
|
||||
{0x79, 0x00, 0x40, 0x80}}; // Terminal transaction qualifiers
|
||||
const PDOLValue pdol_addtnl_term_qualifies = {
|
||||
0x9F40,
|
||||
{0x79, 0x00, 0x40, 0x80}}; // Terminal transaction qualifiers
|
||||
const PDOLValue pdol_amount_authorise = {
|
||||
0x9F02,
|
||||
{0x00, 0x00, 0x00, 0x10, 0x00, 0x00}}; // Amount, authorised
|
||||
const PDOLValue pdol_amount = {0x9F03, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; // Amount
|
||||
const PDOLValue pdol_country_code = {0x9F1A, {0x01, 0x24}}; // Terminal country code
|
||||
const PDOLValue pdol_currency_code = {0x5F2A, {0x01, 0x24}}; // Transaction currency code
|
||||
const PDOLValue pdol_term_verification = {
|
||||
0x95,
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00}}; // Terminal verification results
|
||||
const PDOLValue pdol_transaction_date = {0x9A, {0x19, 0x01, 0x01}}; // Transaction date
|
||||
const PDOLValue pdol_transaction_type = {0x9C, {0x00}}; // Transaction type
|
||||
const PDOLValue pdol_transaction_cert = {0x98, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; // Transaction cert
|
||||
const PDOLValue pdol_unpredict_number = {0x9F37, {0x82, 0x3D, 0xDE, 0x7A}}; // Unpredictable number
|
||||
|
||||
const PDOLValue* const pdol_values[] = {
|
||||
&pdol_term_info,
|
||||
&pdol_term_type,
|
||||
&pdol_merchant_type,
|
||||
&pdol_term_trans_qualifies,
|
||||
&pdol_addtnl_term_qualifies,
|
||||
&pdol_amount_authorise,
|
||||
&pdol_amount,
|
||||
&pdol_country_code,
|
||||
&pdol_currency_code,
|
||||
&pdol_term_verification,
|
||||
&pdol_transaction_date,
|
||||
&pdol_transaction_type,
|
||||
&pdol_transaction_cert,
|
||||
&pdol_unpredict_number,
|
||||
};
|
||||
|
||||
EmvError emv_process_error(Iso14443_4aError error) {
|
||||
switch(error) {
|
||||
case Iso14443_4aErrorNone:
|
||||
return EmvErrorNone;
|
||||
case Iso14443_4aErrorNotPresent:
|
||||
return EmvErrorNotPresent;
|
||||
case Iso14443_4aErrorTimeout:
|
||||
return EmvErrorTimeout;
|
||||
default:
|
||||
return EmvErrorProtocol;
|
||||
}
|
||||
}
|
||||
|
||||
static void emv_trace(EmvPoller* instance, const char* message) {
|
||||
if(furi_log_get_level() == FuriLogLevelTrace) {
|
||||
FURI_LOG_T(TAG, "%s", message);
|
||||
|
||||
printf("TX: ");
|
||||
size_t size = bit_buffer_get_size_bytes(instance->tx_buffer);
|
||||
for(size_t i = 0; i < size; i++) {
|
||||
printf("%02X ", bit_buffer_get_byte(instance->tx_buffer, i));
|
||||
}
|
||||
|
||||
printf("\r\nRX: ");
|
||||
size = bit_buffer_get_size_bytes(instance->rx_buffer);
|
||||
for(size_t i = 0; i < size; i++) {
|
||||
printf("%02X ", bit_buffer_get_byte(instance->rx_buffer, i));
|
||||
}
|
||||
printf("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t emv_prepare_pdol(APDU* dest, APDU* src) {
|
||||
bool tag_found;
|
||||
for(uint16_t i = 0; i < src->size; i++) {
|
||||
tag_found = false;
|
||||
for(uint8_t j = 0; j < sizeof(pdol_values) / sizeof(PDOLValue*); j++) {
|
||||
if(src->data[i] == pdol_values[j]->tag) {
|
||||
// Found tag with 1 byte length
|
||||
uint8_t len = src->data[++i];
|
||||
memcpy(dest->data + dest->size, pdol_values[j]->data, len);
|
||||
dest->size += len;
|
||||
tag_found = true;
|
||||
break;
|
||||
} else if(((src->data[i] << 8) | src->data[i + 1]) == pdol_values[j]->tag) {
|
||||
// Found tag with 2 byte length
|
||||
i += 2;
|
||||
uint8_t len = src->data[i];
|
||||
memcpy(dest->data + dest->size, pdol_values[j]->data, len);
|
||||
dest->size += len;
|
||||
tag_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!tag_found) {
|
||||
// Unknown tag, fill zeros
|
||||
i += 2;
|
||||
uint8_t len = src->data[i];
|
||||
memset(dest->data + dest->size, 0, len);
|
||||
dest->size += len;
|
||||
}
|
||||
}
|
||||
return dest->size;
|
||||
}
|
||||
|
||||
static bool
|
||||
emv_decode_tlv_tag(const uint8_t* buff, uint16_t tag, uint8_t tlen, EmvApplication* app) {
|
||||
uint8_t i = 0;
|
||||
bool success = false;
|
||||
|
||||
switch(tag) {
|
||||
case EMV_TAG_LOG_FMT:
|
||||
furi_check(tlen < sizeof(app->log_fmt));
|
||||
memcpy(app->log_fmt, &buff[i], tlen);
|
||||
app->log_fmt_len = tlen;
|
||||
success = true;
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_LOG_FMT %X: len %d", tag, tlen);
|
||||
break;
|
||||
case EMV_TAG_GPO_FMT1:
|
||||
// skip AIP
|
||||
i += 2;
|
||||
tlen -= 2;
|
||||
furi_check(tlen < sizeof(app->afl.data));
|
||||
memcpy(app->afl.data, &buff[i], tlen);
|
||||
app->afl.size = tlen;
|
||||
success = true;
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_GPO_FMT1 %X: ", tag);
|
||||
break;
|
||||
case EMV_TAG_AID:
|
||||
app->aid_len = tlen;
|
||||
memcpy(app->aid, &buff[i], tlen);
|
||||
success = true;
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_AID %X: ", tag);
|
||||
for(size_t x = 0; x < tlen; x++) {
|
||||
FURI_LOG_RAW_T("%02X ", app->aid[x]);
|
||||
}
|
||||
FURI_LOG_RAW_T("\r\n");
|
||||
break;
|
||||
case EMV_TAG_PRIORITY:
|
||||
memcpy(&app->priority, &buff[i], tlen);
|
||||
success = true;
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_APP_PRIORITY %X: %d", tag, app->priority);
|
||||
break;
|
||||
case EMV_TAG_CARD_NAME:
|
||||
memcpy(app->name, &buff[i], tlen);
|
||||
app->name[tlen] = '\0';
|
||||
app->name_found = true;
|
||||
success = true;
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_CARD_NAME %x : %s", tag, app->name);
|
||||
break;
|
||||
case EMV_TAG_PDOL:
|
||||
memcpy(app->pdol.data, &buff[i], tlen);
|
||||
app->pdol.size = tlen;
|
||||
success = true;
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_PDOL %x (len=%d)", tag, tlen);
|
||||
break;
|
||||
case EMV_TAG_AFL:
|
||||
memcpy(app->afl.data, &buff[i], tlen);
|
||||
app->afl.size = tlen;
|
||||
success = true;
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_AFL %x (len=%d)", tag, tlen);
|
||||
break;
|
||||
// Tracks data https://murdoch.is/papers/defcon20emvdecode.pdf
|
||||
case EMV_TAG_TRACK_1_EQUIV: {
|
||||
// Contain PAN and expire date
|
||||
char track_1_equiv[80];
|
||||
memcpy(track_1_equiv, &buff[i], tlen);
|
||||
track_1_equiv[tlen] = '\0';
|
||||
success = true;
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_TRACK_1_EQUIV %x : %s", tag, track_1_equiv);
|
||||
break;
|
||||
}
|
||||
case EMV_TAG_TRACK_2_DATA:
|
||||
case EMV_TAG_TRACK_2_EQUIV: {
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2 %X", tag);
|
||||
// 0xD0 delimits PAN from expiry (YYMM)
|
||||
for(int x = 1; x < tlen; x++) {
|
||||
if(buff[i + x + 1] > 0xD0) {
|
||||
memcpy(app->pan, &buff[i], x + 1);
|
||||
app->pan_len = x + 1;
|
||||
app->exp_year = (buff[i + x + 1] << 4) | (buff[i + x + 2] >> 4);
|
||||
app->exp_month = (buff[i + x + 2] << 4) | (buff[i + x + 3] >> 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert 4-bit to ASCII representation
|
||||
char track_2_equiv[41];
|
||||
uint8_t track_2_equiv_len = 0;
|
||||
for(int x = 0; x < tlen; x++) {
|
||||
char top = (buff[i + x] >> 4) + '0';
|
||||
char bottom = (buff[i + x] & 0x0F) + '0';
|
||||
track_2_equiv[x * 2] = top;
|
||||
track_2_equiv_len++;
|
||||
if(top == '?') break;
|
||||
track_2_equiv[x * 2 + 1] = bottom;
|
||||
track_2_equiv_len++;
|
||||
if(bottom == '?') break;
|
||||
}
|
||||
track_2_equiv[track_2_equiv_len] = '\0';
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2 %X : %s", tag, track_2_equiv);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
case EMV_TAG_CARDHOLDER_NAME: {
|
||||
char name[27];
|
||||
memcpy(name, &buff[i], tlen);
|
||||
name[tlen] = '\0';
|
||||
success = true;
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_CARDHOLDER_NAME %x: %s", tag, name);
|
||||
break;
|
||||
}
|
||||
case EMV_TAG_PAN:
|
||||
memcpy(app->pan, &buff[i], tlen);
|
||||
app->pan_len = tlen;
|
||||
success = true;
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_PAN %x", tag);
|
||||
break;
|
||||
case EMV_TAG_EXP_DATE:
|
||||
app->exp_year = buff[i];
|
||||
app->exp_month = buff[i + 1];
|
||||
success = true;
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_EXP_DATE %x", tag);
|
||||
break;
|
||||
case EMV_TAG_CURRENCY_CODE:
|
||||
app->currency_code = (buff[i] << 8 | buff[i + 1]);
|
||||
success = true;
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_CURRENCY_CODE %x", tag);
|
||||
break;
|
||||
case EMV_TAG_COUNTRY_CODE:
|
||||
app->country_code = (buff[i] << 8 | buff[i + 1]);
|
||||
success = true;
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_COUNTRY_CODE %x", tag);
|
||||
break;
|
||||
case EMV_TAG_LOG_ENTRY:
|
||||
app->log_sfi = buff[i];
|
||||
app->log_records = buff[i + 1];
|
||||
success = true;
|
||||
FURI_LOG_T(
|
||||
TAG,
|
||||
"found EMV_TAG_LOG_ENTRY %x: sfi 0x%x, records %d",
|
||||
tag,
|
||||
app->log_sfi,
|
||||
app->log_records);
|
||||
break;
|
||||
case EMV_TAG_LAST_ONLINE_ATC:
|
||||
app->last_online_atc = (buff[i] << 8 | buff[i + 1]);
|
||||
success = true;
|
||||
break;
|
||||
case EMV_TAG_ATC:
|
||||
if(app->saving_trans_list)
|
||||
app->trans[app->active_tr].atc = (buff[i] << 8 | buff[i + 1]);
|
||||
else
|
||||
app->transaction_counter = (buff[i] << 8 | buff[i + 1]);
|
||||
success = true;
|
||||
break;
|
||||
case EMV_TAG_LOG_AMOUNT:
|
||||
memcpy(&app->trans[app->active_tr].amount, &buff[i], tlen);
|
||||
success = true;
|
||||
break;
|
||||
case EMV_TAG_LOG_COUNTRY:
|
||||
app->trans[app->active_tr].country = (buff[i] << 8 | buff[i + 1]);
|
||||
success = true;
|
||||
break;
|
||||
case EMV_TAG_LOG_CURRENCY:
|
||||
app->trans[app->active_tr].currency = (buff[i] << 8 | buff[i + 1]);
|
||||
success = true;
|
||||
break;
|
||||
case EMV_TAG_LOG_DATE:
|
||||
memcpy(&app->trans[app->active_tr].date, &buff[i], tlen);
|
||||
success = true;
|
||||
break;
|
||||
case EMV_TAG_LOG_TIME:
|
||||
memcpy(&app->trans[app->active_tr].time, &buff[i], tlen);
|
||||
success = true;
|
||||
break;
|
||||
case EMV_TAG_PIN_TRY_COUNTER:
|
||||
app->pin_try_counter = buff[i];
|
||||
success = true;
|
||||
FURI_LOG_T(TAG, "found EMV_TAG_PIN_TRY_COUNTER %x: %d", tag, app->pin_try_counter);
|
||||
break;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool emv_response_error(const uint8_t* buff, uint16_t len) {
|
||||
uint8_t i = 0;
|
||||
uint8_t first_byte = 0;
|
||||
bool error = true;
|
||||
|
||||
first_byte = buff[i];
|
||||
|
||||
if((len == 2) && ((first_byte >> 4) == 6)) {
|
||||
switch(buff[i]) {
|
||||
case EMV_TAG_RESP_BUF_SIZE:
|
||||
FURI_LOG_T(TAG, " Wrong length. Read %02X bytes", buff[i + 1]);
|
||||
// Need to request SFI again with this length value
|
||||
return error;
|
||||
case EMV_TAG_RESP_BYTES_AVAILABLE:
|
||||
FURI_LOG_T(TAG, " Bytes available: %02X", buff[i + 1]);
|
||||
// Need to request one more time
|
||||
return error;
|
||||
|
||||
default:
|
||||
FURI_LOG_T(TAG, " Error/warning code: %02X %02X", buff[i], buff[i + 1]);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
emv_parse_tag(const uint8_t* buff, uint16_t len, uint16_t* t, uint8_t* tl, uint8_t* off) {
|
||||
uint8_t i = *off;
|
||||
uint16_t tag = 0;
|
||||
uint8_t first_byte = 0;
|
||||
uint8_t tlen = 0;
|
||||
bool success = false;
|
||||
|
||||
first_byte = buff[i];
|
||||
|
||||
if(emv_response_error(buff, len)) return success;
|
||||
|
||||
if((first_byte & 31) == 31) { // 2-byte tag
|
||||
tag = buff[i] << 8 | buff[i + 1];
|
||||
i++;
|
||||
FURI_LOG_T(TAG, " 2-byte TLV EMV tag: %x", tag);
|
||||
} else {
|
||||
tag = buff[i];
|
||||
FURI_LOG_T(TAG, " 1-byte TLV EMV tag: %x", tag);
|
||||
}
|
||||
i++;
|
||||
tlen = buff[i];
|
||||
if((tlen & 128) == 128) { // long length value
|
||||
i++;
|
||||
tlen = buff[i];
|
||||
FURI_LOG_T(TAG, " 2-byte TLV length: %d", tlen);
|
||||
} else {
|
||||
FURI_LOG_T(TAG, " 1-byte TLV length: %d", tlen);
|
||||
}
|
||||
i++;
|
||||
|
||||
*off = i;
|
||||
*t = tag;
|
||||
*tl = tlen;
|
||||
success = true;
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool emv_decode_tl(
|
||||
const uint8_t* buff,
|
||||
uint16_t len,
|
||||
const uint8_t* fmt,
|
||||
uint8_t fmt_len,
|
||||
EmvApplication* app) {
|
||||
uint8_t i = 0;
|
||||
uint8_t f = 0;
|
||||
uint16_t tag = 0;
|
||||
uint8_t tlen = 0;
|
||||
bool success = false;
|
||||
|
||||
if(emv_response_error(buff, len)) return success;
|
||||
|
||||
while(f < fmt_len && i < len) {
|
||||
success = emv_parse_tag(fmt, fmt_len, &tag, &tlen, &f);
|
||||
if(!success) return success;
|
||||
emv_decode_tlv_tag(&buff[i], tag, tlen, app);
|
||||
i += tlen;
|
||||
}
|
||||
success = true;
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool emv_decode_response_tlv(const uint8_t* buff, uint8_t len, EmvApplication* app) {
|
||||
uint8_t i = 0;
|
||||
uint16_t tag = 0;
|
||||
uint8_t first_byte = 0;
|
||||
uint8_t tlen = 0;
|
||||
bool success = false;
|
||||
|
||||
while(i < len) {
|
||||
first_byte = buff[i];
|
||||
|
||||
success = emv_parse_tag(buff, len, &tag, &tlen, &i);
|
||||
if(!success) return success;
|
||||
|
||||
if((first_byte & 32) == 32) { // "Constructed" -- contains more TLV data to parse
|
||||
FURI_LOG_T(TAG, "Constructed TLV %x", tag);
|
||||
if(!emv_decode_response_tlv(&buff[i], tlen, app)) {
|
||||
FURI_LOG_T(TAG, "Failed to decode response for %x", tag);
|
||||
// return false;
|
||||
} else {
|
||||
success = true;
|
||||
}
|
||||
} else {
|
||||
emv_decode_tlv_tag(&buff[i], tag, tlen, app);
|
||||
}
|
||||
i += tlen;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
EmvError emv_poller_select_ppse(EmvPoller* instance) {
|
||||
EmvError error = EmvErrorNone;
|
||||
|
||||
const uint8_t emv_select_ppse_cmd[] = {
|
||||
0x00, 0xA4, // SELECT ppse
|
||||
0x04, 0x00, // P1:By name, P2: empty
|
||||
0x0e, // Lc: Data length
|
||||
0x32, 0x50, 0x41, 0x59, 0x2e, 0x53, 0x59, // Data string:
|
||||
0x53, 0x2e, 0x44, 0x44, 0x46, 0x30, 0x31, // 2PAY.SYS.DDF01 (PPSE)
|
||||
0x00 // Le
|
||||
};
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_reset(instance->rx_buffer);
|
||||
|
||||
bit_buffer_copy_bytes(instance->tx_buffer, emv_select_ppse_cmd, sizeof(emv_select_ppse_cmd));
|
||||
do {
|
||||
FURI_LOG_D(TAG, "Send select PPSE");
|
||||
|
||||
Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block_pwt_ext(
|
||||
instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer);
|
||||
|
||||
if(iso14443_4a_error != Iso14443_4aErrorNone) {
|
||||
FURI_LOG_E(TAG, "Failed select PPSE");
|
||||
error = emv_process_error(iso14443_4a_error);
|
||||
break;
|
||||
}
|
||||
|
||||
emv_trace(instance, "Select PPSE answer:");
|
||||
|
||||
const uint8_t* buff = bit_buffer_get_data(instance->rx_buffer);
|
||||
|
||||
if(!emv_decode_response_tlv(
|
||||
buff,
|
||||
bit_buffer_get_size_bytes(instance->rx_buffer),
|
||||
&instance->data->emv_application)) {
|
||||
error = EmvErrorProtocol;
|
||||
FURI_LOG_E(TAG, "Failed to parse application");
|
||||
}
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
EmvError emv_poller_select_application(EmvPoller* instance) {
|
||||
EmvError error = EmvErrorNone;
|
||||
|
||||
const uint8_t emv_select_header[] = {
|
||||
0x00,
|
||||
0xA4, // SELECT application
|
||||
0x04,
|
||||
0x00 // P1:By name, P2:First or only occurence
|
||||
};
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_reset(instance->rx_buffer);
|
||||
|
||||
// Copy header
|
||||
bit_buffer_copy_bytes(instance->tx_buffer, emv_select_header, sizeof(emv_select_header));
|
||||
|
||||
// Copy AID
|
||||
bit_buffer_append_byte(instance->tx_buffer, instance->data->emv_application.aid_len);
|
||||
bit_buffer_append_bytes(
|
||||
instance->tx_buffer,
|
||||
instance->data->emv_application.aid,
|
||||
instance->data->emv_application.aid_len);
|
||||
bit_buffer_append_byte(instance->tx_buffer, 0x00);
|
||||
|
||||
do {
|
||||
FURI_LOG_D(TAG, "Start application");
|
||||
|
||||
Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block_pwt_ext(
|
||||
instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer);
|
||||
|
||||
emv_trace(instance, "Start application answer:");
|
||||
|
||||
if(iso14443_4a_error != Iso14443_4aErrorNone) {
|
||||
FURI_LOG_E(TAG, "Failed to read PAN or PDOL");
|
||||
error = emv_process_error(iso14443_4a_error);
|
||||
break;
|
||||
}
|
||||
|
||||
const uint8_t* buff = bit_buffer_get_data(instance->rx_buffer);
|
||||
|
||||
if(!emv_decode_response_tlv(
|
||||
buff,
|
||||
bit_buffer_get_size_bytes(instance->rx_buffer),
|
||||
&instance->data->emv_application)) {
|
||||
error = EmvErrorProtocol;
|
||||
FURI_LOG_E(TAG, "Failed to parse application");
|
||||
break;
|
||||
}
|
||||
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
EmvError emv_poller_get_processing_options(EmvPoller* instance) {
|
||||
EmvError error = EmvErrorNone;
|
||||
|
||||
const uint8_t emv_gpo_header[] = {0x80, 0xA8, 0x00, 0x00};
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_reset(instance->rx_buffer);
|
||||
|
||||
// Copy header
|
||||
bit_buffer_copy_bytes(instance->tx_buffer, emv_gpo_header, sizeof(emv_gpo_header));
|
||||
|
||||
// Prepare and copy pdol parameters
|
||||
APDU pdol_data = {0, {0}};
|
||||
emv_prepare_pdol(&pdol_data, &instance->data->emv_application.pdol);
|
||||
|
||||
bit_buffer_append_byte(instance->tx_buffer, 0x02 + pdol_data.size);
|
||||
bit_buffer_append_byte(instance->tx_buffer, 0x83);
|
||||
bit_buffer_append_byte(instance->tx_buffer, pdol_data.size);
|
||||
|
||||
bit_buffer_append_bytes(instance->tx_buffer, pdol_data.data, pdol_data.size);
|
||||
bit_buffer_append_byte(instance->tx_buffer, 0x00);
|
||||
|
||||
do {
|
||||
FURI_LOG_D(TAG, "Get proccessing options");
|
||||
|
||||
Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block_pwt_ext(
|
||||
instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer);
|
||||
|
||||
emv_trace(instance, "Get processing options answer:");
|
||||
|
||||
if(iso14443_4a_error != Iso14443_4aErrorNone) {
|
||||
FURI_LOG_E(TAG, "Failed to get processing options, error %u", iso14443_4a_error);
|
||||
error = emv_process_error(iso14443_4a_error);
|
||||
break;
|
||||
}
|
||||
|
||||
const uint8_t* buff = bit_buffer_get_data(instance->rx_buffer);
|
||||
|
||||
if(!emv_decode_response_tlv(
|
||||
buff,
|
||||
bit_buffer_get_size_bytes(instance->rx_buffer),
|
||||
&instance->data->emv_application)) {
|
||||
error = EmvErrorProtocol;
|
||||
FURI_LOG_E(TAG, "Failed to parse processing options");
|
||||
}
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t record_num) {
|
||||
EmvError error = EmvErrorNone;
|
||||
FuriString* text = furi_string_alloc();
|
||||
|
||||
uint8_t sfi_param = (sfi << 3) | (1 << 2);
|
||||
uint8_t emv_sfi_header[] = {
|
||||
0x00,
|
||||
0xB2, // READ RECORD
|
||||
record_num, // P1:record_number
|
||||
sfi_param, // P2:SFI
|
||||
0x00 // Le
|
||||
};
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_reset(instance->rx_buffer);
|
||||
|
||||
bit_buffer_copy_bytes(instance->tx_buffer, emv_sfi_header, sizeof(emv_sfi_header));
|
||||
|
||||
do {
|
||||
Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block_pwt_ext(
|
||||
instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer);
|
||||
|
||||
furi_string_printf(text, "SFI 0x%X record %d:", sfi, record_num);
|
||||
emv_trace(instance, furi_string_get_cstr(text));
|
||||
|
||||
if(iso14443_4a_error != Iso14443_4aErrorNone) {
|
||||
FURI_LOG_E(TAG, "Failed to read SFI 0x%X record %d", sfi, record_num);
|
||||
error = emv_process_error(iso14443_4a_error);
|
||||
break;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
furi_string_free(text);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
EmvError emv_poller_read_afl(EmvPoller* instance) {
|
||||
EmvError error = EmvErrorNone;
|
||||
|
||||
APDU* afl = &instance->data->emv_application.afl;
|
||||
|
||||
if(afl->size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FURI_LOG_D(TAG, "Search PAN in SFI");
|
||||
|
||||
// Iterate through all files
|
||||
for(size_t i = 0; i < instance->data->emv_application.afl.size; i += 4) {
|
||||
uint8_t sfi = afl->data[i] >> 3;
|
||||
uint8_t record_start = afl->data[i + 1];
|
||||
uint8_t record_end = afl->data[i + 2];
|
||||
// Iterate through all records in file
|
||||
for(uint8_t record = record_start; record <= record_end; ++record) {
|
||||
error = emv_poller_read_sfi_record(instance, sfi, record);
|
||||
if(error != EmvErrorNone) break;
|
||||
|
||||
if(!emv_decode_response_tlv(
|
||||
bit_buffer_get_data(instance->rx_buffer),
|
||||
bit_buffer_get_size_bytes(instance->rx_buffer),
|
||||
&instance->data->emv_application)) {
|
||||
error = EmvErrorProtocol;
|
||||
FURI_LOG_T(TAG, "Failed to parse SFI 0x%X record %d", sfi, record);
|
||||
}
|
||||
|
||||
// Some READ RECORD returns 1 byte response 0x12/0x13 (IDK WTF),
|
||||
// then poller return Timeout to all subsequent requests.
|
||||
// TODO: remove below lines when it was fixed
|
||||
if(instance->data->emv_application.pan_len != 0)
|
||||
return EmvErrorNone; // Card number fetched
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static EmvError emv_poller_req_get_data(EmvPoller* instance, uint16_t tag) {
|
||||
EmvError error = EmvErrorNone;
|
||||
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
bit_buffer_reset(instance->rx_buffer);
|
||||
|
||||
bit_buffer_append_byte(instance->tx_buffer, EMV_REQ_GET_DATA >> 8);
|
||||
bit_buffer_append_byte(instance->tx_buffer, EMV_REQ_GET_DATA & 0xFF);
|
||||
bit_buffer_append_byte(instance->tx_buffer, tag >> 8);
|
||||
bit_buffer_append_byte(instance->tx_buffer, tag & 0xFF);
|
||||
bit_buffer_append_byte(instance->tx_buffer, 0x00); //Length
|
||||
|
||||
do {
|
||||
FURI_LOG_D(TAG, "Get data for tag 0x%x", tag);
|
||||
|
||||
Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block_pwt_ext(
|
||||
instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer);
|
||||
|
||||
emv_trace(instance, "Get log data answer:");
|
||||
|
||||
if(iso14443_4a_error != Iso14443_4aErrorNone) {
|
||||
FURI_LOG_E(TAG, "Failed to get data, error %u", iso14443_4a_error);
|
||||
error = emv_process_error(iso14443_4a_error);
|
||||
break;
|
||||
}
|
||||
|
||||
const uint8_t* buff = bit_buffer_get_data(instance->rx_buffer);
|
||||
|
||||
if(!emv_decode_response_tlv(
|
||||
buff,
|
||||
bit_buffer_get_size_bytes(instance->rx_buffer),
|
||||
&instance->data->emv_application)) {
|
||||
error = EmvErrorProtocol;
|
||||
FURI_LOG_E(TAG, "Failed to parse get data");
|
||||
}
|
||||
} while(false);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
EmvError emv_poller_get_pin_try_counter(EmvPoller* instance) {
|
||||
return emv_poller_req_get_data(instance, EMV_TAG_PIN_TRY_COUNTER);
|
||||
}
|
||||
|
||||
EmvError emv_poller_get_last_online_atc(EmvPoller* instance) {
|
||||
return emv_poller_req_get_data(instance, EMV_TAG_LAST_ONLINE_ATC);
|
||||
}
|
||||
|
||||
static EmvError emv_poller_get_log_format(EmvPoller* instance) {
|
||||
return emv_poller_req_get_data(instance, EMV_TAG_LOG_FMT);
|
||||
}
|
||||
|
||||
EmvError emv_poller_read_log_entry(EmvPoller* instance) {
|
||||
EmvError error = EmvErrorProtocol;
|
||||
|
||||
if(!instance->data->emv_application.log_sfi) return error;
|
||||
uint8_t records = instance->data->emv_application.log_records;
|
||||
if(records == 0) {
|
||||
return error;
|
||||
}
|
||||
|
||||
instance->data->emv_application.saving_trans_list = true;
|
||||
error = emv_poller_get_log_format(instance);
|
||||
if(error != EmvErrorNone) return error;
|
||||
|
||||
FURI_LOG_D(TAG, "Read Transaction logs");
|
||||
|
||||
uint8_t sfi = instance->data->emv_application.log_sfi;
|
||||
uint8_t record_start = 1;
|
||||
uint8_t record_end = records;
|
||||
// Iterate through all records in file
|
||||
for(uint8_t record = record_start; record <= record_end; ++record) {
|
||||
error = emv_poller_read_sfi_record(instance, sfi, record);
|
||||
if(error != EmvErrorNone) break;
|
||||
if(!emv_decode_tl(
|
||||
bit_buffer_get_data(instance->rx_buffer),
|
||||
bit_buffer_get_size_bytes(instance->rx_buffer),
|
||||
instance->data->emv_application.log_fmt,
|
||||
instance->data->emv_application.log_fmt_len,
|
||||
&instance->data->emv_application)) {
|
||||
error = EmvErrorProtocol;
|
||||
FURI_LOG_T(TAG, "Failed to parse log SFI 0x%X record %d", sfi, record);
|
||||
break;
|
||||
}
|
||||
|
||||
instance->data->emv_application.active_tr++;
|
||||
furi_check(
|
||||
instance->data->emv_application.active_tr <
|
||||
COUNT_OF(instance->data->emv_application.trans));
|
||||
}
|
||||
|
||||
instance->data->emv_application.saving_trans_list = false;
|
||||
return error;
|
||||
}
|
||||
51
lib/nfc/protocols/emv/emv_poller_i.h
Normal file
51
lib/nfc/protocols/emv/emv_poller_i.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include "emv_poller.h"
|
||||
|
||||
#include <lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
EmvPollerStateIdle,
|
||||
EmvPollerStateSelectPPSE,
|
||||
EmvPollerStateSelectApplication,
|
||||
EmvPollerStateGetProcessingOptions,
|
||||
EmvPollerStateReadFiles,
|
||||
EmvPollerStateReadExtra,
|
||||
EmvPollerStateReadFailed,
|
||||
EmvPollerStateReadSuccess,
|
||||
|
||||
EmvPollerStateNum,
|
||||
} EmvPollerState;
|
||||
|
||||
typedef enum {
|
||||
EmvPollerSessionStateIdle,
|
||||
EmvPollerSessionStateActive,
|
||||
EmvPollerSessionStateStopRequest,
|
||||
} EmvPollerSessionState;
|
||||
|
||||
struct EmvPoller {
|
||||
Iso14443_4aPoller* iso14443_4a_poller;
|
||||
EmvPollerSessionState session_state;
|
||||
EmvPollerState state;
|
||||
EmvError error;
|
||||
EmvData* data;
|
||||
BitBuffer* tx_buffer;
|
||||
BitBuffer* rx_buffer;
|
||||
EmvPollerEventData emv_event_data;
|
||||
EmvPollerEvent emv_event;
|
||||
NfcGenericEvent general_event;
|
||||
NfcGenericCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
EmvError emv_process_error(Iso14443_4aError error);
|
||||
|
||||
const EmvData* emv_poller_get_data(EmvPoller* instance);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -13,6 +13,7 @@ typedef enum {
|
||||
Iso14443_4aErrorNotPresent,
|
||||
Iso14443_4aErrorProtocol,
|
||||
Iso14443_4aErrorTimeout,
|
||||
Iso14443_4aErrorSendExtra,
|
||||
} Iso14443_4aError;
|
||||
|
||||
typedef enum {
|
||||
|
||||
@@ -56,6 +56,11 @@ Iso14443_4aError iso14443_4a_poller_send_block(
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer);
|
||||
|
||||
Iso14443_4aError iso14443_4a_poller_send_block_pwt_ext(
|
||||
Iso14443_4aPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer);
|
||||
|
||||
/**
|
||||
* @brief Send HALT command to the card.
|
||||
*
|
||||
|
||||
@@ -81,3 +81,50 @@ Iso14443_4aError iso14443_4a_poller_send_block(
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
Iso14443_4aError iso14443_4a_poller_send_block_pwt_ext(
|
||||
Iso14443_4aPoller* instance,
|
||||
const BitBuffer* tx_buffer,
|
||||
BitBuffer* rx_buffer) {
|
||||
furi_assert(instance);
|
||||
|
||||
uint8_t retry = 5;
|
||||
bit_buffer_reset(instance->tx_buffer);
|
||||
iso14443_4_layer_encode_block(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer);
|
||||
|
||||
Iso14443_4aError error = Iso14443_4aErrorNone;
|
||||
|
||||
do {
|
||||
bit_buffer_reset(instance->rx_buffer);
|
||||
Iso14443_3aError iso14443_3a_error = iso14443_3a_poller_send_standard_frame(
|
||||
instance->iso14443_3a_poller,
|
||||
instance->tx_buffer,
|
||||
instance->rx_buffer,
|
||||
iso14443_4a_get_fwt_fc_max(instance->data));
|
||||
|
||||
if(iso14443_3a_error != Iso14443_3aErrorNone) {
|
||||
FURI_LOG_RAW_T("RAW RX(%d):", bit_buffer_get_size_bytes(instance->rx_buffer));
|
||||
for(size_t x = 0; x < bit_buffer_get_size_bytes(instance->rx_buffer); x++) {
|
||||
FURI_LOG_RAW_T("%02X ", bit_buffer_get_byte(instance->rx_buffer, x));
|
||||
}
|
||||
FURI_LOG_RAW_T("\r\n");
|
||||
|
||||
error = iso14443_4a_process_error(iso14443_3a_error);
|
||||
break;
|
||||
|
||||
} else {
|
||||
error = iso14443_4_layer_decode_block_pwt_ext(
|
||||
instance->iso14443_4_layer, rx_buffer, instance->rx_buffer);
|
||||
if(error == Iso14443_4aErrorSendExtra) {
|
||||
if(--retry == 0) break;
|
||||
// Send response for Control message
|
||||
if(bit_buffer_get_size_bytes(rx_buffer))
|
||||
bit_buffer_copy(instance->tx_buffer, rx_buffer);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} while(true);
|
||||
|
||||
return error;
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <nfc/protocols/mf_ultralight/mf_ultralight.h>
|
||||
#include <nfc/protocols/mf_classic/mf_classic.h>
|
||||
#include <nfc/protocols/mf_desfire/mf_desfire.h>
|
||||
#include <nfc/protocols/emv/emv.h>
|
||||
#include <nfc/protocols/slix/slix_device_defs.h>
|
||||
#include <nfc/protocols/st25tb/st25tb.h>
|
||||
|
||||
@@ -42,5 +43,6 @@ const NfcDeviceBase* nfc_devices[NfcProtocolNum] = {
|
||||
[NfcProtocolMfDesfire] = &nfc_device_mf_desfire,
|
||||
[NfcProtocolSlix] = &nfc_device_slix,
|
||||
[NfcProtocolSt25tb] = &nfc_device_st25tb,
|
||||
[NfcProtocolEmv] = &nfc_device_emv,
|
||||
/* Add new protocols here */
|
||||
};
|
||||
|
||||
@@ -20,4 +20,5 @@ const NfcListenerBase* nfc_listeners_api[NfcProtocolNum] = {
|
||||
[NfcProtocolSlix] = &nfc_listener_slix,
|
||||
[NfcProtocolSt25tb] = NULL,
|
||||
[NfcProtocolFelica] = &nfc_listener_felica,
|
||||
[NfcProtocolEmv] = NULL,
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <nfc/protocols/mf_ultralight/mf_ultralight_poller_defs.h>
|
||||
#include <nfc/protocols/mf_classic/mf_classic_poller_defs.h>
|
||||
#include <nfc/protocols/mf_desfire/mf_desfire_poller_defs.h>
|
||||
#include <nfc/protocols/emv/emv_poller_defs.h>
|
||||
#include <nfc/protocols/slix/slix_poller_defs.h>
|
||||
#include <nfc/protocols/st25tb/st25tb_poller_defs.h>
|
||||
|
||||
@@ -23,6 +24,7 @@ const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = {
|
||||
[NfcProtocolMfClassic] = &mf_classic_poller,
|
||||
[NfcProtocolMfDesfire] = &mf_desfire_poller,
|
||||
[NfcProtocolSlix] = &nfc_poller_slix,
|
||||
/* Add new pollers here */
|
||||
[NfcProtocolSt25tb] = &nfc_poller_st25tb,
|
||||
[NfcProtocolEmv] = &emv_poller,
|
||||
/* Add new pollers here */
|
||||
};
|
||||
|
||||
@@ -12,17 +12,19 @@
|
||||
* ```
|
||||
* **************************** Protocol tree structure ***************************
|
||||
*
|
||||
* (Start)
|
||||
* |
|
||||
* +------------------------+-----------+---------+------------+
|
||||
* | | | | |
|
||||
* ISO14443-3A ISO14443-3B Felica ISO15693-3 ST25TB
|
||||
* | | |
|
||||
* +---------------+-------------+ ISO14443-4B SLIX
|
||||
* | | |
|
||||
* ISO14443-4A Mf Ultralight Mf Classic
|
||||
* |
|
||||
* Mf Desfire
|
||||
* (Start)
|
||||
* |
|
||||
* +------------------------+-----------+---------+------------+
|
||||
* | | | | |
|
||||
* ISO14443-3A ISO14443-3B Felica ISO15693-3 ST25TB
|
||||
* | | |
|
||||
* +---------------+-------------+ ISO14443-4B SLIX
|
||||
* | | |
|
||||
* ISO14443-4A Mf Ultralight Mf Classic
|
||||
* |
|
||||
* +-----+-----+
|
||||
* | |
|
||||
* Mf Desfire EMV
|
||||
* ```
|
||||
*
|
||||
* When implementing a new protocol, its place in the tree must be determined first.
|
||||
@@ -61,6 +63,7 @@ static const NfcProtocol nfc_protocol_iso14443_3b_children_protocol[] = {
|
||||
/** List of ISO14443-4A child protocols. */
|
||||
static const NfcProtocol nfc_protocol_iso14443_4a_children_protocol[] = {
|
||||
NfcProtocolMfDesfire,
|
||||
NfcProtocolEmv,
|
||||
};
|
||||
|
||||
/** List of ISO115693-3 child protocols. */
|
||||
@@ -146,6 +149,12 @@ static const NfcProtocolTreeNode nfc_protocol_nodes[NfcProtocolNum] = {
|
||||
.children_num = 0,
|
||||
.children_protocol = NULL,
|
||||
},
|
||||
[NfcProtocolEmv] =
|
||||
{
|
||||
.parent_protocol = NfcProtocolIso14443_4a,
|
||||
.children_num = 0,
|
||||
.children_protocol = NULL,
|
||||
},
|
||||
/* Add new protocols here */
|
||||
};
|
||||
|
||||
|
||||
@@ -72,19 +72,19 @@
|
||||
*
|
||||
* ### 2.2 File structure explanation
|
||||
*
|
||||
* | Filename | Explanation |
|
||||
* |:------------------------------|:------------|
|
||||
* | protocol_name.h | Main protocol data structure and associated functions declarations. It is recommended to keep the former as opaque pointer. |
|
||||
* | protocol_name.c | Implementations of functions declared in `protocol_name.h`. |
|
||||
* | protocol_name_device_defs.h | Declarations for use by the NfcDevice library. See nfc_device_base_i.h for more info. |
|
||||
* | protocol_name_poller.h | Protocol-specific poller and associated functions declarations. |
|
||||
* | protocol_name_poller.c | Implementation of functions declared in `protocol_name_poller.h`. |
|
||||
* | protocol_name_poller_defs.h | Declarations for use by the NfcPoller library. See nfc_poller_base.h for more info. |
|
||||
* | protocol_name_listener.h | Protocol-specific listener and associated functions declarations. Optional, needed for emulation support. |
|
||||
* | protocol_name_listener.c | Implementation of functions declared in `protocol_name_listener.h`. Optional, needed for emulation support. |
|
||||
* | Filename | Explanation |
|
||||
* |:------------------------------|:--------------------------------------------------------------------------------------------------------------------------------|
|
||||
* | protocol_name.h | Main protocol data structure and associated functions declarations. It is recommended to keep the former as opaque pointer. |
|
||||
* | protocol_name.c | Implementations of functions declared in `protocol_name.h`. |
|
||||
* | protocol_name_device_defs.h | Declarations for use by the NfcDevice library. See nfc_device_base_i.h for more info. |
|
||||
* | protocol_name_poller.h | Protocol-specific poller and associated functions declarations. |
|
||||
* | protocol_name_poller.c | Implementation of functions declared in `protocol_name_poller.h`. |
|
||||
* | protocol_name_poller_defs.h | Declarations for use by the NfcPoller library. See nfc_poller_base.h for more info. |
|
||||
* | protocol_name_listener.h | Protocol-specific listener and associated functions declarations. Optional, needed for emulation support. |
|
||||
* | protocol_name_listener.c | Implementation of functions declared in `protocol_name_listener.h`. Optional, needed for emulation support. |
|
||||
* | protocol_name_listener_defs.h | Declarations for use by the NfcListener library. See nfc_listener_base.h for more info. Optional, needed for emulation support. |
|
||||
* | protocol_name_sync.h | Synchronous API declarations. (See below for sync API explanation). Optional.|
|
||||
* | protocol_name_sync.c | Synchronous API implementation. Optional. |
|
||||
* | protocol_name_sync.h | Synchronous API declarations. (See below for sync API explanation). Optional. |
|
||||
* | protocol_name_sync.c | Synchronous API implementation. Optional. |
|
||||
*
|
||||
* ## 3 Implement the code
|
||||
*
|
||||
@@ -187,6 +187,7 @@ typedef enum {
|
||||
NfcProtocolMfDesfire,
|
||||
NfcProtocolSlix,
|
||||
NfcProtocolSt25tb,
|
||||
NfcProtocolEmv,
|
||||
/* Add new protocols here */
|
||||
|
||||
NfcProtocolNum, /**< Special value representing the number of available protocols. */
|
||||
|
||||
@@ -126,6 +126,8 @@ Header,+,lib/nfc/nfc_device.h,,
|
||||
Header,+,lib/nfc/nfc_listener.h,,
|
||||
Header,+,lib/nfc/nfc_poller.h,,
|
||||
Header,+,lib/nfc/nfc_scanner.h,,
|
||||
Header,+,lib/nfc/protocols/emv/emv.h,,
|
||||
Header,+,lib/nfc/protocols/emv/emv_poller.h,,
|
||||
Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a.h,,
|
||||
Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h,,
|
||||
Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h,,
|
||||
@@ -911,6 +913,26 @@ Function,+,elf_symbolname_hash,uint32_t,const char*
|
||||
Function,+,empty_screen_alloc,EmptyScreen*,
|
||||
Function,+,empty_screen_free,void,EmptyScreen*
|
||||
Function,+,empty_screen_get_view,View*,EmptyScreen*
|
||||
Function,+,emv_alloc,EmvData*,
|
||||
Function,+,emv_copy,void,"EmvData*, const EmvData*"
|
||||
Function,+,emv_free,void,EmvData*
|
||||
Function,+,emv_get_base_data,Iso14443_4aData*,const EmvData*
|
||||
Function,+,emv_get_device_name,const char*,"const EmvData*, NfcDeviceNameType"
|
||||
Function,+,emv_get_uid,const uint8_t*,"const EmvData*, size_t*"
|
||||
Function,+,emv_is_equal,_Bool,"const EmvData*, const EmvData*"
|
||||
Function,+,emv_load,_Bool,"EmvData*, FlipperFormat*, uint32_t"
|
||||
Function,+,emv_poller_get_last_online_atc,EmvError,EmvPoller*
|
||||
Function,+,emv_poller_get_pin_try_counter,EmvError,EmvPoller*
|
||||
Function,+,emv_poller_get_processing_options,EmvError,EmvPoller*
|
||||
Function,+,emv_poller_read_afl,EmvError,EmvPoller*
|
||||
Function,+,emv_poller_read_log_entry,EmvError,EmvPoller*
|
||||
Function,+,emv_poller_read_sfi_record,EmvError,"EmvPoller*, uint8_t, uint8_t"
|
||||
Function,+,emv_poller_select_application,EmvError,EmvPoller*
|
||||
Function,+,emv_poller_select_ppse,EmvError,EmvPoller*
|
||||
Function,+,emv_reset,void,EmvData*
|
||||
Function,+,emv_save,_Bool,"const EmvData*, FlipperFormat*"
|
||||
Function,+,emv_set_uid,_Bool,"EmvData*, const uint8_t*, size_t"
|
||||
Function,+,emv_verify,_Bool,"EmvData*, const FuriString*"
|
||||
Function,-,erand48,double,unsigned short[3]
|
||||
Function,-,erf,double,double
|
||||
Function,-,erfc,double,double
|
||||
@@ -2020,6 +2042,7 @@ Function,+,iso14443_4a_load,_Bool,"Iso14443_4aData*, FlipperFormat*, uint32_t"
|
||||
Function,+,iso14443_4a_poller_halt,Iso14443_4aError,Iso14443_4aPoller*
|
||||
Function,+,iso14443_4a_poller_read_ats,Iso14443_4aError,"Iso14443_4aPoller*, Iso14443_4aAtsData*"
|
||||
Function,+,iso14443_4a_poller_send_block,Iso14443_4aError,"Iso14443_4aPoller*, const BitBuffer*, BitBuffer*"
|
||||
Function,+,iso14443_4a_poller_send_block_pwt_ext,Iso14443_4aError,"Iso14443_4aPoller*, const BitBuffer*, BitBuffer*"
|
||||
Function,+,iso14443_4a_reset,void,Iso14443_4aData*
|
||||
Function,+,iso14443_4a_save,_Bool,"const Iso14443_4aData*, FlipperFormat*"
|
||||
Function,+,iso14443_4a_set_uid,_Bool,"Iso14443_4aData*, const uint8_t*, size_t"
|
||||
@@ -3993,6 +4016,7 @@ Variable,+,message_red_255,const NotificationMessage,
|
||||
Variable,+,message_sound_off,const NotificationMessage,
|
||||
Variable,+,message_vibro_off,const NotificationMessage,
|
||||
Variable,+,message_vibro_on,const NotificationMessage,
|
||||
Variable,-,nfc_device_emv,const NfcDeviceBase,
|
||||
Variable,-,nfc_device_mf_classic,const NfcDeviceBase,
|
||||
Variable,-,nfc_device_mf_desfire,const NfcDeviceBase,
|
||||
Variable,-,nfc_device_mf_ultralight,const NfcDeviceBase,
|
||||
|
||||
|
Reference in New Issue
Block a user