Skip to content

Commit 200a613

Browse files
authored
Merge pull request #702 from wosk/emv-fixes
Emv fixes
2 parents b8b344c + 4adb492 commit 200a613

File tree

13 files changed

+297
-157
lines changed

13 files changed

+297
-157
lines changed

applications/main/nfc/helpers/protocol_support/emv/emv.c

-10
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,6 @@ static void nfc_scene_read_success_on_enter_emv(NfcApp* instance) {
6464
furi_string_free(temp_str);
6565
}
6666

67-
// static void nfc_scene_emulate_on_enter_emv(NfcApp* instance) {
68-
// const Iso14443_4aData* iso14443_4a_data =
69-
// nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_4a);
70-
71-
// instance->listener =
72-
// nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, iso14443_4a_data);
73-
// nfc_listener_start(
74-
// instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance);
75-
// }
76-
7767
const NfcProtocolSupportBase nfc_protocol_support_emv = {
7868
.features = NfcProtocolFeatureMoreInfo,
7969

applications/main/nfc/helpers/protocol_support/emv/emv_render.c

+24-46
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "emv_render.h"
22

33
#include "../iso14443_4a/iso14443_4a_render.h"
4+
#include <bit_lib.h>
45
#include "nfc/nfc_app_i.h"
56

67
void nfc_render_emv_info(const EmvData* data, NfcProtocolFormatType format_type, FuriString* str) {
@@ -29,21 +30,9 @@ void nfc_render_emv_uid(const uint8_t* uid, const uint8_t uid_len, FuriString* s
2930
furi_string_cat_printf(str, "\n");
3031
}
3132

32-
void nfc_render_emv_aid(const uint8_t* uid, const uint8_t uid_len, FuriString* str) {
33-
if(uid_len == 0) return;
34-
35-
furi_string_cat_printf(str, "UID: ");
36-
37-
for(uint8_t i = 0; i < uid_len; i++) {
38-
furi_string_cat_printf(str, "%02X ", uid[i]);
39-
}
40-
41-
furi_string_cat_printf(str, "\n");
42-
}
43-
4433
void nfc_render_emv_data(const EmvData* data, FuriString* str) {
4534
nfc_render_emv_pan(data->emv_application.pan, data->emv_application.pan_len, str);
46-
nfc_render_emv_name(data->emv_application.name, str);
35+
nfc_render_emv_name(data->emv_application.application_name, str);
4736
}
4837

4938
void nfc_render_emv_pan(const uint8_t* data, const uint8_t len, FuriString* str) {
@@ -63,11 +52,6 @@ void nfc_render_emv_pan(const uint8_t* data, const uint8_t len, FuriString* str)
6352
furi_string_cat_printf(str, "\n");
6453
}
6554

66-
void nfc_render_emv_expired(const EmvApplication* apl, FuriString* str) {
67-
if(apl->exp_month == 0) return;
68-
furi_string_cat_printf(str, "Exp: %02X/%02X\n", apl->exp_month, apl->exp_year);
69-
}
70-
7155
void nfc_render_emv_currency(uint16_t cur_code, FuriString* str) {
7256
if(!cur_code) return;
7357

@@ -83,21 +67,23 @@ void nfc_render_emv_country(uint16_t country_code, FuriString* str) {
8367
void nfc_render_emv_application(const EmvApplication* apl, FuriString* str) {
8468
const uint8_t len = apl->aid_len;
8569

86-
if(!len) {
87-
furi_string_cat_printf(str, "No Pay Application found\n");
88-
return;
89-
}
90-
9170
furi_string_cat_printf(str, "AID: ");
92-
9371
for(uint8_t i = 0; i < len; i++) furi_string_cat_printf(str, "%02X", apl->aid[i]);
94-
9572
furi_string_cat_printf(str, "\n");
9673
}
9774

98-
static void nfc_render_emv_pin_try_counter(uint8_t counter, FuriString* str) {
99-
if(counter == 0xff) return;
100-
furi_string_cat_printf(str, "PIN attempts left: %d\n", counter);
75+
void nfc_render_emv_application_interchange_profile(const EmvApplication* apl, FuriString* str) {
76+
uint16_t data = bit_lib_bytes_to_num_be(apl->application_interchange_profile, 2);
77+
78+
if(!data) {
79+
furi_string_cat_printf(str, "No Interchange profile found\n");
80+
return;
81+
}
82+
83+
furi_string_cat_printf(str, "Interchange profile: ");
84+
for(uint8_t i = 0; i < 2; i++)
85+
furi_string_cat_printf(str, "%02X", apl->application_interchange_profile[i]);
86+
furi_string_cat_printf(str, "\n");
10187
}
10288

10389
void nfc_render_emv_transactions(const EmvApplication* apl, FuriString* str) {
@@ -126,23 +112,15 @@ void nfc_render_emv_transactions(const EmvApplication* apl, FuriString* str) {
126112
if(!apl->trans[i].amount) {
127113
furi_string_cat_printf(str, "???");
128114
} else {
129-
uint8_t* a = (uint8_t*)&apl->trans[i].amount;
130-
bool top = true;
131-
for(int x = 0; x < 6; x++) {
132-
// cents
133-
if(x == 5) {
134-
furi_string_cat_printf(str, ".%02X", a[x]);
135-
break;
136-
}
137-
if(a[x]) {
138-
if(top) {
139-
furi_string_cat_printf(str, "%X", a[x]);
140-
top = false;
141-
} else {
142-
furi_string_cat_printf(str, "%02X", a[x]);
143-
}
144-
}
145-
}
115+
FURI_LOG_D("EMV Render", "Amount: %llX\n", apl->trans[i].amount);
116+
uint8_t amount_bytes[6];
117+
bit_lib_num_to_bytes_le(apl->trans[i].amount, 6, amount_bytes);
118+
119+
bool junk = false;
120+
uint64_t amount = bit_lib_bytes_to_num_bcd(amount_bytes, 6, &junk);
121+
uint8_t amount_cents = amount % 100;
122+
123+
furi_string_cat_printf(str, "%llu.%02u", amount / 100, amount_cents);
146124
}
147125

148126
if(apl->trans[i].currency) {
@@ -183,8 +161,8 @@ void nfc_render_emv_transactions(const EmvApplication* apl, FuriString* str) {
183161

184162
void nfc_render_emv_extra(const EmvData* data, FuriString* str) {
185163
nfc_render_emv_application(&data->emv_application, str);
164+
nfc_render_emv_application_interchange_profile(&data->emv_application, str);
186165

187166
nfc_render_emv_currency(data->emv_application.currency_code, str);
188167
nfc_render_emv_country(data->emv_application.country_code, str);
189-
nfc_render_emv_pin_try_counter(data->emv_application.pin_try_counter, str);
190168
}

applications/main/nfc/helpers/protocol_support/emv/emv_render.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ void nfc_render_emv_name(const char* data, FuriString* str);
1515

1616
void nfc_render_emv_application(const EmvApplication* data, FuriString* str);
1717

18-
void nfc_render_emv_extra(const EmvData* data, FuriString* str);
18+
void nfc_render_emv_application_interchange_profile(const EmvApplication* apl, FuriString* str);
1919

20-
void nfc_render_emv_expired(const EmvApplication* apl, FuriString* str);
20+
void nfc_render_emv_extra(const EmvData* data, FuriString* str);
2121

2222
void nfc_render_emv_country(uint16_t country_code, FuriString* str);
2323

applications/main/nfc/plugins/supported_cards/emv.c

+60-8
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,11 @@ static bool emv_parse(const NfcDevice* device, FuriString* parsed_data) {
6868
const EmvApplication app = data->emv_application;
6969

7070
do {
71-
if(app.name_found)
72-
furi_string_cat_printf(parsed_data, "\e#%s\n", app.name);
73-
else
71+
if(strlen(app.application_label)) {
72+
furi_string_cat_printf(parsed_data, "\e#%s\n", app.application_label);
73+
} else if(strlen(app.application_name)) {
74+
furi_string_cat_printf(parsed_data, "\e#%s\n", app.application_name);
75+
} else
7476
furi_string_cat_printf(parsed_data, "\e#%s\n", "EMV");
7577

7678
if(app.pan_len) {
@@ -82,25 +84,75 @@ static bool emv_parse(const NfcDevice* device, FuriString* parsed_data) {
8284
// Cut padding 'F' from card number
8385
size_t end = furi_string_search_rchar(pan, 'F');
8486
if(end) furi_string_left(pan, end);
87+
furi_string_cat_printf(pan, "\n");
8588
furi_string_cat(parsed_data, pan);
89+
8690
furi_string_free(pan);
91+
parsed = true;
92+
}
93+
94+
if(strlen(app.cardholder_name)) {
95+
furi_string_cat_printf(parsed_data, "Cardholder name: %s\n", app.cardholder_name);
96+
parsed = true;
8797
}
8898

89-
if(app.exp_month | app.exp_year)
90-
furi_string_cat_printf(parsed_data, "\nExp: %02X/%02X\n", app.exp_month, app.exp_year);
99+
if(app.effective_month) {
100+
char day[] = "--";
101+
if(app.effective_day) itoa(app.effective_day, day, 16);
102+
if(day[1] == '\0') {
103+
day[1] = day[0];
104+
day[0] = '0';
105+
}
106+
107+
furi_string_cat_printf(
108+
parsed_data,
109+
"Effective: %s.%02X.20%02X\n",
110+
day,
111+
app.effective_month,
112+
app.effective_year);
113+
114+
parsed = true;
115+
}
116+
117+
if(app.exp_month) {
118+
char day[] = "--";
119+
if(app.exp_day) itoa(app.exp_day, day, 16);
120+
if(day[1] == '\0') {
121+
day[1] = day[0];
122+
day[0] = '0';
123+
}
124+
125+
furi_string_cat_printf(
126+
parsed_data, "Expires: %s.%02X.20%02X\n", day, app.exp_month, app.exp_year);
127+
128+
parsed = true;
129+
}
91130

92131
FuriString* str = furi_string_alloc();
93132
bool storage_readed = emv_get_country_name(app.country_code, str);
94133

95-
if(storage_readed)
134+
if(storage_readed) {
96135
furi_string_cat_printf(parsed_data, "Country: %s\n", furi_string_get_cstr(str));
136+
parsed = true;
137+
}
97138

98139
storage_readed = emv_get_currency_name(app.currency_code, str);
99-
if(storage_readed)
140+
if(storage_readed) {
100141
furi_string_cat_printf(parsed_data, "Currency: %s\n", furi_string_get_cstr(str));
142+
parsed = true;
143+
}
101144

102-
if(app.pin_try_counter != 0xFF)
145+
if(app.pin_try_counter != 0xFF) {
103146
furi_string_cat_printf(parsed_data, "PIN attempts left: %d\n", app.pin_try_counter);
147+
parsed = true;
148+
}
149+
150+
if((app.application_interchange_profile[1] >> 6) & 0b1) {
151+
furi_string_cat_printf(parsed_data, "Mobile: yes\n");
152+
parsed = true;
153+
}
154+
155+
if(!parsed) furi_string_cat_printf(parsed_data, "No data was parsed\n");
104156

105157
parsed = true;
106158
} while(false);

applications/main/nfc/scenes/nfc_scene_emv_more_info.c

+2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ bool nfc_scene_emv_more_info_on_event(void* context, SceneManagerEvent event) {
3737
const EmvData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolEmv);
3838

3939
if(event.type == SceneManagerEventTypeCustom) {
40+
widget_reset(nfc->widget);
41+
4042
if(event.event == SubmenuIndexTransactions) {
4143
FuriString* temp_str = furi_string_alloc();
4244
nfc_render_emv_transactions(&data->emv_application, temp_str);

lib/nfc/protocols/emv/emv.c

+36-8
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,14 @@ bool emv_load(EmvData* data, FlipperFormat* ff, uint32_t version) {
7676

7777
EmvApplication* app = &data->emv_application;
7878

79-
//Read name
80-
if(!flipper_format_read_string(ff, "Name", temp_str)) break;
81-
strcpy(app->name, furi_string_get_cstr(temp_str));
82-
if(app->name[0] != '\0') app->name_found = true;
79+
flipper_format_read_string(ff, "Cardholder name", temp_str);
80+
strcpy(app->cardholder_name, furi_string_get_cstr(temp_str));
81+
82+
flipper_format_read_string(ff, "Application name", temp_str);
83+
strcpy(app->application_name, furi_string_get_cstr(temp_str));
84+
85+
flipper_format_read_string(ff, "Application label", temp_str);
86+
strcpy(app->application_label, furi_string_get_cstr(temp_str));
8387

8488
uint32_t pan_len;
8589
if(!flipper_format_read_uint32(ff, "PAN length", &pan_len, 1)) break;
@@ -93,15 +97,24 @@ bool emv_load(EmvData* data, FlipperFormat* ff, uint32_t version) {
9397

9498
if(!flipper_format_read_hex(ff, "AID", app->aid, aid_len)) break;
9599

100+
if(!flipper_format_read_hex(
101+
ff, "Application interchange profile", app->application_interchange_profile, 2))
102+
break;
103+
96104
if(!flipper_format_read_hex(ff, "Country code", (uint8_t*)&app->country_code, 2)) break;
97105

98106
if(!flipper_format_read_hex(ff, "Currency code", (uint8_t*)&app->currency_code, 2)) break;
99107

100108
if(!flipper_format_read_hex(ff, "Expiration year", &app->exp_year, 1)) break;
101109
if(!flipper_format_read_hex(ff, "Expiration month", &app->exp_month, 1)) break;
110+
if(!flipper_format_read_hex(ff, "Expiration day", &app->exp_day, 1)) break;
111+
112+
if(!flipper_format_read_hex(ff, "Effective year", &app->effective_year, 1)) break;
113+
if(!flipper_format_read_hex(ff, "Effective month", &app->effective_month, 1)) break;
114+
if(!flipper_format_read_hex(ff, "Effective day", &app->effective_day, 1)) break;
102115

103116
uint32_t pin_try_counter;
104-
if(!flipper_format_read_uint32(ff, "PIN counter", &pin_try_counter, 1)) break;
117+
if(!flipper_format_read_uint32(ff, "PIN try counter", &pin_try_counter, 1)) break;
105118
app->pin_try_counter = pin_try_counter;
106119

107120
parsed = true;
@@ -124,7 +137,12 @@ bool emv_save(const EmvData* data, FlipperFormat* ff) {
124137

125138
if(!flipper_format_write_comment_cstr(ff, "EMV specific data:\n")) break;
126139

127-
if(!flipper_format_write_string_cstr(ff, "Name", app.name)) break;
140+
if(!flipper_format_write_string_cstr(ff, "Cardholder name", app.cardholder_name)) break;
141+
142+
if(!flipper_format_write_string_cstr(ff, "Application name", app.application_name)) break;
143+
144+
if(!flipper_format_write_string_cstr(ff, "Application label", app.application_label))
145+
break;
128146

129147
uint32_t pan_len = app.pan_len;
130148
if(!flipper_format_write_uint32(ff, "PAN length", &pan_len, 1)) break;
@@ -136,15 +154,25 @@ bool emv_save(const EmvData* data, FlipperFormat* ff) {
136154

137155
if(!flipper_format_write_hex(ff, "AID", app.aid, aid_len)) break;
138156

157+
if(!flipper_format_write_hex(
158+
ff, "Application interchange profile", app.application_interchange_profile, 2))
159+
break;
160+
139161
if(!flipper_format_write_hex(ff, "Country code", (uint8_t*)&app.country_code, 2)) break;
140162

141163
if(!flipper_format_write_hex(ff, "Currency code", (uint8_t*)&app.currency_code, 2)) break;
142164

143165
if(!flipper_format_write_hex(ff, "Expiration year", (uint8_t*)&app.exp_year, 1)) break;
144-
145166
if(!flipper_format_write_hex(ff, "Expiration month", (uint8_t*)&app.exp_month, 1)) break;
167+
if(!flipper_format_write_hex(ff, "Expiration day", (uint8_t*)&app.exp_day, 1)) break;
168+
169+
if(!flipper_format_write_hex(ff, "Effective year", (uint8_t*)&app.effective_year, 1))
170+
break;
171+
if(!flipper_format_write_hex(ff, "Effective month", (uint8_t*)&app.effective_month, 1))
172+
break;
173+
if(!flipper_format_write_hex(ff, "Effective day", (uint8_t*)&app.effective_day, 1)) break;
146174

147-
if(!flipper_format_write_uint32(ff, "PIN counter", (uint32_t*)&app.pin_try_counter, 1))
175+
if(!flipper_format_write_uint32(ff, "PIN try counter", (uint32_t*)&app.pin_try_counter, 1))
148176
break;
149177

150178
saved = true;

0 commit comments

Comments
 (0)