diff --git a/lib/nfc/helpers/iso14443_4_layer.c b/lib/nfc/helpers/iso14443_4_layer.c index 3e8c6e83a..209549938 100644 --- a/lib/nfc/helpers/iso14443_4_layer.c +++ b/lib/nfc/helpers/iso14443_4_layer.c @@ -2,10 +2,42 @@ #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) +#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_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; @@ -31,9 +63,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, @@ -46,6 +100,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, @@ -55,9 +114,26 @@ bool iso14443_4_layer_decode_block( bool ret = false; do { - if(!bit_buffer_starts_with_byte(block_data, instance->pcb_prev)) break; - 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 437c2e8a6..67a7f37fe 100644 --- a/lib/nfc/helpers/iso14443_4_layer.h +++ b/lib/nfc/helpers/iso14443_4_layer.h @@ -14,6 +14,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_poller.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h index fef565e51..80a4c1540 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h @@ -56,6 +56,69 @@ Iso14443_4aError iso14443_4a_poller_send_block( 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 a9453b039..427973f4a 100644 --- a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c @@ -114,3 +114,34 @@ 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; +} diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 160c065e9..ca74ece8a 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,75.0,, +Version,+,75.3,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, @@ -2125,6 +2125,9 @@ Function,+,iso14443_4a_load,_Bool,"Iso14443_4aData*, FlipperFormat*, uint32_t" 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_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"