diff --git a/Makefile.standard_app b/Makefile.standard_app index 2cee343d9..74e9359ad 100644 --- a/Makefile.standard_app +++ b/Makefile.standard_app @@ -39,6 +39,12 @@ ifeq ($(TARGET_NAME),$(filter $(TARGET_NAME), TARGET_STAX TARGET_FLEX)) endif endif +ifeq ($(ENABLE_NFC_READER), 1) +ifeq ($(TARGET_NAME),$(filter $(TARGET_NAME), TARGET_STAX TARGET_FLEX)) + DEFINES += HAVE_NFC_READER +endif +endif + ##################################################################### # SWAP # ##################################################################### diff --git a/include/os_io_nfc.h b/include/os_io_nfc.h index b8602c25f..2a95b9e80 100644 --- a/include/os_io_nfc.h +++ b/include/os_io_nfc.h @@ -19,8 +19,74 @@ #ifndef OS_IO_NFC_H #define OS_IO_NFC_H +#include "arch.h" + +#include "os_io_seproxyhal.h" + +/* Public API for reader application --------------------------------------- */ +#ifdef HAVE_NFC_READER + +enum card_tech { + NFC_A, + NFC_B +}; + +struct card_info { + enum card_tech tech; + uint8_t nfcid[7]; + size_t nfcid_len; +}; + +enum nfc_event { + CARD_DETECTED, + CARD_REMOVED, +}; + +typedef void (*nfc_evt_callback_t)(enum nfc_event event, struct card_info *info); +typedef void (*nfc_resp_callback_t)(bool error, bool timeout, uint8_t *resp_data, size_t resp_len); + +/* Functions */ + +/* return false in case of error + in that case, callback will not be called */ +bool nfc_reader_send(const uint8_t *cmd_data, + size_t cmd_len, + nfc_resp_callback_t callback, + int timeout_ms); + +/* Return false if nfc reeder can not be started in current conditions */ +bool nfc_reader_start(nfc_evt_callback_t callback); +void nfc_reader_stop(void); +bool nfc_is_reader(void); + +#endif // HAVE_NFC_READER + +/* SDK internal API --------------------------------------- */ + +#ifdef HAVE_NFC_READER + +struct nfc_reader_context { + nfc_resp_callback_t resp_callback; + nfc_evt_callback_t evt_callback; + bool reader_mode; + bool event_happened; + bool response_received; + unsigned int remaining_ms; + enum nfc_event last_event; + struct card_info card; +}; + +extern struct nfc_reader_context G_io_reader_ctx; +#endif // HAVE_NFC_READER + void io_nfc_init(void); void io_nfc_recv_event(void); void io_nfc_send_response(const uint8_t *packet, uint16_t packet_length); +#ifdef HAVE_NFC_READER +void io_nfc_event(void); +void io_nfc_ticker(void); +void nfc_process_events(void); +#endif // HAVE_NFC_READER + #endif diff --git a/include/seproxyhal_protocol.h b/include/seproxyhal_protocol.h index 3b60abc0e..4795b87be 100644 --- a/include/seproxyhal_protocol.h +++ b/include/seproxyhal_protocol.h @@ -112,7 +112,12 @@ #define SEPROXYHAL_TAG_BOOTLOADER_RAPDU_EVENT 0x19 // #define SEPROXYHAL_TAG_UX_EVENT 0x1A // #ifdef HAVE_NFC -#define SEPROXYHAL_TAG_NFC_APDU_EVENT 0x1C +#define SEPROXYHAL_TAG_NFC_APDU_EVENT 0x1C +#define SEPROXYHAL_TAG_NFC_EVENT 0x1E +#define SEPROXYHAL_TAG_NFC_EVENT_CARD_DETECTED 0x01 // card_detected + type a/b + nfcid[max 7] +#define SEPROXYHAL_TAG_NFC_EVENT_CARD_DETECTED_A 0x01 +#define SEPROXYHAL_TAG_NFC_EVENT_CARD_DETECTED_B 0x02 +#define SEPROXYHAL_TAG_NFC_EVENT_CARD_LOST 0x02 // card lost #endif #define SEPH_PROTOCOL_EVT_POWER_BUTTON_EVENT 0x1B @@ -166,8 +171,11 @@ // #define SEPROXYHAL_TAG_SCREEN_DISPLAY 0x4A // wait for display_event after sent #ifdef HAVE_NFC -#define SEPROXYHAL_TAG_NFC_RAPDU 0x4A -#define SEPROXYHAL_TAG_NFC_POWER 0x34 +#define SEPROXYHAL_TAG_NFC_RAPDU 0x4A +#define SEPROXYHAL_TAG_NFC_POWER 0x34 +#define SEPROXYHAL_TAG_NFC_POWER_OFF 0x00 +#define SEPROXYHAL_TAG_NFC_POWER_ON_CE 0x01 +#define SEPROXYHAL_TAG_NFC_POWER_ON_READER 0x02 #endif #define SEPROXYHAL_TAG_DEVICE_OFF 0x4B diff --git a/lib_standard_app/io.c b/lib_standard_app/io.c index cf878725f..898cb498d 100644 --- a/lib_standard_app/io.c +++ b/lib_standard_app/io.c @@ -25,6 +25,10 @@ #include "swap.h" #endif +#ifdef HAVE_NFC_READER +#include "os_io_nfc.h" +#endif // HAVE_NFC_READER + // TODO: Temporary workaround, at some point all status words should be defined by the SDK and // removed from the application #define SW_OK 0x9000 @@ -83,6 +87,9 @@ WEAK uint8_t io_event(uint8_t channel) case SEPROXYHAL_TAG_TICKER_EVENT: app_ticker_event_callback(); UX_TICKER_EVENT(G_io_seproxyhal_spi_buffer, {}); +#ifdef HAVE_NFC_READER + io_nfc_ticker(); +#endif // HAVE_NFC_READER break; default: UX_DEFAULT_EVENT(); diff --git a/src/os_io_nfc.c b/src/os_io_nfc.c index 3413a22f5..6ca3f0ba0 100644 --- a/src/os_io_nfc.c +++ b/src/os_io_nfc.c @@ -15,7 +15,11 @@ * See the License for the specific language governing permissions and * limitations under the License. ********************************************************************************/ -#include "bolos_target.h" + +#include "os.h" +#include "os_settings.h" +#include "os_io_seproxyhal.h" + #include "errors.h" #include "exceptions.h" #ifdef HAVE_NFC @@ -25,17 +29,11 @@ #endif // DEBUG_OS_STACK_CONSUMPTION #include "os_io.h" +#include "os_io_nfc.h" #include "os_utils.h" #include "os_io_seproxyhal.h" #include -#ifdef DEBUG -#define LOG printf -#else -#define LOG(...) -#endif - -#include "os.h" #include "ledger_protocol.h" static uint8_t rx_apdu_buffer[IO_APDU_BUFFER_SIZE]; @@ -55,6 +53,9 @@ void io_nfc_init(void) ledger_protocol_data.rx_dst_buffer = G_io_apdu_buffer; #endif LEDGER_PROTOCOL_init(&ledger_protocol_data); +#ifdef HAVE_NFC_READER + memset((void *) &G_io_reader_ctx, 0, sizeof(G_io_reader_ctx)); +#endif // HAVE_NFC_READER } void io_nfc_recv_event(void) @@ -65,6 +66,13 @@ void io_nfc_recv_event(void) // Full apdu is received, copy it to global apdu buffer if (ledger_protocol_data.rx_apdu_status == APDU_STATUS_COMPLETE) { +#ifdef HAVE_NFC_READER + if (G_io_reader_ctx.reader_mode) { + G_io_reader_ctx.response_received = true; + return; + } +#endif // HAVE_NFC_READER + memcpy(ledger_protocol_data.rx_dst_buffer, ledger_protocol_data.rx_apdu_buffer, ledger_protocol_data.rx_apdu_length); @@ -106,4 +114,136 @@ void io_nfc_send_response(const uint8_t *packet, uint16_t packet_length) } } +#ifdef HAVE_NFC_READER + +void io_nfc_event(void) +{ + size_t size = U2BE(G_io_seproxyhal_spi_buffer, 1); + + if (size >= 1) { + switch (G_io_seproxyhal_spi_buffer[3]) { + case SEPROXYHAL_TAG_NFC_EVENT_CARD_DETECTED: { + G_io_reader_ctx.event_happened = true; + G_io_reader_ctx.last_event = CARD_DETECTED; + G_io_reader_ctx.card.tech + = (G_io_seproxyhal_spi_buffer[4] == SEPROXYHAL_TAG_NFC_EVENT_CARD_DETECTED_A) + ? NFC_A + : NFC_B; + G_io_reader_ctx.card.nfcid_len = MIN(size - 2, sizeof(G_io_reader_ctx.card.nfcid)); + memcpy((void *) G_io_reader_ctx.card.nfcid, + G_io_seproxyhal_spi_buffer + 5, + G_io_reader_ctx.card.nfcid_len); + } break; + + case SEPROXYHAL_TAG_NFC_EVENT_CARD_LOST: + if (G_io_reader_ctx.evt_callback != NULL) { + G_io_reader_ctx.event_happened = true; + G_io_reader_ctx.last_event = CARD_REMOVED; + } + break; + } + } +} + +void nfc_process_events(void) +{ + if (G_io_reader_ctx.response_received) { + G_io_reader_ctx.response_received = false; + if (G_io_reader_ctx.resp_callback != NULL) { + G_io_reader_ctx.resp_callback(false, + false, + ledger_protocol_data.rx_apdu_buffer, + ledger_protocol_data.rx_apdu_length); + } + memset(ledger_protocol_data.rx_apdu_buffer, 0, ledger_protocol_data.rx_apdu_length); + } + + if (G_io_reader_ctx.resp_callback != NULL && G_io_reader_ctx.remaining_ms == 0) { + G_io_reader_ctx.resp_callback(false, true, NULL, 0); + G_io_reader_ctx.resp_callback = NULL; + } + + if (G_io_reader_ctx.event_happened) { + G_io_reader_ctx.event_happened = 0; + + // if in_progress, call the resp_callback with an error + if (G_io_reader_ctx.resp_callback != NULL) { + G_io_reader_ctx.resp_callback(true, false, NULL, 0); + G_io_reader_ctx.resp_callback = NULL; + } + + if (G_io_reader_ctx.evt_callback != NULL) { + G_io_reader_ctx.evt_callback(G_io_reader_ctx.last_event, + (struct card_info *) &G_io_reader_ctx.card); + } + if (G_io_reader_ctx.last_event == CARD_REMOVED) { + memset((void *) &G_io_reader_ctx.card, 0, sizeof(G_io_reader_ctx.card)); + } + } +} + +void io_nfc_ticker(void) +{ + if (G_io_reader_ctx.resp_callback != NULL) { + if (G_io_reader_ctx.remaining_ms <= 100) { + G_io_reader_ctx.remaining_ms = 0; + } + else { + G_io_reader_ctx.remaining_ms -= 100; + } + } +} + +bool nfc_reader_send(const uint8_t *cmd_data, + size_t cmd_len, + nfc_resp_callback_t callback, + int timeout_ms) +{ + G_io_reader_ctx.resp_callback = PIC(callback); + io_nfc_send_response(PIC(cmd_data), cmd_len); + + G_io_reader_ctx.response_received = false; + G_io_reader_ctx.remaining_ms = timeout_ms; + + return true; +} + +void nfc_reader_power(void) +{ + uint8_t buffer[4]; + buffer[0] = SEPROXYHAL_TAG_NFC_POWER; + buffer[1] = 0; + buffer[2] = 1; + buffer[3] = SEPROXYHAL_TAG_NFC_POWER_ON_READER; + io_seproxyhal_spi_send(buffer, 4); +} + +bool nfc_reader_start(nfc_evt_callback_t callback) +{ + G_io_reader_ctx.evt_callback = PIC(callback); + G_io_reader_ctx.reader_mode = true; + G_io_reader_ctx.event_happened = false; + G_io_reader_ctx.resp_callback = NULL; + G_io_reader_ctx.response_received = false; + nfc_reader_power(); + return true; +} + +void nfc_reader_stop() +{ + G_io_reader_ctx.evt_callback = NULL; + G_io_reader_ctx.reader_mode = false; + G_io_reader_ctx.event_happened = false; + G_io_reader_ctx.resp_callback = NULL; + G_io_reader_ctx.response_received = false; + io_seproxyhal_nfc_power(false); +} + +bool nfc_is_reader(void) +{ + return G_io_reader_ctx.reader_mode; +} + +#endif // HAVE_NFC_READER + #endif // HAVE_NFC diff --git a/src/os_io_seproxyhal.c b/src/os_io_seproxyhal.c index 155681a03..1dfd44b0f 100644 --- a/src/os_io_seproxyhal.c +++ b/src/os_io_seproxyhal.c @@ -274,7 +274,12 @@ unsigned int io_seproxyhal_handle_event(void) case SEPROXYHAL_TAG_NFC_APDU_EVENT: io_nfc_recv_event(); return 1; -#endif +#ifdef HAVE_NFC_READER + case SEPROXYHAL_TAG_NFC_EVENT: + io_nfc_event(); + return 1; +#endif // HAVE_NFC_READER +#endif // HAVE_NFC case SEPROXYHAL_TAG_UX_EVENT: switch (G_io_seproxyhal_spi_buffer[3]) { @@ -364,6 +369,9 @@ unsigned int io_seproxyhal_handle_event(void) } } #endif // HAVE_BLE_APDU +#ifdef HAVE_NFC_READER + io_nfc_ticker(); +#endif FALL_THROUGH; // no break is intentional default: @@ -502,9 +510,10 @@ void io_seproxyhal_nfc_power(bool forceInit) { uint8_t buffer[4]; uint8_t power - = forceInit - ? 1 - : (os_setting_get(OS_SETTING_FEATURES, NULL, 0) & OS_SETTING_FEATURES_NFC_ENABLED); + = (forceInit + || (os_setting_get(OS_SETTING_FEATURES, NULL, 0) & OS_SETTING_FEATURES_NFC_ENABLED)) + ? SEPROXYHAL_TAG_NFC_POWER_ON_CE + : SEPROXYHAL_TAG_NFC_POWER_OFF; buffer[0] = SEPROXYHAL_TAG_NFC_POWER; buffer[1] = 0; buffer[2] = 1; @@ -1351,6 +1360,9 @@ unsigned short io_exchange(unsigned char channel, unsigned short tx_len) G_io_seproxyhal_spi_buffer, sizeof(G_io_seproxyhal_spi_buffer), 0); // process without sending status on tickers etc, to ensure keeping the hand os_io_seph_recv_and_process(1); +#if (!defined(HAVE_BOLOS) && defined(HAVE_NFC_READER)) + nfc_process_events(); +#endif // !HAVE_BOLOS && HAVE_NFC_READER } // reinit sending timeout for APDU replied within io_exchange @@ -1447,6 +1459,9 @@ unsigned short io_exchange(unsigned char channel, unsigned short tx_len) } // avoid a general status to be replied io_seproxyhal_handle_event(); +#if (!defined(HAVE_BOLOS) && defined(HAVE_NFC_READER)) + nfc_process_events(); +#endif // !HAVE_BOLOS && HAVE_NFC_READER } while (io_seproxyhal_spi_is_status_sent()); } #ifdef U2F_PROXY_MAGIC @@ -1493,6 +1508,9 @@ unsigned short io_exchange(unsigned char channel, unsigned short tx_len) } // avoid a general status to be replied io_seproxyhal_handle_event(); +#if (!defined(HAVE_BOLOS) && defined(HAVE_NFC_READER)) + nfc_process_events(); +#endif // !HAVE_BOLOS && HAVE_NFC_READER } while (io_seproxyhal_spi_is_status_sent()); } // reset apdu state @@ -1599,6 +1617,9 @@ unsigned short io_exchange(unsigned char channel, unsigned short tx_len) } io_seproxyhal_handle_event(); +#if (!defined(HAVE_BOLOS) && defined(HAVE_NFC_READER)) + nfc_process_events(); +#endif // !HAVE_BOLOS && HAVE_NFC_READER } break; diff --git a/src/os_io_task.c b/src/os_io_task.c index 674d1c25f..fed6a0205 100644 --- a/src/os_io_task.c +++ b/src/os_io_task.c @@ -4,6 +4,7 @@ #include "os_io_seproxyhal.h" #include "os_task.h" #include "os_types.h" +#include "os_io_nfc.h" #include @@ -27,6 +28,10 @@ bolos_ux_asynch_callback_t G_io_asynch_ux_callback; // !defined(DEBUG_VARIABLE_SPI_SIZE)) #endif // !HAVE_BLE +// For some reason the linker only works correctly +// if the struct declaration is in this file +struct nfc_reader_context G_io_reader_ctx; + // Buffer dedicated to the MCU <-> SE data transfer. unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B]; io_seph_app_t G_io_app;