Skip to content

Commit

Permalink
Merge pull request #665 from LedgerHQ/yri_split_nfc_apdus
Browse files Browse the repository at this point in the history
Split extended NFC APDUs into smaller seph messages
  • Loading branch information
yrichard-ledger committed May 30, 2024
2 parents 3054e61 + b08c539 commit be085f3
Show file tree
Hide file tree
Showing 7 changed files with 232 additions and 116 deletions.
6 changes: 3 additions & 3 deletions include/ledger_protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,6 @@ typedef struct ledger_protocol_s {
/* Exported variables --------------------------------------------------------*/

/* Exported functions prototypes--------------------------------------------- */
void LEDGER_PROTOCOL_init(ledger_protocol_t *data);
void LEDGER_PROTOCOL_rx(const uint8_t *buffer, uint16_t length);
void LEDGER_PROTOCOL_tx(const uint8_t *buffer, uint16_t length);
void LEDGER_PROTOCOL_init(ledger_protocol_t *ctx);
void LEDGER_PROTOCOL_rx(ledger_protocol_t *ctx, const uint8_t *buffer, uint16_t length);
void LEDGER_PROTOCOL_tx(ledger_protocol_t *ctx, const uint8_t *buffer, uint16_t length);
26 changes: 26 additions & 0 deletions include/os_io_nfc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

/*******************************************************************************
* Ledger Nano S - Secure firmware
* (c) 2022 Ledger
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************/

#ifndef OS_IO_NFC_H
#define OS_IO_NFC_H

void io_nfc_init(void);
void io_nfc_recv_event(void);
void io_nfc_send_response(const uint8_t *packet, uint16_t packet_length);

#endif
2 changes: 1 addition & 1 deletion include/os_io_seproxyhal.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ void io_set_timeout(unsigned int timeout);

#ifdef HAVE_NFC
// Needs to be aligned with RFAL_FEATURE_ISO_DEP_IBLOCK_MAX_LEN defined on mcu side in platform.h
#define NFC_APDU_MAX_SIZE 256
#define NFC_APDU_MAX_SIZE 1024
void io_seproxyhal_nfc_power(bool forceInit);
#endif

Expand Down
16 changes: 9 additions & 7 deletions lib_blewbxx_impl/src/ledger_ble.c
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ static void hci_evt_cmd_complete(const uint8_t *buffer, uint16_t length)
}
else {
if (ledger_protocol_data.tx_apdu_buffer) {
LEDGER_PROTOCOL_tx(NULL, 0);
LEDGER_PROTOCOL_tx(&ledger_protocol_data, NULL, 0);
notify_chunk();
}
if (!ledger_protocol_data.tx_apdu_buffer) {
Expand Down Expand Up @@ -870,7 +870,7 @@ static void attribute_modified(const uint8_t *buffer, uint16_t length)
&& (ledger_ble_data.notifications_enabled) && (ledger_ble_data.connection.encrypted)
&& (att_data_length)) {
LOG_BLE("WRITE CMD %d\n", length - 4);
LEDGER_PROTOCOL_rx(&buffer[4], length - 4);
LEDGER_PROTOCOL_rx(&ledger_protocol_data, &buffer[4], length - 4);

if (ledger_protocol_data.rx_apdu_status == APDU_STATUS_COMPLETE) {
check_transfer_mode(G_io_app.transfer_mode);
Expand All @@ -882,7 +882,8 @@ static void attribute_modified(const uint8_t *buffer, uint16_t length)
copy_apdu_to_app(true);
}
else if (ledger_ble_data.resp_length) {
LEDGER_PROTOCOL_tx(ledger_ble_data.resp, ledger_ble_data.resp_length);
LEDGER_PROTOCOL_tx(
&ledger_protocol_data, ledger_ble_data.resp, ledger_ble_data.resp_length);
ledger_ble_data.resp_length = 0;
notify_chunk();
}
Expand Down Expand Up @@ -916,7 +917,7 @@ static void write_permit_request(const uint8_t *buffer, uint16_t length)
if ((att_handle == ledger_ble_data.ledger_gatt_write_characteristic_handle + 1)
&& (ledger_ble_data.notifications_enabled) && (ledger_ble_data.connection.encrypted)
&& (data_length)) {
LEDGER_PROTOCOL_rx(&buffer[1], length - 1);
LEDGER_PROTOCOL_rx(&ledger_protocol_data, &buffer[1], length - 1);
aci_gatt_write_resp(ledger_ble_data.connection.connection_handle,
att_handle,
0,
Expand Down Expand Up @@ -1052,16 +1053,17 @@ void LEDGER_BLE_send(const uint8_t *packet, uint16_t packet_length)
ledger_ble_data.resp[0] = packet[0];
ledger_ble_data.resp[1] = packet[1];
if (ledger_protocol_data.rx_apdu_length) {
LEDGER_PROTOCOL_tx(packet, packet_length);
LEDGER_PROTOCOL_tx(&ledger_protocol_data, packet, packet_length);
notify_chunk();
}
}
else {
if ((ledger_ble_data.resp_length != 0) && (U2BE(ledger_ble_data.resp, 0) != SWO_SUCCESS)) {
LEDGER_PROTOCOL_tx(ledger_ble_data.resp, ledger_ble_data.resp_length);
LEDGER_PROTOCOL_tx(
&ledger_protocol_data, ledger_ble_data.resp, ledger_ble_data.resp_length);
}
else {
LEDGER_PROTOCOL_tx(packet, packet_length);
LEDGER_PROTOCOL_tx(&ledger_protocol_data, packet, packet_length);
}
ledger_ble_data.resp_length = 0;

Expand Down
155 changes: 74 additions & 81 deletions src/ledger_protocol.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,42 +47,40 @@
/* Private macros-------------------------------------------------------------*/

/* Private functions prototypes ----------------------------------------------*/
static void process_apdu_chunk(const uint8_t *buffer, uint16_t length);
static void process_apdu_chunk(ledger_protocol_t *ctx, const uint8_t *buffer, uint16_t length);

/* Exported variables --------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/
static const uint8_t protocol_version[4] = {0x00, 0x00, 0x00, 0x00};

static ledger_protocol_t *ledger_protocol;

/* Private functions ---------------------------------------------------------*/
static void process_apdu_chunk(const uint8_t *buffer, uint16_t length)
static void process_apdu_chunk(ledger_protocol_t *ctx, const uint8_t *buffer, uint16_t length)
{
// Check the sequence number
if ((length < 2) || ((uint16_t) U2BE(buffer, 0) != ledger_protocol->rx_apdu_sequence_number)) {
ledger_protocol->rx_apdu_status = APDU_STATUS_WAITING;
if ((length < 2) || ((uint16_t) U2BE(buffer, 0) != ctx->rx_apdu_sequence_number)) {
ctx->rx_apdu_status = APDU_STATUS_WAITING;
return;
}
// Check total length presence
if ((length < 4) && (ledger_protocol->rx_apdu_sequence_number == 0)) {
ledger_protocol->rx_apdu_status = APDU_STATUS_WAITING;
if ((length < 4) && (ctx->rx_apdu_sequence_number == 0)) {
ctx->rx_apdu_status = APDU_STATUS_WAITING;
return;
}

if (ledger_protocol->rx_apdu_sequence_number == 0) {
if (ctx->rx_apdu_sequence_number == 0) {
// First chunk
ledger_protocol->rx_apdu_status = APDU_STATUS_NEED_MORE_DATA;
ledger_protocol->rx_apdu_length = (uint16_t) U2BE(buffer, 2);
ctx->rx_apdu_status = APDU_STATUS_NEED_MORE_DATA;
ctx->rx_apdu_length = (uint16_t) U2BE(buffer, 2);
ctx->rx_apdu_offset = 0;
// Check if we have enough space to store the apdu
if (ledger_protocol->rx_apdu_length > ledger_protocol->rx_apdu_buffer_max_length) {
LOG_BLE_PROTOCOL("APDU WAITING - %d\n", ledger_protocol->rx_apdu_length);
ledger_protocol->rx_apdu_length = 0;
ledger_protocol->rx_apdu_status = APDU_STATUS_WAITING;
if (ctx->rx_apdu_length > ctx->rx_apdu_buffer_max_length) {
LOG_BLE_PROTOCOL("APDU WAITING - %d\n", ctx->rx_apdu_length);
ctx->rx_apdu_length = 0;
ctx->rx_apdu_status = APDU_STATUS_WAITING;
return;
}
ledger_protocol->rx_apdu_offset = 0;
buffer = &buffer[4];
buffer = &buffer[4];
length -= 4;
}
else {
Expand All @@ -91,83 +89,80 @@ static void process_apdu_chunk(const uint8_t *buffer, uint16_t length)
length -= 2;
}

if ((ledger_protocol->rx_apdu_offset + length) > ledger_protocol->rx_apdu_length) {
length = ledger_protocol->rx_apdu_length - ledger_protocol->rx_apdu_offset;
if ((ctx->rx_apdu_offset + length) > ctx->rx_apdu_length) {
length = ctx->rx_apdu_length - ctx->rx_apdu_offset;
}

memcpy(&ledger_protocol->rx_apdu_buffer[ledger_protocol->rx_apdu_offset], buffer, length);
ledger_protocol->rx_apdu_offset += length;
memcpy(&ctx->rx_apdu_buffer[ctx->rx_apdu_offset], buffer, length);
ctx->rx_apdu_offset += length;

if (ledger_protocol->rx_apdu_offset == ledger_protocol->rx_apdu_length) {
ledger_protocol->rx_apdu_sequence_number = 0;
ledger_protocol->rx_apdu_status = APDU_STATUS_COMPLETE;
if (ctx->rx_apdu_offset == ctx->rx_apdu_length) {
ctx->rx_apdu_sequence_number = 0;
ctx->rx_apdu_status = APDU_STATUS_COMPLETE;
LOG_BLE_PROTOCOL("APDU COMPLETE\n");
}
else {
ledger_protocol->rx_apdu_sequence_number++;
ledger_protocol->rx_apdu_status = APDU_STATUS_NEED_MORE_DATA;
ctx->rx_apdu_sequence_number++;
ctx->rx_apdu_status = APDU_STATUS_NEED_MORE_DATA;
LOG_BLE_PROTOCOL("APDU NEED MORE DATA\n");
}
}

/* Exported functions --------------------------------------------------------*/
void LEDGER_PROTOCOL_init(ledger_protocol_t *data)
void LEDGER_PROTOCOL_init(ledger_protocol_t *ctx)
{
ledger_protocol = data;
ledger_protocol->rx_apdu_status = APDU_STATUS_WAITING;
ledger_protocol->rx_apdu_sequence_number = 0;
ctx->rx_apdu_status = APDU_STATUS_WAITING;
ctx->rx_apdu_sequence_number = 0;
}

void LEDGER_PROTOCOL_rx(const uint8_t *buffer, uint16_t length)
void LEDGER_PROTOCOL_rx(ledger_protocol_t *ctx, const uint8_t *buffer, uint16_t length)
{
if (!buffer || length < 3) {
if (!ctx || !buffer || length < 3) {
return;
}

memset(ledger_protocol->tx_chunk, 0, sizeof(ledger_protocol->tx_chunk));
memset(ctx->tx_chunk, 0, sizeof(ctx->tx_chunk));

// For all calls to this function, the buffer was pre-initialized to the same constant
// In order for the input buffer to be 'const', this constant is forced directly here
ledger_protocol->tx_chunk[0] = 0xDE;
ledger_protocol->tx_chunk[1] = 0xF1;
ctx->tx_chunk[0] = 0xDE;
ctx->tx_chunk[1] = 0xF1;

switch (buffer[2]) {
case TAG_GET_PROTOCOL_VERSION:
LOG_BLE_PROTOCOL("TAG_GET_PROTOCOL_VERSION\n");
ledger_protocol->tx_chunk[2] = TAG_GET_PROTOCOL_VERSION;
ledger_protocol->tx_chunk_length
= MIN(sizeof(protocol_version), (sizeof(ledger_protocol->tx_chunk) - 3));
memcpy(
&ledger_protocol->tx_chunk[3], protocol_version, ledger_protocol->tx_chunk_length);
ledger_protocol->tx_chunk_length += 3;
ctx->tx_chunk[2] = TAG_GET_PROTOCOL_VERSION;
ctx->tx_chunk_length = MIN(sizeof(protocol_version), (sizeof(ctx->tx_chunk) - 3));
memcpy(&ctx->tx_chunk[3], protocol_version, ctx->tx_chunk_length);
ctx->tx_chunk_length += 3;
break;

case TAG_ALLOCATE_CHANNEL:
LOG_BLE_PROTOCOL("TAG_ALLOCATE_CHANNEL\n");
ledger_protocol->tx_chunk[2] = TAG_ALLOCATE_CHANNEL;
ledger_protocol->tx_chunk_length = 3;
ctx->tx_chunk[2] = TAG_ALLOCATE_CHANNEL;
ctx->tx_chunk_length = 3;
break;

case TAG_PING:
LOG_BLE_PROTOCOL("TAG_PING\n");
ledger_protocol->tx_chunk_length = MIN(sizeof(ledger_protocol->tx_chunk), length);
memcpy(ledger_protocol->tx_chunk, buffer, ledger_protocol->tx_chunk_length);
ctx->tx_chunk_length = MIN(sizeof(ctx->tx_chunk), length);
memcpy(ctx->tx_chunk, buffer, ctx->tx_chunk_length);
break;

case TAG_APDU:
LOG_BLE_PROTOCOL("TAG_APDU\n");
process_apdu_chunk(&buffer[3], length - 3);
process_apdu_chunk(ctx, &buffer[3], length - 3);
break;

case TAG_MTU:
LOG_BLE_PROTOCOL("TAG_MTU\n");
ledger_protocol->tx_chunk[2] = TAG_MTU;
ledger_protocol->tx_chunk[3] = 0x00;
ledger_protocol->tx_chunk[4] = 0x00;
ledger_protocol->tx_chunk[5] = 0x00;
ledger_protocol->tx_chunk[6] = 0x01;
ledger_protocol->tx_chunk[7] = ledger_protocol->mtu - 2;
ledger_protocol->tx_chunk_length = 8;
ctx->tx_chunk[2] = TAG_MTU;
ctx->tx_chunk[3] = 0x00;
ctx->tx_chunk[4] = 0x00;
ctx->tx_chunk[5] = 0x00;
ctx->tx_chunk[6] = 0x01;
ctx->tx_chunk[7] = ctx->mtu - 2;
ctx->tx_chunk_length = 8;
break;

default:
Expand All @@ -176,54 +171,52 @@ void LEDGER_PROTOCOL_rx(const uint8_t *buffer, uint16_t length)
}
}

void LEDGER_PROTOCOL_tx(const uint8_t *buffer, uint16_t length)
void LEDGER_PROTOCOL_tx(ledger_protocol_t *ctx, const uint8_t *buffer, uint16_t length)
{
if (!buffer && !ledger_protocol->tx_apdu_buffer) {
if (!ctx || (!buffer && !ctx->tx_apdu_buffer)) {
return;
}
if (buffer) {
LOG_BLE_PROTOCOL("FIRST CHUNK");
ledger_protocol->tx_apdu_buffer = buffer;
ledger_protocol->tx_apdu_length = length;
ledger_protocol->tx_apdu_sequence_number = 0;
ledger_protocol->tx_apdu_offset = 0;
memset(ledger_protocol->tx_chunk, 0, sizeof(ledger_protocol->tx_chunk));
ctx->tx_apdu_buffer = buffer;
ctx->tx_apdu_length = length;
ctx->tx_apdu_sequence_number = 0;
ctx->tx_apdu_offset = 0;
memset(ctx->tx_chunk, 0, sizeof(ctx->tx_chunk));
}
else {
LOG_BLE_PROTOCOL("NEXT CHUNK");
}

uint16_t tx_chunk_offset = 2; // Because channel id has been already filled beforehand

ledger_protocol->tx_chunk[tx_chunk_offset++] = TAG_APDU;
ctx->tx_chunk[tx_chunk_offset++] = TAG_APDU;

U2BE_ENCODE(
ledger_protocol->tx_chunk, tx_chunk_offset, ledger_protocol->tx_apdu_sequence_number);
U2BE_ENCODE(ctx->tx_chunk, tx_chunk_offset, ctx->tx_apdu_sequence_number);
tx_chunk_offset += 2;

if (ledger_protocol->tx_apdu_sequence_number == 0) {
U2BE_ENCODE(ledger_protocol->tx_chunk, tx_chunk_offset, ledger_protocol->tx_apdu_length);
if (ctx->tx_apdu_sequence_number == 0) {
U2BE_ENCODE(ctx->tx_chunk, tx_chunk_offset, ctx->tx_apdu_length);
tx_chunk_offset += 2;
}
if ((ledger_protocol->tx_apdu_length + tx_chunk_offset)
>= (ledger_protocol->mtu + ledger_protocol->tx_apdu_offset)) {
if ((ctx->tx_apdu_length + tx_chunk_offset) >= (ctx->mtu + ctx->tx_apdu_offset)) {
// Remaining buffer length doesn't fit the chunk
memcpy(&ledger_protocol->tx_chunk[tx_chunk_offset],
&ledger_protocol->tx_apdu_buffer[ledger_protocol->tx_apdu_offset],
ledger_protocol->mtu - tx_chunk_offset);
ledger_protocol->tx_apdu_offset += ledger_protocol->mtu - tx_chunk_offset;
ledger_protocol->tx_apdu_sequence_number++;
tx_chunk_offset = ledger_protocol->mtu;
memcpy(&ctx->tx_chunk[tx_chunk_offset],
&ctx->tx_apdu_buffer[ctx->tx_apdu_offset],
ctx->mtu - tx_chunk_offset);
ctx->tx_apdu_offset += ctx->mtu - tx_chunk_offset;
ctx->tx_apdu_sequence_number++;
tx_chunk_offset = ctx->mtu;
}
else {
// Remaining buffer fits the chunk TODO pad for usb
memcpy(&ledger_protocol->tx_chunk[tx_chunk_offset],
&ledger_protocol->tx_apdu_buffer[ledger_protocol->tx_apdu_offset],
ledger_protocol->tx_apdu_length - ledger_protocol->tx_apdu_offset);
tx_chunk_offset += (ledger_protocol->tx_apdu_length - ledger_protocol->tx_apdu_offset);
ledger_protocol->tx_apdu_offset = ledger_protocol->tx_apdu_length;
ledger_protocol->tx_apdu_buffer = NULL;
memcpy(&ctx->tx_chunk[tx_chunk_offset],
&ctx->tx_apdu_buffer[ctx->tx_apdu_offset],
ctx->tx_apdu_length - ctx->tx_apdu_offset);
tx_chunk_offset += (ctx->tx_apdu_length - ctx->tx_apdu_offset);
ctx->tx_apdu_offset = ctx->tx_apdu_length;
ctx->tx_apdu_buffer = NULL;
}
ledger_protocol->tx_chunk_length = tx_chunk_offset;
LOG_BLE_PROTOCOL(" %d\n", ledger_protocol->tx_chunk_length);
ctx->tx_chunk_length = tx_chunk_offset;
LOG_BLE_PROTOCOL(" %d\n", ctx->tx_chunk_length);
}
Loading

0 comments on commit be085f3

Please sign in to comment.