mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2024-12-26 23:05:05 +03:00
read transactions fail on some cards fixed
This commit is contained in:
parent
c061fb1681
commit
7b01a33b3f
@ -112,6 +112,7 @@ void nfc_render_emv_transactions(const EmvApplication* apl, FuriString* str) {
|
|||||||
if(!apl->trans[i].amount) {
|
if(!apl->trans[i].amount) {
|
||||||
furi_string_cat_printf(str, "???");
|
furi_string_cat_printf(str, "???");
|
||||||
} else {
|
} else {
|
||||||
|
FURI_LOG_D("EMV Render", "Amount: %llX\n", apl->trans[i].amount);
|
||||||
uint8_t amount_bytes[6];
|
uint8_t amount_bytes[6];
|
||||||
bit_lib_num_to_bytes_le(apl->trans[i].amount, 6, amount_bytes);
|
bit_lib_num_to_bytes_le(apl->trans[i].amount, 6, amount_bytes);
|
||||||
|
|
||||||
|
@ -99,7 +99,8 @@ static NfcCommand emv_poller_handler_get_processing_options(EmvPoller* instance)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static NfcCommand emv_poller_handler_read_files(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);
|
emv_poller_read_log_entry(instance);
|
||||||
|
|
||||||
instance->state = EmvPollerStateReadExtra;
|
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_last_online_atc(instance);
|
||||||
emv_poller_get_pin_try_counter(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;
|
instance->state = EmvPollerStateReadSuccess;
|
||||||
return NfcCommandContinue;
|
return NfcCommandContinue;
|
||||||
}
|
}
|
||||||
|
@ -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_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);
|
EmvError emv_poller_read_log_entry(EmvPoller* instance);
|
||||||
|
|
||||||
|
@ -621,75 +621,77 @@ EmvError emv_poller_read_sfi_record(EmvPoller* instance, uint8_t sfi, uint8_t re
|
|||||||
return error;
|
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;
|
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);
|
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);
|
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))
|
if(!bruteforce_sfi) {
|
||||||
cardholder_name_fetched = true;
|
// SEARCH PAN, RETURN WHEN FOUND
|
||||||
error = emv_poller_read_sfi_record(instance, sfi, record);
|
APDU* afl = &instance->data->emv_application.afl;
|
||||||
if(error != EmvErrorNone) break;
|
|
||||||
|
|
||||||
if(!emv_decode_response_tlv(
|
if(afl->size == 0) {
|
||||||
bit_buffer_get_data(instance->rx_buffer),
|
return false;
|
||||||
bit_buffer_get_size_bytes(instance->rx_buffer),
|
}
|
||||||
&instance->data->emv_application)) {
|
|
||||||
error = EmvErrorProtocol;
|
FURI_LOG_D(TAG, "Search PAN in SFI");
|
||||||
FURI_LOG_T(TAG, "Failed to parse SFI 0x%X record %d", sfi, record);
|
|
||||||
|
// 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;
|
return EmvErrorNone;
|
||||||
else
|
else
|
||||||
return error;
|
return error;
|
||||||
|
@ -39,6 +39,7 @@ struct EmvPoller {
|
|||||||
EmvPollerEvent emv_event;
|
EmvPollerEvent emv_event;
|
||||||
NfcGenericEvent general_event;
|
NfcGenericEvent general_event;
|
||||||
NfcGenericCallback callback;
|
NfcGenericCallback callback;
|
||||||
|
uint16_t records_mask;
|
||||||
void* context;
|
void* context;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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_last_online_atc,EmvError,EmvPoller*
|
||||||
Function,+,emv_poller_get_pin_try_counter,EmvError,EmvPoller*
|
Function,+,emv_poller_get_pin_try_counter,EmvError,EmvPoller*
|
||||||
Function,+,emv_poller_get_processing_options,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_log_entry,EmvError,EmvPoller*
|
||||||
Function,+,emv_poller_read_sfi_record,EmvError,"EmvPoller*, uint8_t, uint8_t"
|
Function,+,emv_poller_read_sfi_record,EmvError,"EmvPoller*, uint8_t, uint8_t"
|
||||||
Function,+,emv_poller_select_application,EmvError,EmvPoller*
|
Function,+,emv_poller_select_application,EmvError,EmvPoller*
|
||||||
|
|
Loading…
Reference in New Issue
Block a user