Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NFC: EMV expiry date month bug #289

Draft
wants to merge 1 commit into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 21 additions & 86 deletions lib/nfc/helpers/iso14443_4_layer.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,22 @@

#include <furi.h>

// EMV Specific masks
#define ISO14443_4_BLOCK_PCB (1U << 1)
#define ISO14443_4_BLOCK_PCB_I (0U)
#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_WTX (3U << 4)
#define ISO14443_4_BLOCK_PCB_S (3U << 6)
//

#define ISO14443_4_BLOCK_PCB (1U << 1)
#define ISO14443_4_BLOCK_PCB_MASK (0x03)

#define ISO14443_4_BLOCK_PCB_I (0U)
#define ISO14443_4_BLOCK_PCB_I_NAD_OFFSET (2)
#define ISO14443_4_BLOCK_PCB_I_CID_OFFSET (3)
#define ISO14443_4_BLOCK_PCB_I_CHAIN_OFFSET (4)
#define ISO14443_4_BLOCK_PCB_I_NAD_MASK (1U << ISO14443_4_BLOCK_PCB_I_NAD_OFFSET)
#define ISO14443_4_BLOCK_PCB_I_CID_MASK (1U << ISO14443_4_BLOCK_PCB_I_CID_OFFSET)
#define ISO14443_4_BLOCK_PCB_I_CHAIN_MASK (1U << ISO14443_4_BLOCK_PCB_I_CHAIN_OFFSET)

#define ISO14443_4_BLOCK_PCB_R_MASK (5U << 5)
#define ISO14443_4_BLOCK_PCB_R_NACK_OFFSET (4)
#define ISO14443_4_BLOCK_PCB_R_CID_OFFSET (3)
#define ISO14443_4_BLOCK_PCB_R_CID_MASK (1U << ISO14443_4_BLOCK_PCB_R_CID_OFFSET)
#define ISO14443_4_BLOCK_PCB_R_NACK_MASK (1U << ISO14443_4_BLOCK_PCB_R_NACK_OFFSET)

#define ISO14443_4_BLOCK_PCB_S_MASK (3U << 6)
#define ISO14443_4_BLOCK_PCB_S_CID_OFFSET (3)
#define ISO14443_4_BLOCK_PCB_S_WTX_DESELECT_OFFSET (4)
#define ISO14443_4_BLOCK_PCB_S_CID_MASK (1U << ISO14443_4_BLOCK_PCB_R_CID_OFFSET)
#define ISO14443_4_BLOCK_PCB_S_WTX_DESELECT_MASK (3U << ISO14443_4_BLOCK_PCB_S_WTX_DESELECT_OFFSET)

#define ISO14443_4_BLOCK_PCB_BITS_ACTIVE(pcb, mask) (((pcb) & (mask)) == (mask))

#define ISO14443_4_BLOCK_PCB_IS_R_BLOCK(pcb) \
ISO14443_4_BLOCK_PCB_BITS_ACTIVE(pcb, ISO14443_4_BLOCK_PCB_R_MASK)
#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_IS_S_BLOCK(pcb) \
ISO14443_4_BLOCK_PCB_BITS_ACTIVE(pcb, ISO14443_4_BLOCK_PCB_S_MASK)

#define ISO14443_4_BLOCK_PCB_IS_CHAIN_ACTIVE(pcb) \
ISO14443_4_BLOCK_PCB_BITS_ACTIVE(pcb, ISO14443_4_BLOCK_PCB_I_CHAIN_MASK)

#define ISO14443_4_BLOCK_PCB_R_NACK_ACTIVE(pcb) \
ISO14443_4_BLOCK_PCB_BITS_ACTIVE(pcb, ISO14443_4_BLOCK_PCB_R_NACK_MASK)
#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;
Expand All @@ -71,31 +43,9 @@ void iso14443_4_layer_free(Iso14443_4Layer* instance) {

void iso14443_4_layer_reset(Iso14443_4Layer* instance) {
furi_assert(instance);
instance->pcb_prev = 0;
instance->pcb = ISO14443_4_BLOCK_PCB_I | ISO14443_4_BLOCK_PCB;
}

void iso14443_4_layer_set_i_block(Iso14443_4Layer* instance, bool chaining, bool CID_present) {
uint8_t block_pcb = instance->pcb & ISO14443_4_BLOCK_PCB_MASK;
instance->pcb = ISO14443_4_BLOCK_PCB_I | (chaining << ISO14443_4_BLOCK_PCB_I_CHAIN_OFFSET) |
(CID_present << ISO14443_4_BLOCK_PCB_I_CID_OFFSET) | block_pcb;
}

void iso14443_4_layer_set_r_block(Iso14443_4Layer* instance, bool acknowledged, bool CID_present) {
furi_assert(instance);
uint8_t block_pcb = instance->pcb & ISO14443_4_BLOCK_PCB_MASK;
instance->pcb = ISO14443_4_BLOCK_PCB_R_MASK |
(!acknowledged << ISO14443_4_BLOCK_PCB_R_NACK_OFFSET) |
(CID_present << ISO14443_4_BLOCK_PCB_R_CID_OFFSET) | block_pcb;
}

void iso14443_4_layer_set_s_block(Iso14443_4Layer* instance, bool deselect, bool CID_present) {
furi_assert(instance);
uint8_t des_wtx = !deselect ? (ISO14443_4_BLOCK_PCB_S_WTX_DESELECT_MASK) : 0;
instance->pcb = ISO14443_4_BLOCK_PCB_S_MASK | des_wtx |
(CID_present << ISO14443_4_BLOCK_PCB_S_CID_OFFSET) | ISO14443_4_BLOCK_PCB;
}

void iso14443_4_layer_encode_block(
Iso14443_4Layer* instance,
const BitBuffer* input_data,
Expand All @@ -108,11 +58,6 @@ void iso14443_4_layer_encode_block(
iso14443_4_layer_update_pcb(instance);
}

static inline uint8_t iso14443_4_layer_get_response_pcb(const BitBuffer* block_data) {
const uint8_t* data = bit_buffer_get_data(block_data);
return data[0];
}

bool iso14443_4_layer_decode_block(
Iso14443_4Layer* instance,
BitBuffer* output_data,
Expand All @@ -121,27 +66,17 @@ bool iso14443_4_layer_decode_block(

bool ret = false;

// TODO: Fix properly! this is a very big kostyl na velosipede
// (bit_buffer_copy_right are called to copy bigger buffer into smaller buffer causing crash on furi check) issue comes iso14443_4a_poller_send_block at line 109
// Mimicks furi_check()s in bit_buffer_copy_right(): buf=output_data other=block_data start_index=1
if(!(bit_buffer_get_size_bytes(block_data) > 1)) return ret;
if(!(bit_buffer_get_capacity_bytes(output_data) >= bit_buffer_get_size_bytes(block_data) - 1))
return ret;

do {
if(ISO14443_4_BLOCK_PCB_IS_R_BLOCK(instance->pcb_prev)) {
const uint8_t response_pcb = iso14443_4_layer_get_response_pcb(block_data);
ret = (ISO14443_4_BLOCK_PCB_IS_R_BLOCK(response_pcb)) &&
(!ISO14443_4_BLOCK_PCB_R_NACK_ACTIVE(response_pcb));
instance->pcb &= ISO14443_4_BLOCK_PCB_MASK;
iso14443_4_layer_update_pcb(instance);
} else if(ISO14443_4_BLOCK_PCB_IS_CHAIN_ACTIVE(instance->pcb_prev)) {
const uint8_t response_pcb = iso14443_4_layer_get_response_pcb(block_data);
ret = (ISO14443_4_BLOCK_PCB_IS_R_BLOCK(response_pcb)) &&
(!ISO14443_4_BLOCK_PCB_R_NACK_ACTIVE(response_pcb));
instance->pcb &= ~(ISO14443_4_BLOCK_PCB_I_CHAIN_MASK);
} else if(ISO14443_4_BLOCK_PCB_IS_S_BLOCK(instance->pcb_prev)) {
ret = bit_buffer_starts_with_byte(block_data, instance->pcb_prev);
if(bit_buffer_get_size_bytes(block_data) > 1)
bit_buffer_copy_right(output_data, block_data, 1);
} else {
if(!bit_buffer_starts_with_byte(block_data, instance->pcb_prev)) break;
bit_buffer_copy_right(output_data, block_data, 1);
ret = true;
}
if(!bit_buffer_starts_with_byte(block_data, instance->pcb_prev)) break;
bit_buffer_copy_right(output_data, block_data, 1);
ret = true;
} while(false);

return ret;
Expand Down
4 changes: 0 additions & 4 deletions lib/nfc/helpers/iso14443_4_layer.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ void iso14443_4_layer_free(Iso14443_4Layer* instance);

void iso14443_4_layer_reset(Iso14443_4Layer* instance);

void iso14443_4_layer_set_i_block(Iso14443_4Layer* instance, bool chaining, bool CID_present);
void iso14443_4_layer_set_r_block(Iso14443_4Layer* instance, bool acknowledged, bool CID_present);
void iso14443_4_layer_set_s_block(Iso14443_4Layer* instance, bool deselect, bool CID_present);

void iso14443_4_layer_encode_block(
Iso14443_4Layer* instance,
const BitBuffer* input_data,
Expand Down
14 changes: 4 additions & 10 deletions lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,10 @@ static NfcCommand iso14443_4a_listener_run(NfcGenericEvent event, void* context)
if(instance->state == Iso14443_4aListenerStateIdle) {
if(bit_buffer_get_size_bytes(rx_buffer) == 2 &&
bit_buffer_get_byte(rx_buffer, 0) == ISO14443_4A_CMD_READ_ATS) {
if(iso14443_4a_listener_send_ats(instance, &instance->data->ats_data) ==
if(iso14443_4a_listener_send_ats(instance, &instance->data->ats_data) !=
Iso14443_4aErrorNone) {
command = NfcCommandContinue;
} else {
instance->state = Iso14443_4aListenerStateActive;
}
}
Expand All @@ -82,15 +84,7 @@ static NfcCommand iso14443_4a_listener_run(NfcGenericEvent event, void* context)
iso14443_3a_event->type == Iso14443_3aListenerEventTypeHalted ||
iso14443_3a_event->type == Iso14443_3aListenerEventTypeFieldOff) {
instance->state = Iso14443_4aListenerStateIdle;

instance->iso14443_4a_event.type = iso14443_3a_event->type ==
Iso14443_3aListenerEventTypeHalted ?
Iso14443_4aListenerEventTypeHalted :
Iso14443_4aListenerEventTypeFieldOff;

if(instance->callback) {
command = instance->callback(instance->generic_event, instance->context);
}
command = NfcCommandContinue;
}

return command;
Expand Down
1 change: 0 additions & 1 deletion lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ typedef struct Iso14443_4aListener Iso14443_4aListener;

typedef enum {
Iso14443_4aListenerEventTypeHalted,
Iso14443_4aListenerEventTypeFieldOff,
Iso14443_4aListenerEventTypeReceivedData,
} Iso14443_4aListenerEventType;

Expand Down
63 changes: 0 additions & 63 deletions lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,69 +61,6 @@ Iso14443_4aError iso14443_4a_poller_send_block_pwt_ext(
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer);

/**
* @brief Transmit and receive Iso14443_4a chained block in poller mode. Also it
* automatically modifies PCB packet byte with appropriate bits then resets them back
*
* Must ONLY be used inside the callback function.
*
* The rx_buffer will be filled with any data received as a response to data
* sent from tx_buffer. The fwt parameter is calculated during activation procedure.
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
* @param[out] rx_buffer pointer to the buffer to be filled with received data.
* @return Iso14443_4aErrorNone on success, an error code on failure.
*/
Iso14443_4aError iso14443_4a_poller_send_chain_block(
Iso14443_4aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer);

/**
* @brief Transmit Iso14443_4a R-block in poller mode. This block never contains
* data, but can contain CID and NAD, therefore in tx_buffer only two bytes can be added.
* The first one will represent CID, the second one will represent NAD.
*
* Must ONLY be used inside the callback function.
*
* The rx_buffer will be filled with R-block repsonse
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in] acknowledged Sets appropriate bit in PCB byte. True - ACK, false - NAK
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
* @param[out] rx_buffer pointer to the buffer to be filled with received data.
* @return Iso14443_4aErrorNone on success, an error code on failure.
*/
Iso14443_4aError iso14443_4a_poller_send_receive_ready_block(
Iso14443_4aPoller* instance,
bool acknowledged,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer);

/**
* @brief Transmit Iso14443_4a S-block in poller mode. S-block used to exchange control
* information between the card and the reader. Two different types of S-blocks
* are defined:
* - Waiting time extension containing a 1 byte long INF field and (deselect = false)
* - DESELECT containing no INF field (deselect = true)
*
* Must ONLY be used inside the callback function.
*
* The rx_buffer will be filled with R-block repsonse
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in] deselect Sets appropriate bit in PCB byte.
* @param[in] tx_buffer pointer to the buffer containing the data to be transmitted.
* @param[out] rx_buffer pointer to the buffer to be filled with received data.
* @return Iso14443_4aErrorNone on success, an error code on failure.
*/
Iso14443_4aError iso14443_4a_poller_send_supervisory_block(
Iso14443_4aPoller* instance,
bool deselect,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer);

/**
* @brief Send HALT command to the card.
*
Expand Down
31 changes: 0 additions & 31 deletions lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,37 +116,6 @@ Iso14443_4aError iso14443_4a_poller_send_block(
return error;
}

Iso14443_4aError iso14443_4a_poller_send_chain_block(
Iso14443_4aPoller* instance,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer) {
iso14443_4_layer_set_i_block(instance->iso14443_4_layer, true, false);
Iso14443_4aError error = iso14443_4a_poller_send_block(instance, tx_buffer, rx_buffer);
return error;
}

Iso14443_4aError iso14443_4a_poller_send_receive_ready_block(
Iso14443_4aPoller* instance,
bool acknowledged,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer) {
bool CID_present = bit_buffer_get_size_bytes(tx_buffer) != 0;
iso14443_4_layer_set_r_block(instance->iso14443_4_layer, acknowledged, CID_present);
Iso14443_4aError error = iso14443_4a_poller_send_block(instance, tx_buffer, rx_buffer);
return error;
}

Iso14443_4aError iso14443_4a_poller_send_supervisory_block(
Iso14443_4aPoller* instance,
bool deselect,
const BitBuffer* tx_buffer,
BitBuffer* rx_buffer) {
bool CID_present = bit_buffer_get_size_bytes(tx_buffer) != 0;
iso14443_4_layer_set_s_block(instance->iso14443_4_layer, deselect, CID_present);
Iso14443_4aError error = iso14443_4a_poller_send_block(instance, tx_buffer, rx_buffer);
return error;
}

Iso14443_4aError iso14443_4a_poller_send_block_pwt_ext(
Iso14443_4aPoller* instance,
const BitBuffer* tx_buffer,
Expand Down
3 changes: 0 additions & 3 deletions targets/f7/api_symbols.csv
Original file line number Diff line number Diff line change
Expand Up @@ -2211,9 +2211,6 @@ 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_poller_send_chain_block,Iso14443_4aError,"Iso14443_4aPoller*, const BitBuffer*, BitBuffer*"
Function,+,iso14443_4a_poller_send_receive_ready_block,Iso14443_4aError,"Iso14443_4aPoller*, _Bool, const BitBuffer*, BitBuffer*"
Function,+,iso14443_4a_poller_send_supervisory_block,Iso14443_4aError,"Iso14443_4aPoller*, _Bool, 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"
Expand Down
Loading