diff --git a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c index 9e315ff47..ba6866a50 100644 --- a/applications/main/nfc/helpers/protocol_support/emv/emv_render.c +++ b/applications/main/nfc/helpers/protocol_support/emv/emv_render.c @@ -112,6 +112,7 @@ void nfc_render_emv_transactions(const EmvApplication* apl, FuriString* str) { if(!apl->trans[i].amount) { furi_string_cat_printf(str, "???"); } else { + FURI_LOG_D("EMV Render", "Amount: %llX\n", apl->trans[i].amount); uint8_t amount_bytes[6]; bit_lib_num_to_bytes_le(apl->trans[i].amount, 6, amount_bytes); diff --git a/lib/nfc/protocols/emv/emv_poller.c b/lib/nfc/protocols/emv/emv_poller.c index 9acb854d6..3042352fe 100644 --- a/lib/nfc/protocols/emv/emv_poller.c +++ b/lib/nfc/protocols/emv/emv_poller.c @@ -99,7 +99,8 @@ static NfcCommand emv_poller_handler_get_processing_options(EmvPoller* instance) } static NfcCommand emv_poller_handler_read_files(EmvPoller* instance) { - emv_poller_read_afl(instance); + // Search PAN + emv_poller_read_afl(instance, false, &instance->records_mask); emv_poller_read_log_entry(instance); instance->state = EmvPollerStateReadExtra; @@ -110,6 +111,9 @@ static NfcCommand emv_poller_handler_read_extra_data(EmvPoller* instance) { emv_poller_get_last_online_atc(instance); emv_poller_get_pin_try_counter(instance); + // Search cardholder name. This operation may break communication with the card, so it should be the last one + emv_poller_read_afl(instance, true, &instance->records_mask); + instance->state = EmvPollerStateReadSuccess; return NfcCommandContinue; } diff --git a/lib/nfc/protocols/emv/emv_poller.h b/lib/nfc/protocols/emv/emv_poller.h index 64bd0be9d..9cae80195 100644 --- a/lib/nfc/protocols/emv/emv_poller.h +++ b/lib/nfc/protocols/emv/emv_poller.h @@ -46,7 +46,7 @@ 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_afl(EmvPoller* instance, bool bruteforce_sfi, uint16_t* readed_mask); EmvError emv_poller_read_log_entry(EmvPoller* instance); diff --git a/lib/nfc/protocols/emv/emv_poller_i.c b/lib/nfc/protocols/emv/emv_poller_i.c index e72432287..74734c894 100644 --- a/lib/nfc/protocols/emv/emv_poller_i.c +++ b/lib/nfc/protocols/emv/emv_poller_i.c @@ -621,75 +621,77 @@ EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t re return error; } -EmvError emv_poller_read_afl(EmvPoller* instance) { +EmvError emv_poller_read_afl(EmvPoller* instance, bool bruteforce_sfi, uint16_t* readed_mask) { EmvError error = EmvErrorNone; - - APDU* afl = &instance->data->emv_application.afl; - - if(afl->size == 0) { - return false; - } - - FURI_LOG_D(TAG, "Search PAN in SFI"); - - uint8_t sfi_2_mask = 0; - uint8_t sfi_3_mask = 0; - bool pan_fetched = (instance->data->emv_application.pan_len); - - // 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) { - if((sfi == 2) && (record < 8)) FURI_BIT_SET(sfi_2_mask, record); - if((sfi == 3) && (record < 8)) FURI_BIT_SET(sfi_3_mask, 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); - } - - if(instance->data->emv_application.pan_len) pan_fetched = true; // Card number fetched - } - } bool cardholder_name_fetched = strlen(instance->data->emv_application.cardholder_name); - // Bruteforse files 2-3 - FURI_LOG_T(TAG, "Bruteforce files 2-3"); - for(size_t sfi = 2; sfi <= 3; sfi++) { - // Iterate through records 1-5 in file - for(size_t record = 1; record <= 5; record++) { - // Skip previously readed sfi - if(sfi == 2) { - if((sfi_2_mask >> record) & (0b1)) continue; - } - if(sfi == 3) { - if((sfi_3_mask >> record) & (0b1)) continue; - } - if(strlen(instance->data->emv_application.cardholder_name)) - cardholder_name_fetched = true; - error = emv_poller_read_sfi_record(instance, sfi, record); - if(error != EmvErrorNone) break; + if(!bruteforce_sfi) { + // SEARCH PAN, RETURN WHEN FOUND + APDU* afl = &instance->data->emv_application.afl; - 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); + 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) { + if((sfi <= 3) && (record <= 5)) + FURI_BIT_SET( + *readed_mask, + record + ((sfi - 2) * 8)); //black magic: mask 0003333300022222 + + 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); + } + + if(instance->data->emv_application.pan_len) { + pan_fetched = true; + break; + } // Card number fetched + } + if(pan_fetched) break; + } + } else { // BRUTFORCE FILES 2-3. SEARCH CARDHOLDER NAME + FURI_LOG_T(TAG, "Bruteforce files 2-3"); + for(size_t sfi = 2; sfi <= 3; sfi++) { + // Iterate through records 1-5 in file + for(size_t record = 1; record <= 5; record++) { + // Skip previously readed sfi + if((*readed_mask >> (record + ((sfi - 2) * 8))) & (0b1)) continue; + + 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); + } + + if(strlen(instance->data->emv_application.cardholder_name)) + cardholder_name_fetched = true; } } } - if(pan_fetched || cardholder_name_fetched) + + if((pan_fetched && (!bruteforce_sfi)) || (cardholder_name_fetched && bruteforce_sfi)) return EmvErrorNone; else return error; diff --git a/lib/nfc/protocols/emv/emv_poller_i.h b/lib/nfc/protocols/emv/emv_poller_i.h index 704365747..cec48f800 100644 --- a/lib/nfc/protocols/emv/emv_poller_i.h +++ b/lib/nfc/protocols/emv/emv_poller_i.h @@ -39,6 +39,7 @@ struct EmvPoller { EmvPollerEvent emv_event; NfcGenericEvent general_event; NfcGenericCallback callback; + uint16_t records_mask; void* context; }; diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 84bae90ab..a2b2b6614 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -909,7 +909,7 @@ 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_afl,EmvError,"EmvPoller*, _Bool, uint16_t*" 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*