Skip to content

Commit 0f5b1ff

Browse files
authored
Merge pull request #26 from SnowLeopard71/dev
Rework NFC EMV response parsing. Split TLV and tags per EMV spec
2 parents 01e082e + 7b8855d commit 0f5b1ff

File tree

1 file changed

+112
-97
lines changed

1 file changed

+112
-97
lines changed

lib/nfc_protocols/emv.c

+112-97
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ const PDOLValue pdol_merchant_type = {0x9F58, {0x01}}; // Merchant type indicato
1010
const PDOLValue pdol_term_trans_qualifies = {
1111
0x9F66,
1212
{0x79, 0x00, 0x40, 0x80}}; // Terminal transaction qualifiers
13+
const PDOLValue pdol_addtnl_term_qualifies = {
14+
0x9F40,
15+
{0x79, 0x00, 0x40, 0x80}}; // Terminal transaction qualifiers
1316
const PDOLValue pdol_amount_authorise = {
1417
0x9F02,
1518
{0x00, 0x00, 0x00, 0x10, 0x00, 0x00}}; // Amount, authorised
@@ -30,6 +33,7 @@ const PDOLValue* const pdol_values[] = {
3033
&pdol_term_type,
3134
&pdol_merchant_type,
3235
&pdol_term_trans_qualifies,
36+
&pdol_addtnl_term_qualifies,
3337
&pdol_amount_authorise,
3438
&pdol_amount,
3539
&pdol_country_code,
@@ -61,49 +65,121 @@ static const uint8_t pdol_ans[] = {0x77, 0x40, 0x82, 0x02, 0x20, 0x00, 0x57, 0x1
6165
static void emv_trace(FuriHalNfcTxRxContext* tx_rx, const char* message) {
6266
if(furi_log_get_level() == FuriLogLevelTrace) {
6367
FURI_LOG_T(TAG, "%s", message);
68+
printf("TX: ");
69+
for(size_t i = 0; i < tx_rx->tx_bits / 8; i++) {
70+
printf("%02X ", tx_rx->tx_data[i]);
71+
}
72+
printf("\r\nRX: ");
6473
for(size_t i = 0; i < tx_rx->rx_bits / 8; i++) {
6574
printf("%02X ", tx_rx->rx_data[i]);
6675
}
6776
printf("\r\n");
6877
}
6978
}
7079

71-
static uint16_t emv_parse_TLV(uint8_t* dest, uint8_t* src, uint16_t* idx) {
72-
uint8_t len = src[*idx + 1];
73-
memcpy(dest, &src[*idx + 2], len);
74-
*idx = *idx + len + 1;
75-
return len;
76-
}
77-
78-
static bool emv_decode_search_tag_u16_r(uint16_t tag, uint8_t* buff, uint16_t* idx) {
79-
if((buff[*idx] << 8 | buff[*idx + 1]) == tag) {
80-
*idx = *idx + 3;
81-
return true;
82-
}
83-
return false;
84-
}
85-
86-
bool emv_decode_ppse_response(uint8_t* buff, uint16_t len, EmvApplication* app) {
80+
static bool emv_decode_response(uint8_t* buff, uint16_t len, EmvApplication* app) {
8781
uint16_t i = 0;
88-
bool app_aid_found = false;
82+
uint16_t tag = 0, fb = 0;
83+
uint16_t tlen = 0;
84+
bool success = false;
8985

9086
while(i < len) {
91-
if(buff[i] == EMV_TAG_APP_TEMPLATE) {
92-
uint8_t app_len = buff[++i];
93-
for(uint16_t j = i; j < MIN(i + app_len, len - 1); j++) {
94-
if(buff[j] == EMV_TAG_AID) {
95-
app_aid_found = true;
96-
app->aid_len = buff[j + 1];
97-
emv_parse_TLV(app->aid, buff, &j);
98-
} else if(buff[j] == EMV_TAG_PRIORITY) {
99-
emv_parse_TLV(&app->priority, buff, &j);
87+
fb = buff[i]; // first byte
88+
if((fb & 31) == 31) { // 2-byte tag
89+
tag = buff[i] << 8 | buff[i + 1];
90+
i++;
91+
FURI_LOG_T(TAG, " 2-byte TLV EMV tag: %x", tag);
92+
} else {
93+
tag = buff[i];
94+
FURI_LOG_T(TAG, " 1-byte TLV EMV tag: %x", tag);
95+
}
96+
i++;
97+
tlen = buff[i];
98+
if((tlen & 128) == 128) { // long length value
99+
i++;
100+
tlen = buff[i];
101+
FURI_LOG_T(TAG, " 2-byte TLV length: %d", tlen);
102+
} else {
103+
FURI_LOG_T(TAG, " 1-byte TLV length: %d", tlen);
104+
}
105+
i++;
106+
if((fb & 32) == 32) { // "Constructed" -- contains more TLV data to parse
107+
FURI_LOG_T(TAG, "Constructed TLV %x", tag);
108+
if(!emv_decode_response(&buff[i], tlen, app)) {
109+
printf("Failed to decode response for %x \r\n", tag);
110+
// return false;
111+
} else {
112+
success = true;
113+
}
114+
} else {
115+
switch(tag) {
116+
case EMV_TAG_AID:
117+
app->aid_len = tlen;
118+
memcpy(app->aid, &buff[i], tlen);
119+
success = true;
120+
FURI_LOG_T(TAG, "found EMV_TAG_AID %x", tag);
121+
break;
122+
case EMV_TAG_PRIORITY:
123+
memcpy(&app->priority, &buff[i], tlen);
124+
success = true;
125+
break;
126+
case EMV_TAG_CARD_NAME:
127+
memcpy(app->name, &buff[i], tlen);
128+
app->name[tlen] = '\0';
129+
app->name_found = true;
130+
success = true;
131+
FURI_LOG_T(TAG, "found EMV_TAG_CARD_NAME %x : %s", tag, app->name);
132+
break;
133+
case EMV_TAG_PDOL:
134+
memcpy(app->pdol.data, &buff[i], tlen);
135+
app->pdol.size = tlen;
136+
success = true;
137+
FURI_LOG_T(TAG, "found EMV_TAG_PDOL %x (len=%d)", tag, tlen);
138+
break;
139+
case EMV_TAG_AFL:
140+
memcpy(app->afl.data, &buff[i], tlen);
141+
app->afl.size = tlen;
142+
success = true;
143+
FURI_LOG_T(TAG, "found EMV_TAG_AFL %x (len=%d)", tag, tlen);
144+
break;
145+
case EMV_TAG_CARD_NUM: // Track 2 Equivalent Data. 0xD0 delimits PAN from expiry (YYMM)
146+
for(int x = 1; x < tlen; x++) {
147+
if(buff[i + x + 1] > 0xD0) {
148+
memcpy(app->card_number, &buff[i], x + 1);
149+
app->card_number_len = x + 1;
150+
break;
151+
}
100152
}
153+
success = true;
154+
FURI_LOG_T(
155+
TAG,
156+
"found EMV_TAG_CARD_NUM %x (len=%d)",
157+
EMV_TAG_CARD_NUM,
158+
app->card_number_len);
159+
break;
160+
case EMV_TAG_PAN:
161+
memcpy(app->card_number, &buff[i], tlen);
162+
app->card_number_len = tlen;
163+
success = true;
164+
break;
165+
case EMV_TAG_EXP_DATE:
166+
app->exp_year = buff[i];
167+
app->exp_month = buff[i + 1];
168+
success = true;
169+
break;
170+
case EMV_TAG_CURRENCY_CODE:
171+
app->currency_code = (buff[i] << 8 | buff[i + 1]);
172+
success = true;
173+
break;
174+
case EMV_TAG_COUNTRY_CODE:
175+
app->country_code = (buff[i] << 8 | buff[i + 1]);
176+
success = true;
177+
break;
101178
}
102-
i += app_len;
103179
}
104-
i++;
180+
i += tlen;
105181
}
106-
return app_aid_found;
182+
return success;
107183
}
108184

109185
bool emv_select_ppse(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
@@ -124,7 +200,7 @@ bool emv_select_ppse(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
124200
FURI_LOG_D(TAG, "Send select PPSE");
125201
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
126202
emv_trace(tx_rx, "Select PPSE answer:");
127-
if(emv_decode_ppse_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
203+
if(emv_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
128204
app_aid_found = true;
129205
} else {
130206
FURI_LOG_E(TAG, "Failed to parse application");
@@ -136,28 +212,6 @@ bool emv_select_ppse(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
136212
return app_aid_found;
137213
}
138214

139-
static bool emv_decode_select_app_response(uint8_t* buff, uint16_t len, EmvApplication* app) {
140-
uint16_t i = 0;
141-
bool decode_success = false;
142-
143-
while(i < len) {
144-
if(buff[i] == EMV_TAG_CARD_NAME) {
145-
uint8_t name_len = buff[i + 1];
146-
emv_parse_TLV((uint8_t*)app->name, buff, &i);
147-
app->name[name_len] = '\0';
148-
app->name_found = true;
149-
decode_success = true;
150-
} else if(((buff[i] << 8) | buff[i + 1]) == EMV_TAG_PDOL) {
151-
i++;
152-
app->pdol.size = emv_parse_TLV(app->pdol.data, buff, &i);
153-
decode_success = true;
154-
}
155-
i++;
156-
}
157-
158-
return decode_success;
159-
}
160-
161215
bool emv_select_app(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
162216
bool select_app_success = false;
163217
const uint8_t emv_select_header[] = {
@@ -181,7 +235,7 @@ bool emv_select_app(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
181235
FURI_LOG_D(TAG, "Start application");
182236
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
183237
emv_trace(tx_rx, "Start application answer:");
184-
if(emv_decode_select_app_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
238+
if(emv_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
185239
select_app_success = true;
186240
} else {
187241
FURI_LOG_E(TAG, "Failed to read PAN or PDOL");
@@ -226,22 +280,6 @@ static uint16_t emv_prepare_pdol(APDU* dest, APDU* src) {
226280
return dest->size;
227281
}
228282

229-
static bool emv_decode_get_proc_opt(uint8_t* buff, uint16_t len, EmvApplication* app) {
230-
bool card_num_read = false;
231-
232-
for(uint16_t i = 0; i < len; i++) {
233-
if(buff[i] == EMV_TAG_CARD_NUM) {
234-
app->card_number_len = 8;
235-
memcpy(app->card_number, &buff[i + 2], app->card_number_len);
236-
card_num_read = true;
237-
} else if(buff[i] == EMV_TAG_AFL) {
238-
app->afl.size = emv_parse_TLV(app->afl.data, buff, &i);
239-
}
240-
}
241-
242-
return card_num_read;
243-
}
244-
245283
static bool emv_get_processing_options(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) {
246284
bool card_num_read = false;
247285
const uint8_t emv_gpo_header[] = {0x80, 0xA8, 0x00, 0x00};
@@ -264,8 +302,10 @@ static bool emv_get_processing_options(FuriHalNfcTxRxContext* tx_rx, EmvApplicat
264302
FURI_LOG_D(TAG, "Get proccessing options");
265303
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
266304
emv_trace(tx_rx, "Get processing options answer:");
267-
if(emv_decode_get_proc_opt(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
268-
card_num_read = true;
305+
if(emv_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
306+
if(app->card_number_len > 0) {
307+
card_num_read = true;
308+
}
269309
}
270310
} else {
271311
FURI_LOG_E(TAG, "Failed to get processing options");
@@ -274,31 +314,6 @@ static bool emv_get_processing_options(FuriHalNfcTxRxContext* tx_rx, EmvApplicat
274314
return card_num_read;
275315
}
276316

277-
static bool emv_decode_read_sfi_record(uint8_t* buff, uint16_t len, EmvApplication* app) {
278-
bool pan_parsed = false;
279-
280-
for(uint16_t i = 0; i < len; i++) {
281-
if(buff[i] == EMV_TAG_PAN) {
282-
if(buff[i + 1] == 8 || buff[i + 1] == 10) {
283-
app->card_number_len = buff[i + 1];
284-
memcpy(app->card_number, &buff[i + 2], app->card_number_len);
285-
pan_parsed = true;
286-
}
287-
} else if(emv_decode_search_tag_u16_r(EMV_TAG_EXP_DATE, buff, &i)) {
288-
app->exp_year = buff[i++];
289-
app->exp_month = buff[i++];
290-
} else if(emv_decode_search_tag_u16_r(EMV_TAG_CURRENCY_CODE, buff, &i)) {
291-
app->currency_code = (buff[i] << 8) | buff[i + 1];
292-
i += 2;
293-
} else if(emv_decode_search_tag_u16_r(EMV_TAG_COUNTRY_CODE, buff, &i)) {
294-
app->country_code = (buff[i] << 8) | buff[i + 1];
295-
i += 1;
296-
}
297-
}
298-
299-
return pan_parsed;
300-
}
301-
302317
static bool emv_read_sfi_record(
303318
FuriHalNfcTxRxContext* tx_rx,
304319
EmvApplication* app,
@@ -320,7 +335,7 @@ static bool emv_read_sfi_record(
320335

321336
if(furi_hal_nfc_tx_rx(tx_rx, 300)) {
322337
emv_trace(tx_rx, "SFI record:");
323-
if(emv_decode_read_sfi_record(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
338+
if(emv_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) {
324339
card_num_read = true;
325340
}
326341
} else {

0 commit comments

Comments
 (0)