diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index 5defdadad..c62003d70 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -525,6 +525,19 @@ void canvas_draw_xbm( canvas_draw_u8g2_bitmap(&canvas->fb, x, y, width, height, bitmap, IconRotation0); } +void canvas_draw_xbm_mirrored( + Canvas* canvas, + int32_t x, + int32_t y, + size_t width, + size_t height, + const uint8_t* bitmap_data) { + furi_check(canvas); + x += canvas->offset_x; + y += canvas->offset_y; + canvas_draw_u8g2_bitmap(&canvas->fb, x, y, width, height, bitmap_data, IconRotation180); +} + void canvas_draw_glyph(Canvas* canvas, int32_t x, int32_t y, uint16_t ch) { furi_check(canvas); x += canvas->offset_x; diff --git a/applications/services/gui/canvas.h b/applications/services/gui/canvas.h index a58be130d..7d22bffe5 100644 --- a/applications/services/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -296,6 +296,23 @@ void canvas_draw_xbm( size_t height, const uint8_t* bitmap); +/** Draw mirrored XBM bitmap + * + * @param canvas Canvas instance + * @param x x coordinate + * @param y y coordinate + * @param[in] width bitmap width + * @param[in] height bitmap height + * @param bitmap pointer to XBM bitmap data + */ +void canvas_draw_xbm_mirrored( + Canvas* canvas, + int32_t x, + int32_t y, + size_t width, + size_t height, + const uint8_t* bitmap_data); + /** Draw dot at x,y * * @param canvas Canvas instance diff --git a/lib/nfc/helpers/iso14443_4_layer.c b/lib/nfc/helpers/iso14443_4_layer.c index d524c267c..9d817e050 100644 --- a/lib/nfc/helpers/iso14443_4_layer.c +++ b/lib/nfc/helpers/iso14443_4_layer.c @@ -2,22 +2,50 @@ #include -#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) - +// EMV Specific masks #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_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 (1U << 1) +#define ISO14443_4_BLOCK_PCB_MASK (0x03) -#define ISO14443_4_BLOCK_PCB_NAD (1U << 2) -#define ISO14443_4_BLOCK_PCB_CID (1U << 3) -#define ISO14443_4_BLOCK_PCB_CHAINING (1U << 4) +#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_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) struct Iso14443_4Layer { uint8_t pcb; @@ -43,9 +71,31 @@ 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, @@ -58,6 +108,11 @@ 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, @@ -67,10 +122,26 @@ bool iso14443_4_layer_decode_block( bool ret = false; do { - if(!bit_buffer_starts_with_byte(block_data, instance->pcb_prev)) break; - // TODO: Fix crash - bit_buffer_copy_right(output_data, block_data, 1); - ret = true; + 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; + } } while(false); return ret; diff --git a/lib/nfc/helpers/iso14443_4_layer.h b/lib/nfc/helpers/iso14443_4_layer.h index f4f8db520..9daf25775 100644 --- a/lib/nfc/helpers/iso14443_4_layer.h +++ b/lib/nfc/helpers/iso14443_4_layer.h @@ -15,6 +15,10 @@ 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, diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c index 95612bf54..32cc8f198 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c @@ -84,6 +84,15 @@ 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; } diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.h index ba649847b..04f0b197a 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.h +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.h @@ -12,6 +12,7 @@ typedef struct Iso14443_4aListener Iso14443_4aListener; typedef enum { Iso14443_4aListenerEventTypeHalted, + Iso14443_4aListenerEventTypeFieldOff, Iso14443_4aListenerEventTypeReceivedData, } Iso14443_4aListenerEventType; diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h index 019beb2be..7b43d6ee8 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h @@ -61,6 +61,69 @@ 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. * diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c index 02d78fba5..97b3171d5 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c @@ -164,3 +164,34 @@ Iso14443_4aError iso14443_4a_poller_send_block_pwt_ext( 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; +} diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 781da2bb3..d51a5cb7f 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -832,6 +832,7 @@ Function,+,canvas_draw_str,void,"Canvas*, int32_t, int32_t, const char*" Function,+,canvas_draw_str_aligned,void,"Canvas*, int32_t, int32_t, Align, Align, const char*" Function,+,canvas_draw_triangle,void,"Canvas*, int32_t, int32_t, size_t, size_t, CanvasDirection" Function,+,canvas_draw_xbm,void,"Canvas*, int32_t, int32_t, size_t, size_t, const uint8_t*" +Function,+,canvas_draw_xbm_mirrored,void,"Canvas*, int32_t, int32_t, size_t, size_t, const uint8_t*" Function,+,canvas_get_font_params,const CanvasFontParameters*,"const Canvas*, Font" Function,+,canvas_glyph_width,size_t,"Canvas*, uint16_t" Function,+,canvas_height,size_t,const Canvas* @@ -2174,6 +2175,9 @@ 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"