Skip to content

Commit

Permalink
Add support to start NFC in CE and reader mode
Browse files Browse the repository at this point in the history
  • Loading branch information
yrichard-ledger committed Oct 11, 2024
1 parent 0bac17f commit 93c3a80
Show file tree
Hide file tree
Showing 7 changed files with 266 additions and 15 deletions.
6 changes: 6 additions & 0 deletions Makefile.standard_app
Original file line number Diff line number Diff line change
Expand Up @@ -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 #
#####################################################################
Expand Down
66 changes: 66 additions & 0 deletions include/os_io_nfc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 io_nfc_reader_send(const uint8_t *cmd_data,
size_t cmd_len,
nfc_resp_callback_t callback,
int timeout_ms);

/* Return false if nfc reader can not be started in current conditions */
bool io_nfc_reader_start(nfc_evt_callback_t callback);
void io_nfc_reader_stop(void);
bool io_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 io_nfc_process_events(void);
#endif // HAVE_NFC_READER

#endif
14 changes: 11 additions & 3 deletions include/seproxyhal_protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,12 @@
#define SEPROXYHAL_TAG_BOOTLOADER_RAPDU_EVENT 0x19 // <RAPDU from the bootloader>
#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
Expand Down Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions lib_standard_app/io.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,21 @@
#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
#define SW_WRONG_RESPONSE_LENGTH 0xB000

uint8_t G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B];

#ifdef HAVE_NFC_READER
struct nfc_reader_context G_io_reader_ctx;
#endif

/**
* Variable containing the length of the APDU response to send back.
*/
Expand Down Expand Up @@ -83,6 +91,10 @@ 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();
io_nfc_process_events();
#endif // HAVE_NFC_READER
break;
default:
UX_DEFAULT_EVENT();
Expand Down
156 changes: 148 additions & 8 deletions src/os_io_nfc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 <string.h>

#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];
Expand All @@ -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)
Expand All @@ -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);
Expand Down Expand Up @@ -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 io_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 io_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 io_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 io_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;
io_nfc_reader_power();
return true;
}

void io_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 io_nfc_is_reader(void)
{
return G_io_reader_ctx.reader_mode;
}

#endif // HAVE_NFC_READER

#endif // HAVE_NFC
20 changes: 16 additions & 4 deletions src/os_io_seproxyhal.c
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,19 @@ unsigned int io_seproxyhal_handle_event(void)
#ifdef HAVE_NFC
case SEPROXYHAL_TAG_NFC_APDU_EVENT:
io_nfc_recv_event();
#ifndef HAVE_BOLOS
io_nfc_process_events();
#endif // !HAVE_BOLOS
return 1;
#endif
#ifdef HAVE_NFC_READER
case SEPROXYHAL_TAG_NFC_EVENT:
io_nfc_event();
#ifndef HAVE_BOLOS
io_nfc_process_events();
#endif // !HAVE_BOLOS
return 1;
#endif // HAVE_NFC_READER
#endif // HAVE_NFC

case SEPROXYHAL_TAG_UX_EVENT:
switch (G_io_seproxyhal_spi_buffer[3]) {
Expand Down Expand Up @@ -502,9 +513,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;
Expand Down
7 changes: 7 additions & 0 deletions src/os_io_task.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "os_io_seproxyhal.h"
#include "os_task.h"
#include "os_types.h"
#include "os_io_nfc.h"

#include <string.h>

Expand All @@ -27,6 +28,12 @@ bolos_ux_asynch_callback_t G_io_asynch_ux_callback;
// !defined(DEBUG_VARIABLE_SPI_SIZE))
#endif // !HAVE_BLE

#ifdef HAVE_NFC_READER
// For some reason the linker only works correctly
// if the struct declaration is in this file
struct nfc_reader_context G_io_reader_ctx;
#endif

// 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;
Expand Down

0 comments on commit 93c3a80

Please sign in to comment.