diff --git a/include/os_io_seproxyhal.h b/include/os_io_seproxyhal.h index 0587289bb..72087b6d5 100644 --- a/include/os_io_seproxyhal.h +++ b/include/os_io_seproxyhal.h @@ -246,6 +246,7 @@ typedef struct io_seph_s { unsigned short apdu_length; // total length to be received unsigned short io_flags; // flags to be set when calling io_exchange io_apdu_media_t apdu_media; + io_apdu_media_t apdu_media_lock; unsigned int ms; @@ -285,11 +286,29 @@ extern io_seph_app_t G_io_app; */ void io_task(void); /** - * IO task initializez + * IO task initialize */ void io_start(void); #endif // HAVE_IO_TASK +/** + * Lock apdu source to a media + * After this function is called, all subsequent apdus + * that come from any other media will be refused + */ +void io_apdu_media_lock(io_apdu_media_t media); +/** + * Unlock apdu source. + * After this function is called, apdus any media will be processed + */ +void io_apdu_media_unlock(void); +/** + * Check if an Apdu media shall be accepted. + * Return true if source media is not locked or if the media corresponds to the current lock + * Return false if media doesn't correspond to the current lock + */ +bool io_apdu_is_media_accepted(io_apdu_media_t media); + void io_seproxyhal_setup_ticker(unsigned int interval_ms); void io_seproxyhal_power_off(bool criticalBattery); void io_seproxyhal_se_reset(void); diff --git a/include/os_io_usb.h b/include/os_io_usb.h index d9b6d99c2..29def885e 100644 --- a/include/os_io_usb.h +++ b/include/os_io_usb.h @@ -50,6 +50,13 @@ io_usb_hid_receive_status_t io_usb_hid_receive(io_send_t sndfct, unsigned short l, apdu_buffer_t *apdu_buffer); +/** + * Read next HID transport packet, keep track of received APDU but discard content. + * Return true when an apdu is fully received (and discarded) + * To be called typically upon USB OUT event while an other apdu is being processed + */ +bool io_usb_hid_discard(unsigned char *buffer, unsigned short l); + /** * Mark the last chunk transmitted as sent. * To be called typically upon USB IN ACK event diff --git a/lib_blewbxx_impl/src/ledger_ble.c b/lib_blewbxx_impl/src/ledger_ble.c index ec3f8719f..ca23322f8 100644 --- a/lib_blewbxx_impl/src/ledger_ble.c +++ b/lib_blewbxx_impl/src/ledger_ble.c @@ -517,7 +517,19 @@ static void hci_evt_cmd_complete(const uint8_t *buffer, uint16_t length) if (ledger_ble_data.transfer_mode_enable) { if ((ledger_protocol_data.rx_apdu_length) && (ledger_protocol_data.rx_apdu_status == APDU_STATUS_COMPLETE)) { - copy_apdu_to_app(false); + if (G_io_app.apdu_state == APDU_IDLE + && io_apdu_is_media_accepted(IO_APDU_MEDIA_BLE)) { + copy_apdu_to_app(false); + } + else { + // Discard received APDU and respond with error + ledger_protocol_data.rx_apdu_length = 0; + ledger_protocol_data.rx_apdu_status = APDU_STATUS_WAITING; + uint8_t resp[2]; + U2BE_ENCODE(resp, 0, SWO_IOL_STA_02); + LEDGER_PROTOCOL_tx(&ledger_protocol_data, resp, 2); + notify_chunk(); + } } } else { @@ -528,7 +540,9 @@ static void hci_evt_cmd_complete(const uint8_t *buffer, uint16_t length) if (!ledger_protocol_data.tx_apdu_buffer) { ledger_protocol_data.tx_chunk_length = 0; G_io_app.ble_xfer_timeout = 0; - G_io_app.apdu_state = APDU_IDLE; + if (G_io_app.apdu_state == APDU_BLE) { + G_io_app.apdu_state = APDU_IDLE; + } if ((!ledger_ble_data.connection_updated) && (ledger_ble_data.connection.conn_interval > BLE_SLAVE_CONN_INTERVAL_MIN)) { ledger_ble_data.connection_updated = 1; @@ -879,7 +893,19 @@ static void attribute_modified(const uint8_t *buffer, uint16_t length) LOG_BLE("Transfer failed 0x%04x\n", U2BE(ledger_ble_data.resp, 0)); G_io_app.transfer_mode = 0; check_transfer_mode(G_io_app.transfer_mode); - copy_apdu_to_app(true); + if (G_io_app.apdu_state == APDU_IDLE + && io_apdu_is_media_accepted(IO_APDU_MEDIA_BLE)) { + copy_apdu_to_app(true); + } + else { + // Discard received APDU and respond with error + ledger_protocol_data.rx_apdu_length = 0; + ledger_protocol_data.rx_apdu_status = APDU_STATUS_WAITING; + uint8_t resp[2]; + U2BE_ENCODE(resp, 0, SWO_IOL_STA_02); + LEDGER_PROTOCOL_tx(&ledger_protocol_data, resp, 2); + notify_chunk(); + } } else if (ledger_ble_data.resp_length) { LEDGER_PROTOCOL_tx( @@ -889,13 +915,26 @@ static void attribute_modified(const uint8_t *buffer, uint16_t length) } } else { - copy_apdu_to_app(true); + // Nominal case for apdu reception + if (G_io_app.apdu_state == APDU_IDLE + && io_apdu_is_media_accepted(IO_APDU_MEDIA_BLE)) { + copy_apdu_to_app(true); + } + else { + // Discard received APDU and respond with error + ledger_protocol_data.rx_apdu_length = 0; + ledger_protocol_data.rx_apdu_status = APDU_STATUS_WAITING; + uint8_t resp[2]; + U2BE_ENCODE(resp, 0, SWO_IOL_STA_02); + LEDGER_PROTOCOL_tx(&ledger_protocol_data, resp, 2); + notify_chunk(); + } } } else if (ledger_protocol_data.tx_chunk_length >= 2) { + // apdu not complete yet G_io_app.ble_xfer_timeout = 2000; notify_chunk(); - G_io_app.apdu_state = APDU_BLE; } } else { @@ -925,7 +964,18 @@ static void write_permit_request(const uint8_t *buffer, uint16_t length) data_length, &buffer[3]); if (ledger_protocol_data.rx_apdu_status == APDU_STATUS_COMPLETE) { - copy_apdu_to_app(true); + if (G_io_app.apdu_state == APDU_IDLE && io_apdu_is_media_accepted(IO_APDU_MEDIA_BLE)) { + copy_apdu_to_app(true); + } + else { + // Discard received APDU and respond with error + ledger_protocol_data.rx_apdu_length = 0; + ledger_protocol_data.rx_apdu_status = APDU_STATUS_WAITING; + uint8_t resp[2]; + U2BE_ENCODE(resp, 0, SWO_IOL_STA_02); + LEDGER_PROTOCOL_tx(&ledger_protocol_data, resp, 2); + notify_chunk(); + } } } else { @@ -1048,7 +1098,9 @@ void LEDGER_BLE_set_recv_buffer(uint8_t *buffer, uint16_t buffer_length) void LEDGER_BLE_send(const uint8_t *packet, uint16_t packet_length) { if ((ledger_ble_data.transfer_mode_enable != 0) && (packet_length == 2)) { - G_io_app.apdu_state = APDU_IDLE; + if (G_io_app.apdu_state == APDU_BLE) { + G_io_app.apdu_state = APDU_IDLE; + } ledger_ble_data.resp_length = 2; ledger_ble_data.resp[0] = packet[0]; ledger_ble_data.resp[1] = packet[1]; diff --git a/lib_standard_app/io.c b/lib_standard_app/io.c index cf878725f..f0397835e 100644 --- a/lib_standard_app/io.c +++ b/lib_standard_app/io.c @@ -61,9 +61,9 @@ WEAK uint8_t io_event(uint8_t channel) UX_BUTTON_PUSH_EVENT(G_io_seproxyhal_spi_buffer); break; case SEPROXYHAL_TAG_STATUS_EVENT: - if (G_io_apdu_media == IO_APDU_MEDIA_USB_HID && // - !(U4BE(G_io_seproxyhal_spi_buffer, 3) & // - SEPROXYHAL_TAG_STATUS_EVENT_FLAG_USB_POWERED)) { + if (G_io_apdu_media == IO_APDU_MEDIA_USB_HID + && !(U4BE(G_io_seproxyhal_spi_buffer, 3) + & SEPROXYHAL_TAG_STATUS_EVENT_FLAG_USB_POWERED)) { THROW(EXCEPTION_IO_RESET); } __attribute__((fallthrough)); diff --git a/lib_stusb/STM32_USB_Device_Library/Class/CCID/src/usbd_ccid_if.c b/lib_stusb/STM32_USB_Device_Library/Class/CCID/src/usbd_ccid_if.c index 777d4ebe5..62775992d 100644 --- a/lib_stusb/STM32_USB_Device_Library/Class/CCID/src/usbd_ccid_if.c +++ b/lib_stusb/STM32_USB_Device_Library/Class/CCID/src/usbd_ccid_if.c @@ -138,7 +138,9 @@ void CCID_BulkMessage_In (USBD_HandleTypeDef *pdev, // not timeout compliant // USBD_LL_PrepareReceive(pdev, CCID_BULK_OUT_EP, CCID_BULK_EPOUT_SIZE); // mark transfer as completed - G_io_app.apdu_state = APDU_IDLE; + if (G_io_app.apdu_state == APDU_USB_CCID){ + G_io_app.apdu_state = APDU_IDLE; + } } // if remaining length is < EPIN_SIZE: send packet and prepare to receive a new command diff --git a/lib_stusb_impl/usbd_impl.c b/lib_stusb_impl/usbd_impl.c index d8b721506..58f0646f9 100644 --- a/lib_stusb_impl/usbd_impl.c +++ b/lib_stusb_impl/usbd_impl.c @@ -1074,7 +1074,8 @@ uint8_t USBD_HID_DataOut_impl(USBD_HandleTypeDef *pdev, #ifndef HAVE_USB_HIDKBD // avoid troubles when an apdu has not been replied yet - if (G_io_app.apdu_media == IO_APDU_MEDIA_NONE) { + if (G_io_app.apdu_state == APDU_IDLE + && io_apdu_is_media_accepted(IO_APDU_MEDIA_USB_HID)) { // add to the hid transport switch (io_usb_hid_receive(io_usb_send_apdu_data, buffer, @@ -1090,6 +1091,14 @@ uint8_t USBD_HID_DataOut_impl(USBD_HandleTypeDef *pdev, break; } } + else { + if (io_usb_hid_discard(buffer, io_seproxyhal_get_ep_rx_size(HID_EPOUT_ADDR))) { + // Discarded new APDU, send error response + unsigned char resp_apdu[2] = {}; + U2BE_ENCODE(resp_apdu, 0, SWO_IOL_STA_02); + io_usb_hid_send(io_usb_send_apdu_data, 2, resp_apdu); + } + } #endif // HAVE_USB_HIDKBD break; } @@ -1154,7 +1163,8 @@ uint8_t USBD_WEBUSB_DataOut(USBD_HandleTypeDef *pdev, USBD_LL_PrepareReceive(pdev, WEBUSB_EPOUT_ADDR, WEBUSB_EPOUT_SIZE); // avoid troubles when an apdu has not been replied yet - if (G_io_app.apdu_media == IO_APDU_MEDIA_NONE) { + if (G_io_app.apdu_state == APDU_IDLE + && io_apdu_is_media_accepted(IO_APDU_MEDIA_USB_WEBUSB)) { // add to the hid transport switch (io_usb_hid_receive(io_usb_send_apdu_data_ep0x83, buffer, @@ -1170,6 +1180,14 @@ uint8_t USBD_WEBUSB_DataOut(USBD_HandleTypeDef *pdev, break; } } + else { + if (io_usb_hid_discard(buffer, io_seproxyhal_get_ep_rx_size(WEBUSB_EPOUT_ADDR))) { + // Discarded new APDU, send error response + unsigned char resp_apdu[2] = {}; + U2BE_ENCODE(resp_apdu, 0, SWO_IOL_STA_02); + io_usb_hid_send(io_usb_send_apdu_data_ep0x83, 2, resp_apdu); + } + } break; } diff --git a/lib_u2f/src/u2f_transport.c b/lib_u2f/src/u2f_transport.c index 4334fdd5c..7a71cfbd6 100644 --- a/lib_u2f/src/u2f_transport.c +++ b/lib_u2f/src/u2f_transport.c @@ -154,7 +154,9 @@ void u2f_transport_sent(u2f_service_t *service, u2f_transport_media_t media) u2f_transport_reset(service); // we sent the whole response (even if we haven't yet received the ack for the last sent usb // in packet) - G_io_app.apdu_state = APDU_IDLE; + if (G_io_app.apdu_state == APDU_U2F) { + G_io_app.apdu_state = APDU_IDLE; + } } } diff --git a/src/os_io_nfc.c b/src/os_io_nfc.c index 3413a22f5..13c10a58e 100644 --- a/src/os_io_nfc.c +++ b/src/os_io_nfc.c @@ -41,6 +41,8 @@ static uint8_t rx_apdu_buffer[IO_APDU_BUFFER_SIZE]; static ledger_protocol_t ledger_protocol_data; +static void nfc_send_rapdu(const uint8_t *packet, uint16_t packet_length); + void io_nfc_init(void) { memset(&rx_apdu_buffer, 0, sizeof(rx_apdu_buffer)); @@ -65,14 +67,24 @@ 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) { - memcpy(ledger_protocol_data.rx_dst_buffer, - ledger_protocol_data.rx_apdu_buffer, - ledger_protocol_data.rx_apdu_length); - G_io_app.apdu_length = ledger_protocol_data.rx_apdu_length; - G_io_app.apdu_state = APDU_NFC; - G_io_app.apdu_media = IO_APDU_MEDIA_NFC; - ledger_protocol_data.rx_apdu_length = 0; - ledger_protocol_data.rx_apdu_status = APDU_STATUS_WAITING; + if (G_io_app.apdu_state == APDU_IDLE && io_apdu_is_media_accepted(IO_APDU_MEDIA_NFC)) { + memcpy(ledger_protocol_data.rx_dst_buffer, + ledger_protocol_data.rx_apdu_buffer, + ledger_protocol_data.rx_apdu_length); + G_io_app.apdu_length = ledger_protocol_data.rx_apdu_length; + G_io_app.apdu_state = APDU_NFC; + G_io_app.apdu_media = IO_APDU_MEDIA_NFC; + ledger_protocol_data.rx_apdu_length = 0; + ledger_protocol_data.rx_apdu_status = APDU_STATUS_WAITING; + } + else { + ledger_protocol_data.rx_apdu_length = 0; + ledger_protocol_data.rx_apdu_status = APDU_STATUS_WAITING; + // Refuse APDU, send error reply + uint8_t response[2] = {}; + U2BE_ENCODE(response, 0, SWO_IOL_STA_02); + nfc_send_rapdu(response, sizeof(response)); + } } } diff --git a/src/os_io_seproxyhal.c b/src/os_io_seproxyhal.c index 155681a03..848f2c92b 100644 --- a/src/os_io_seproxyhal.c +++ b/src/os_io_seproxyhal.c @@ -228,7 +228,7 @@ void io_usb_send_apdu_data_ep0x83(unsigned char *buffer, unsigned short length) void io_seproxyhal_handle_capdu_event(void) { - if (G_io_app.apdu_state == APDU_IDLE) { + if (G_io_app.apdu_state == APDU_IDLE && io_apdu_is_media_accepted(IO_APDU_MEDIA_RAW)) { size_t max = MIN(sizeof(G_io_apdu_buffer) - 3, sizeof(G_io_seproxyhal_spi_buffer) - 3); size_t size = U2BE(G_io_seproxyhal_spi_buffer, 1); @@ -238,6 +238,14 @@ void io_seproxyhal_handle_capdu_event(void) // copy apdu to apdu buffer memcpy(G_io_apdu_buffer, G_io_seproxyhal_spi_buffer + 3, G_io_app.apdu_length); } + else { + // Refuse APDU, send error reply + G_io_seproxyhal_spi_buffer[0] = SEPROXYHAL_TAG_RAPDU; + G_io_seproxyhal_spi_buffer[1] = 0; + G_io_seproxyhal_spi_buffer[2] = 2; + U2BE_ENCODE(G_io_seproxyhal_spi_buffer, 3, SWO_IOL_STA_02); + io_seproxyhal_spi_send(G_io_seproxyhal_spi_buffer, 5); + } } unsigned int io_seproxyhal_handle_event(void) @@ -438,9 +446,10 @@ void io_seproxyhal_init(void) G_io_app.plane_mode = plane; #endif // HAVE_BLE - G_io_app.apdu_state = APDU_IDLE; - G_io_app.apdu_length = 0; - G_io_app.apdu_media = IO_APDU_MEDIA_NONE; + G_io_app.apdu_state = APDU_IDLE; + G_io_app.apdu_length = 0; + G_io_app.apdu_media = IO_APDU_MEDIA_NONE; + G_io_app.apdu_media_lock = IO_APDU_MEDIA_NONE; G_io_app.ms = 0; @@ -467,6 +476,22 @@ void io_seproxyhal_init(void) #endif // !defined(HAVE_BOLOS) && defined(HAVE_PENDING_REVIEW_SCREEN) } +void io_apdu_media_lock(io_apdu_media_t media) +{ + G_io_app.apdu_media_lock = media; +} +void io_apdu_media_unlock(void) +{ + G_io_app.apdu_media_lock = IO_APDU_MEDIA_NONE; +} +bool io_apdu_is_media_accepted(io_apdu_media_t media) +{ + if (G_io_app.apdu_media_lock == IO_APDU_MEDIA_NONE) { + return true; + } + return (G_io_app.apdu_media_lock == media); +} + #ifdef HAVE_PIEZO_SOUND void io_seproxyhal_play_tune(tune_index_e tune_index) { @@ -1393,8 +1418,9 @@ unsigned short io_exchange(unsigned char channel, unsigned short tx_len) io_seproxyhal_spi_send(G_io_seproxyhal_spi_buffer, 3); io_seproxyhal_spi_send(G_io_apdu_buffer, tx_len); - // isngle packet reply, mark immediate idle + // single packet reply, mark immediate idle G_io_app.apdu_state = APDU_IDLE; + G_io_app.apdu_media = IO_APDU_MEDIA_NONE; // finished, no chunking goto break_send; diff --git a/src/os_io_usb.c b/src/os_io_usb.c index 5af0ee509..e65ea3d61 100644 --- a/src/os_io_usb.c +++ b/src/os_io_usb.c @@ -238,6 +238,45 @@ io_usb_hid_receive_status_t io_usb_hid_receive(io_send_t sndfct, return IO_USB_APDU_RESET; } +bool io_usb_hid_discard(unsigned char *buffer, unsigned short l) +{ + static unsigned int seq_number = 0; + static unsigned int remaining_length = 0; + + if (buffer[2] != 0x05) { + return false; + } + + // ensure sequence idx is 0 for the first chunk + if ((unsigned int) U2BE(buffer, 3) != (unsigned int) seq_number) { + // ignore packet + seq_number = 0; + remaining_length = 0; + return false; + } + + // cid, tag, seq + l -= 2 + 1 + 2; + + if (seq_number == 0) { + // total apdu size to receive + remaining_length = U2BE(buffer, 5); + l -= 2; + } + if (l > remaining_length) { + l = remaining_length; + } + remaining_length -= l; + seq_number++; + + // Full apdu received, reset sequence number for next time + if (remaining_length == 0) { + seq_number = 0; + return true; + } + return false; +} + void io_usb_hid_init(void) { G_io_usb_hid_sequence_number = 0; @@ -293,7 +332,9 @@ void io_usb_hid_sent(io_send_t sndfct) io_usb_hid_init(); // we sent the whole response - G_io_app.apdu_state = APDU_IDLE; + if (G_io_app.apdu_state == APDU_USB_HID || G_io_app.apdu_state == APDU_USB_WEBUSB) { + G_io_app.apdu_state = APDU_IDLE; + } } }