diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..0c0d678
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,66 @@
+ #
+ # This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid).
+ # Copyright (c) 2022 Pol Henarejos.
+ #
+ # This program is free software: you can redistribute it and/or modify
+ # it under the terms of the GNU General Public License as published by
+ # the Free Software Foundation, version 3.
+ #
+ # This program is distributed in the hope that it will be useful, but
+ # WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ # General Public License for more details.
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program. If not, see .
+ #
+
+cmake_minimum_required(VERSION 3.13)
+
+include(pico_sdk_import.cmake)
+
+project(pico_ccid C CXX ASM)
+
+set(CMAKE_C_STANDARD 11)
+set(CMAKE_CXX_STANDARD 17)
+
+pico_sdk_init()
+
+add_executable(pico_ccid)
+
+if (NOT DEFINED USB_VID)
+ set(USB_VID 0xFEFF)
+endif()
+add_definitions(-DUSB_VID=${USB_VID})
+if (NOT DEFINED USB_PID)
+ set(USB_PID 0xFCFD)
+endif()
+add_definitions(-DUSB_PID=${USB_PID})
+
+set_source_files_properties(
+ ${CMAKE_CURRENT_LIST_DIR}/OpenSC/src/libopensc/ctx.c
+ PROPERTIES COMPILE_DEFINITIONS "PACKAGE_VERSION=\"0.22.0\";OPENSC_CONF_PATH=\".\""
+ )
+
+target_sources(pico_ccid PUBLIC
+ ${CMAKE_CURRENT_LIST_DIR}/src/ccid/ccid2040.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/usb/usb_descriptors.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/fs/file.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/fs/flash.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/fs/low_flash.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/rng/random.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/rng/neug.c
+ )
+
+target_include_directories(pico_ccid PUBLIC
+ ${CMAKE_CURRENT_LIST_DIR}/src/fs
+ ${CMAKE_CURRENT_LIST_DIR}/src/ccid
+ ${CMAKE_CURRENT_LIST_DIR}/src/rng
+ ${CMAKE_CURRENT_LIST_DIR}/src/usb
+ )
+
+pico_add_extra_outputs(pico_ccid)
+
+#target_compile_definitions(pico_ccid PRIVATE MBEDTLS_ECDSA_DETERMINISTIC=1)
+
+target_link_libraries(pico_ccid PRIVATE pico_stdlib tinyusb_device tinyusb_board pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id hardware_rtc)
diff --git a/pico_sdk_import.cmake b/pico_sdk_import.cmake
new file mode 100644
index 0000000..28efe9e
--- /dev/null
+++ b/pico_sdk_import.cmake
@@ -0,0 +1,62 @@
+# This is a copy of /external/pico_sdk_import.cmake
+
+# This can be dropped into an external project to help locate this SDK
+# It should be include()ed prior to project()
+
+if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
+ set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
+ message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
+endif ()
+
+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
+ set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
+ message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
+endif ()
+
+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
+ set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
+ message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
+endif ()
+
+set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
+set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
+set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
+
+if (NOT PICO_SDK_PATH)
+ if (PICO_SDK_FETCH_FROM_GIT)
+ include(FetchContent)
+ set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
+ if (PICO_SDK_FETCH_FROM_GIT_PATH)
+ get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
+ endif ()
+ FetchContent_Declare(
+ pico_sdk
+ GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
+ GIT_TAG master
+ )
+ if (NOT pico_sdk)
+ message("Downloading Raspberry Pi Pico SDK")
+ FetchContent_Populate(pico_sdk)
+ set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
+ endif ()
+ set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
+ else ()
+ message(FATAL_ERROR
+ "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
+ )
+ endif ()
+endif ()
+
+get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
+if (NOT EXISTS ${PICO_SDK_PATH})
+ message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
+endif ()
+
+set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
+if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
+ message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
+endif ()
+
+set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
+
+include(${PICO_SDK_INIT_CMAKE_FILE})
diff --git a/src/ccid/ccid2040.c b/src/ccid/ccid2040.c
new file mode 100644
index 0000000..92ac0d5
--- /dev/null
+++ b/src/ccid/ccid2040.c
@@ -0,0 +1,1575 @@
+/*
+ * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid).
+ * Copyright (c) 2022 Pol Henarejos.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+#include
+
+// Pico
+#include "pico/stdlib.h"
+#include
+
+// For memcpy
+#include
+
+#include "bsp/board.h"
+#include "tusb.h"
+#include "usb_descriptors.h"
+#include "device/usbd_pvt.h"
+#include "pico/multicore.h"
+#include "random.h"
+#include "ccid2040.h"
+#include "hardware/rtc.h"
+
+extern void do_flash();
+extern void low_flash_init();
+
+static uint8_t itf_num;
+
+#if MAX_RES_APDU_DATA_SIZE > MAX_CMD_APDU_DATA_SIZE
+#define USB_BUF_SIZE (MAX_RES_APDU_DATA_SIZE+20+9)
+#else
+#define USB_BUF_SIZE (MAX_CMD_APDU_DATA_SIZE+20+9)
+#endif
+
+struct apdu apdu;
+static struct ccid ccid;
+
+static uint8_t ccid_buffer[USB_BUF_SIZE];
+
+#define CCID_SET_PARAMS 0x61 /* non-ICCD command */
+#define CCID_POWER_ON 0x62
+#define CCID_POWER_OFF 0x63
+#define CCID_SLOT_STATUS 0x65 /* non-ICCD command */
+#define CCID_SECURE 0x69 /* non-ICCD command */
+#define CCID_GET_PARAMS 0x6C /* non-ICCD command */
+#define CCID_RESET_PARAMS 0x6D /* non-ICCD command */
+#define CCID_XFR_BLOCK 0x6F
+#define CCID_DATA_BLOCK_RET 0x80
+#define CCID_SLOT_STATUS_RET 0x81 /* non-ICCD result */
+#define CCID_PARAMS_RET 0x82 /* non-ICCD result */
+
+#define CCID_MSG_SEQ_OFFSET 6
+#define CCID_MSG_STATUS_OFFSET 7
+#define CCID_MSG_ERROR_OFFSET 8
+#define CCID_MSG_CHAIN_OFFSET 9
+#define CCID_MSG_DATA_OFFSET 10 /* == CCID_MSG_HEADER_SIZE */
+#define CCID_MAX_MSG_DATA_SIZE USB_BUF_SIZE
+
+#define CCID_STATUS_RUN 0x00
+#define CCID_STATUS_PRESENT 0x01
+#define CCID_STATUS_NOTPRESENT 0x02
+#define CCID_CMD_STATUS_OK 0x00
+#define CCID_CMD_STATUS_ERROR 0x40
+#define CCID_CMD_STATUS_TIMEEXT 0x80
+
+#define CCID_ERROR_XFR_OVERRUN 0xFC
+
+/*
+ * Since command-byte is at offset 0,
+ * error with offset 0 means "command not supported".
+ */
+#define CCID_OFFSET_CMD_NOT_SUPPORTED 0
+#define CCID_OFFSET_DATA_LEN 1
+#define CCID_OFFSET_PARAM 8
+
+static app_t apps[4];
+static uint8_t num_apps = 0;
+
+app_t *current_app = NULL;
+
+extern void card_thread();
+
+queue_t *card_comm = NULL;
+queue_t *ccid_comm = NULL;
+extern void low_flash_init_core1();
+
+int register_app(app_t * (*select_aid)()) {
+ if (num_apps < sizeof(apps)/sizeof(app_t)) {
+ apps[num_apps].select_aid = select_aid;
+ num_apps++;
+ return 1;
+ }
+ return 0;
+}
+
+struct ep_in {
+ uint8_t ep_num;
+ uint8_t tx_done;
+ const uint8_t *buf;
+ size_t cnt;
+ size_t buf_len;
+ void *priv;
+ void (*next_buf) (struct ep_in *epi, size_t len);
+};
+
+static void epi_init (struct ep_in *epi, int ep_num, void *priv) {
+ epi->ep_num = ep_num;
+ epi->tx_done = 0;
+ epi->buf = NULL;
+ epi->cnt = 0;
+ epi->buf_len = 0;
+ epi->priv = priv;
+ epi->next_buf = NULL;
+}
+
+struct ep_out {
+ uint8_t ep_num;
+ uint8_t err;
+ uint8_t *buf;
+ size_t cnt;
+ size_t buf_len;
+ void *priv;
+ void (*next_buf) (struct ep_out *epo, size_t len);
+ int (*end_rx) (struct ep_out *epo, size_t orig_len);
+ uint8_t ready;
+};
+
+static struct ep_out endpoint_out;
+static struct ep_in endpoint_in;
+
+static void epo_init (struct ep_out *epo, int ep_num, void *priv) {
+ epo->ep_num = ep_num;
+ epo->err = 0;
+ epo->buf = NULL;
+ epo->cnt = 0;
+ epo->buf_len = 0;
+ epo->priv = priv;
+ epo->next_buf = NULL;
+ epo->end_rx = NULL;
+ epo->ready = 0;
+}
+
+struct ccid_header {
+ uint8_t msg_type;
+ uint32_t data_len;
+ uint8_t slot;
+ uint8_t seq;
+ uint8_t rsvd;
+ uint16_t param;
+} __attribute__((packed));
+
+
+/* Data structure handled by CCID layer */
+struct ccid {
+ uint32_t ccid_state : 4;
+ uint32_t state : 4;
+ uint32_t err : 1;
+ uint32_t tx_busy : 1;
+ uint32_t timeout_cnt: 3;
+
+ uint8_t *p;
+ size_t len;
+
+ struct ccid_header ccid_header;
+
+ uint8_t sw1sw2[2];
+ uint8_t chained_cls_ins_p1_p2[4];
+
+ struct ep_out *epo;
+ struct ep_in *epi;
+
+ queue_t ccid_comm;
+ queue_t card_comm;
+
+ uint8_t application;
+
+ struct apdu *a;
+};
+
+static uint8_t endp1_rx_buf[64];
+static uint8_t endp1_tx_buf[64];
+
+#define APDU_STATE_WAIT_COMMAND 0
+#define APDU_STATE_COMMAND_CHAINING 1
+#define APDU_STATE_COMMAND_RECEIVED 2
+#define APDU_STATE_RESULT 3
+#define APDU_STATE_RESULT_GET_RESPONSE 4
+
+static void ccid_prepare_receive (struct ccid *c);
+static void apdu_init (struct apdu *a);
+
+static void ccid_reset (struct ccid *c) {
+ apdu_init(c->a);
+ c->err = 0;
+ c->tx_busy = 0;
+ c->state = APDU_STATE_WAIT_COMMAND;
+ c->p = c->a->cmd_apdu_data;
+ c->len = MAX_CMD_APDU_DATA_SIZE;
+ c->a->cmd_apdu_data_len = 0;
+ c->a->expected_res_size = 0;
+}
+
+static void ccid_init(struct ccid *c, struct ep_in *epi, struct ep_out *epo, struct apdu *a) {
+ c->ccid_state = CCID_STATE_START;
+ c->err = 0;
+ c->tx_busy = 0;
+ c->state = APDU_STATE_WAIT_COMMAND;
+ c->p = a->cmd_apdu_data;
+ c->len = MAX_CMD_APDU_DATA_SIZE;
+ memset (&c->ccid_header, 0, sizeof (struct ccid_header));
+ c->sw1sw2[0] = 0x90;
+ c->sw1sw2[1] = 0x00;
+ c->application = 0;
+ c->epi = epi;
+ c->epo = epo;
+ c->a = a;
+
+ queue_init(&c->card_comm, sizeof(uint32_t), 64);
+ queue_init(&c->ccid_comm, sizeof(uint32_t), 64);
+}
+
+#define CMD_APDU_HEAD_SIZE 5
+
+static void apdu_init (struct apdu *a) {
+ a->seq = 0; /* will be set by lower layer */
+ a->cmd_apdu_head = &ccid_buffer[0];
+ a->cmd_apdu_data = &ccid_buffer[5];
+ a->cmd_apdu_data_len = 0; /* will be set by lower layer */
+ a->expected_res_size = 0; /* will be set by lower layer */
+
+ a->sw = 0x9000; /* will be set by upper layer */
+ a->res_apdu_data = &ccid_buffer[5]; /* will be set by upper layer */
+ a->res_apdu_data_len = 0; /* will be set by upper layer */
+}
+
+/*
+!!!! IT USES ENDP2, Interruption
+
+#define NOTIFY_SLOT_CHANGE 0x50
+static void ccid_notify_slot_change(struct ccid *c)
+{
+ uint8_t msg;
+ uint8_t notification[2];
+
+ if (c->ccid_state == CCID_STATE_NOCARD)
+ msg = 0x02;
+ else
+ msg = 0x03;
+
+ notification[0] = NOTIFY_SLOT_CHANGE;
+ notification[1] = msg;
+
+ tud_vendor_write(notification, sizeof(notification));
+}
+*/
+
+#define USB_CCID_TIMEOUT (50)
+
+#define GPG_THREAD_TERMINATED 0xffff
+#define GPG_ACK_TIMEOUT 0x6600
+
+static void ccid_init_cb(void) {
+ struct ccid *c = &ccid;
+ TU_LOG1("-------- CCID INIT\r\n");
+ vendord_init();
+
+ //ccid_notify_slot_change(c);
+}
+
+static void ccid_reset_cb(uint8_t rhport) {
+ TU_LOG1("-------- CCID RESET\r\n");
+ itf_num = 0;
+ vendord_reset(rhport);
+}
+
+static uint16_t ccid_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
+ uint8_t *itf_vendor = (uint8_t *)malloc(sizeof(uint8_t)*max_len);
+ TU_LOG1("-------- CCID OPEN\r\n");
+ TU_VERIFY(itf_desc->bInterfaceClass == TUSB_CLASS_SMART_CARD && itf_desc->bInterfaceSubClass == 0 && itf_desc->bInterfaceProtocol == 0, 0);
+
+ //vendord_open expects a CLASS_VENDOR interface class
+ memcpy(itf_vendor, itf_desc, sizeof(uint8_t)*max_len);
+ ((tusb_desc_interface_t *)itf_vendor)->bInterfaceClass = TUSB_CLASS_VENDOR_SPECIFIC;
+ vendord_open(rhport, (tusb_desc_interface_t *)itf_vendor, max_len);
+ free(itf_vendor);
+
+ uint16_t const drv_len = sizeof(tusb_desc_interface_t) + sizeof(struct ccid_class_descriptor) + 2*sizeof(tusb_desc_endpoint_t);
+ TU_VERIFY(max_len >= drv_len, 0);
+
+ itf_num = itf_desc->bInterfaceNumber;
+ return drv_len;
+}
+
+// Support for parameterized reset via vendor interface control request
+static bool ccid_control_xfer_cb(uint8_t __unused rhport, uint8_t stage, tusb_control_request_t const * request) {
+ // nothing to do with DATA & ACK stage
+ TU_LOG2("-------- CCID CTRL XFER\r\n");
+ if (stage != CONTROL_STAGE_SETUP) return true;
+
+ if (request->wIndex == itf_num)
+ {
+ TU_LOG2("-------- bmRequestType %x, bRequest %x, wValue %x, wLength %x\r\n",request->bmRequestType,request->bRequest, request->wValue, request->wLength);
+/*
+#if PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_BOOTSEL
+ if (request->bRequest == RESET_REQUEST_BOOTSEL) {
+#ifdef PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED
+ uint gpio_mask = 1u << PICO_STDIO_USB_RESET_BOOTSEL_ACTIVITY_LED;
+#else
+ uint gpio_mask = 0u;
+#endif
+#if !PICO_STDIO_USB_RESET_BOOTSEL_FIXED_ACTIVITY_LED
+ if (request->wValue & 0x100) {
+ gpio_mask = 1u << (request->wValue >> 9u);
+ }
+#endif
+ reset_usb_boot(gpio_mask, (request->wValue & 0x7f) | PICO_STDIO_USB_RESET_BOOTSEL_INTERFACE_DISABLE_MASK);
+ // does not return, otherwise we'd return true
+ }
+#endif
+
+#if PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_RESET_TO_FLASH_BOOT
+ if (request->bRequest == RESET_REQUEST_FLASH) {
+ watchdog_reboot(0, 0, PICO_STDIO_USB_RESET_RESET_TO_FLASH_DELAY_MS);
+ return true;
+ }
+#endif
+*/
+ return true;
+ }
+ return false;
+}
+
+static bool ccid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
+ //TU_LOG2("------ CALLED XFER_CB\r\n");
+ return vendord_xfer_cb(rhport, ep_addr, result, xferred_bytes);
+ //return true;
+}
+
+
+static usbd_class_driver_t const ccid_driver = {
+#if CFG_TUSB_DEBUG >= 2
+ .name = "CCID",
+#endif
+ .init = ccid_init_cb,
+ .reset = ccid_reset_cb,
+ .open = ccid_open,
+ .control_xfer_cb = ccid_control_xfer_cb,
+ .xfer_cb = ccid_xfer_cb,
+ .sof = NULL
+};
+
+// Implement callback to add our custom driver
+usbd_class_driver_t const *usbd_app_driver_get_cb(uint8_t *driver_count) {
+ *driver_count = 1;
+ return &ccid_driver;
+}
+
+
+static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
+
+void led_set_blink(uint32_t mode) {
+ blink_interval_ms = mode;
+}
+
+void execute_tasks();
+
+static void wait_button() {
+ led_set_blink((1000 << 16) | 100);
+ while (board_button_read() == false) {
+ execute_tasks();
+ //sleep_ms(10);
+ }
+ while (board_button_read() == true) {
+ execute_tasks();
+ //sleep_ms(10);
+ }
+ led_set_blink(BLINK_PROCESSING);
+}
+
+void usb_tx_enable(const uint8_t *buf, uint32_t len) {
+ if (len > 0) {
+ if (buf[0] != 0x81)
+ DEBUG_PAYLOAD(buf,len);
+ //DEBUG_PAYLOAD(buf,len);
+ tud_vendor_write(buf, len);
+ }
+}
+
+/*
+ * ATR (Answer To Reset) string
+ *
+ * TS = 0x3b: Direct conversion
+ * T0 = 0xda: TA1, TC1 and TD1 follow, 10 historical bytes
+ * TA1 = 0x11: FI=1, DI=1
+ * TC1 = 0xff
+ * TD1 = 0x81: TD2 follows, T=1
+ * TD2 = 0xb1: TA3, TB3 and TD3 follow, T=1
+ * TA3 = 0xFE: IFSC = 254 bytes
+ * TB3 = 0x55: BWI = 5, CWI = 5 (BWT timeout 3.2 sec)
+ * TD3 = 0x1f: TA4 follows, T=15
+ * TA4 = 0x03: 5V or 3.3V
+ *
+ * Minimum: 0x3b, 0x8a, 0x80, 0x01
+ *
+ */
+static const uint8_t ATR_head[] = {
+ 0x3b, 0xda, 0x11, 0xff, 0x81, 0xb1, 0xfe, 0x55, 0x1f, 0x03,
+ //0x3B,0xFE,0x18,0x00,0x00,0x81,0x31,0xFE,0x45,0x80,0x31,0x81,0x54,0x48,0x53,0x4D,0x31,0x73,0x80,0x21,0x40,0x81,0x07,0xFA
+};
+
+/* Send back ATR (Answer To Reset) */
+static enum ccid_state ccid_power_on(struct ccid *c) {
+ TU_LOG1("!!! CCID POWER ON %d\r\n",c->application);
+ uint8_t p[CCID_MSG_HEADER_SIZE+1]; /* >= size of historical_bytes -1 */
+ int hist_len = historical_bytes[0];
+
+ //char atr_sc_hsm[] = { 0x3B,0x8E,0x80,0x01,0x80,0x31,0x81,0x54,0x48,0x53,0x4D,0x31,0x73,0x80,0x21,0x40,0x81,0x07,0x18 };
+ //char atr_sc_hsm[] = { 0x3B, 0xDE, 0x18, 0xFF, 0x81, 0x91, 0xFE, 0x1F, 0xC3, 0x80, 0x31, 0x81, 0x54, 0x48, 0x53, 0x4D, 0x31, 0x73, 0x80, 0x21, 0x40, 0x81, 0x07, 0x1C };
+ char atr_sc_hsm[] = { 0x3B,0xFE,0x18,0x00,0x00,0x81,0x31,0xFE,0x45,0x80,0x31,0x81,0x54,0x48,0x53,0x4D,0x31,0x73,0x80,0x21,0x40,0x81,0x07,0xFA };
+ uint8_t mode = 1; //1 sc-hsm, 0 openpgp
+ size_t size_atr;
+ if (mode == 1)
+ size_atr = sizeof(atr_sc_hsm);
+ else
+ size_atr = sizeof (ATR_head) + hist_len + 1;
+ uint8_t xor_check = 0;
+ int i;
+ if (c->application == 0) {
+ multicore_reset_core1();
+ multicore_launch_core1(card_thread);
+ multicore_fifo_push_blocking((uint32_t)&c->ccid_comm);
+ multicore_fifo_push_blocking((uint32_t)&c->card_comm);
+ c->application = 1;
+ }
+ p[0] = CCID_DATA_BLOCK_RET;
+ p[1] = size_atr;
+ p[2] = 0x00;
+ p[3] = 0x00;
+ p[4] = 0x00;
+ p[5] = 0x00; /* Slot */
+ p[CCID_MSG_SEQ_OFFSET] = c->ccid_header.seq;
+ p[CCID_MSG_STATUS_OFFSET] = 0x00;
+ p[CCID_MSG_ERROR_OFFSET] = 0x00;
+ p[CCID_MSG_CHAIN_OFFSET] = 0x00;
+
+ memcpy(endp1_tx_buf, p, CCID_MSG_HEADER_SIZE);
+ if (mode == 1) {
+ memcpy(endp1_tx_buf+CCID_MSG_HEADER_SIZE, atr_sc_hsm, sizeof(atr_sc_hsm));
+ }
+ else {
+ memcpy(endp1_tx_buf+CCID_MSG_HEADER_SIZE, ATR_head, sizeof (ATR_head));
+
+ for (i = 1; i < (int)sizeof (ATR_head); i++)
+ xor_check ^= ATR_head[i];
+ memcpy (p, historical_bytes + 1, hist_len);
+#ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT
+ if (file_selection == 255)
+ p[7] = 0x03;
+#endif
+ for (i = 0; i < hist_len; i++)
+ xor_check ^= p[i];
+ p[i] = xor_check;
+ memcpy(endp1_tx_buf+CCID_MSG_HEADER_SIZE+sizeof (ATR_head), p, hist_len+1);
+ }
+
+ /* This is a single packet Bulk-IN transaction */
+ c->epi->buf = NULL;
+ c->epi->tx_done = 1;
+
+ usb_tx_enable(endp1_tx_buf, CCID_MSG_HEADER_SIZE + size_atr);
+
+ DEBUG_INFO("ON\r\n");
+ c->tx_busy = 1;
+ led_set_blink(BLINK_MOUNTED);
+ return CCID_STATE_WAIT;
+}
+
+static void ccid_send_status(struct ccid *c) {
+ uint8_t ccid_reply[CCID_MSG_HEADER_SIZE];
+
+ ccid_reply[0] = CCID_SLOT_STATUS_RET;
+ ccid_reply[1] = 0x00;
+ ccid_reply[2] = 0x00;
+ ccid_reply[3] = 0x00;
+ ccid_reply[4] = 0x00;
+ ccid_reply[5] = 0x00; /* Slot */
+ ccid_reply[CCID_MSG_SEQ_OFFSET] = c->ccid_header.seq;
+ if (c->ccid_state == CCID_STATE_NOCARD)
+ ccid_reply[CCID_MSG_STATUS_OFFSET] = 2; /* 2: No ICC present */
+ else if (c->ccid_state == CCID_STATE_START)
+ /* 1: ICC present but not activated */
+ ccid_reply[CCID_MSG_STATUS_OFFSET] = 1;
+ else
+ ccid_reply[CCID_MSG_STATUS_OFFSET] = 0; /* An ICC is present and active */
+ ccid_reply[CCID_MSG_ERROR_OFFSET] = 0x00;
+ ccid_reply[CCID_MSG_CHAIN_OFFSET] = 0x00;
+
+ /* This is a single packet Bulk-IN transaction */
+ c->epi->buf = NULL;
+ c->epi->tx_done = 1;
+
+ memcpy(endp1_tx_buf, ccid_reply, CCID_MSG_HEADER_SIZE);
+ usb_tx_enable(endp1_tx_buf, CCID_MSG_HEADER_SIZE);
+ c->tx_busy = 1;
+}
+
+static enum ccid_state ccid_power_off(struct ccid *c) {
+ if (c->application) {
+ uint32_t flag = EV_EXIT;
+ queue_try_add(&c->card_comm, &flag);
+ c->application = 0;
+ }
+
+ c->ccid_state = CCID_STATE_START; /* This status change should be here */
+ ccid_send_status (c);
+ DEBUG_INFO ("OFF\r\n");
+ c->tx_busy = 1;
+ led_set_blink(BLINK_SUSPENDED);
+ return CCID_STATE_START;
+}
+
+static void no_buf(struct ep_in *epi, size_t len) {
+ (void)len;
+ epi->buf = NULL;
+ epi->cnt = 0;
+ epi->buf_len = 0;
+}
+
+static void set_sw1sw2(struct ccid *c, size_t chunk_len) {
+ if (c->a->expected_res_size >= c->len) {
+ c->sw1sw2[0] = 0x90;
+ c->sw1sw2[1] = 0x00;
+ }
+ else
+ {
+ c->sw1sw2[0] = 0x61;
+ if (c->len - chunk_len >= 256)
+ c->sw1sw2[1] = 0;
+ else
+ c->sw1sw2[1] = (uint8_t)(c->len - chunk_len);
+ }
+}
+
+static void get_sw1sw2(struct ep_in *epi, size_t len) {
+ struct ccid *c = (struct ccid *)epi->priv;
+
+ (void)len;
+ epi->buf = c->sw1sw2;
+ epi->cnt = 0;
+ epi->buf_len = 2;
+ epi->next_buf = no_buf;
+}
+
+static void ccid_send_data_block_internal(struct ccid *c, uint8_t status, uint8_t error) {
+ int tx_size = USB_LL_BUF_SIZE;
+ uint8_t p[CCID_MSG_HEADER_SIZE];
+ size_t len;
+
+ if (status == 0)
+ len = c->a->res_apdu_data_len + 2;
+ else
+ len = 0;
+
+ p[0] = CCID_DATA_BLOCK_RET;
+ p[1] = len & 0xFF;
+ p[2] = (len >> 8)& 0xFF;
+ p[3] = (len >> 16)& 0xFF;
+ p[4] = (len >> 24)& 0xFF;
+ p[5] = 0x00; /* Slot */
+ p[CCID_MSG_SEQ_OFFSET] = c->a->seq;
+ p[CCID_MSG_STATUS_OFFSET] = status;
+ p[CCID_MSG_ERROR_OFFSET] = error;
+ p[CCID_MSG_CHAIN_OFFSET] = 0;
+
+ memcpy(endp1_tx_buf, p, CCID_MSG_HEADER_SIZE);
+
+ if (len == 0) {
+ c->epi->buf = NULL;
+ c->epi->tx_done = 1;
+ usb_tx_enable(endp1_tx_buf, CCID_MSG_HEADER_SIZE);
+ c->tx_busy = 1;
+ return;
+ }
+
+ if (CCID_MSG_HEADER_SIZE + len <= USB_LL_BUF_SIZE) {
+ memcpy(endp1_tx_buf+CCID_MSG_HEADER_SIZE, c->a->res_apdu_data, c->a->res_apdu_data_len);
+ memcpy(endp1_tx_buf+CCID_MSG_HEADER_SIZE+c->a->res_apdu_data_len, c->sw1sw2, 2);
+
+ c->epi->buf = NULL;
+ if (CCID_MSG_HEADER_SIZE + len < USB_LL_BUF_SIZE)
+ c->epi->tx_done = 1;
+ tx_size = CCID_MSG_HEADER_SIZE + len;
+ }
+ else if (CCID_MSG_HEADER_SIZE + len - 1 == USB_LL_BUF_SIZE) {
+ memcpy(endp1_tx_buf+CCID_MSG_HEADER_SIZE, c->a->res_apdu_data, c->a->res_apdu_data_len);
+ memcpy(endp1_tx_buf+CCID_MSG_HEADER_SIZE+c->a->res_apdu_data_len, c->sw1sw2, 1);
+
+ c->epi->buf = &c->sw1sw2[1];
+ c->epi->cnt = 1;
+ c->epi->buf_len = 1;
+ c->epi->next_buf = no_buf;
+ }
+ else if (CCID_MSG_HEADER_SIZE + len - 2 == USB_LL_BUF_SIZE) {
+ memcpy(endp1_tx_buf+CCID_MSG_HEADER_SIZE, c->a->res_apdu_data, c->a->res_apdu_data_len);
+
+ c->epi->buf = &c->sw1sw2[0];
+ c->epi->cnt = 0;
+ c->epi->buf_len = 2;
+ c->epi->next_buf = no_buf;
+ }
+ else {
+ memcpy(endp1_tx_buf+CCID_MSG_HEADER_SIZE, c->a->res_apdu_data, USB_LL_BUF_SIZE - CCID_MSG_HEADER_SIZE);
+
+ c->epi->buf = c->a->res_apdu_data + USB_LL_BUF_SIZE - CCID_MSG_HEADER_SIZE;
+ c->epi->cnt = USB_LL_BUF_SIZE - CCID_MSG_HEADER_SIZE;
+ c->epi->buf_len = c->a->res_apdu_data_len - (USB_LL_BUF_SIZE - CCID_MSG_HEADER_SIZE);
+ c->epi->next_buf = get_sw1sw2;
+ }
+ usb_tx_enable(endp1_tx_buf, tx_size);
+ c->tx_busy = 1;
+}
+
+static void ccid_send_data_block(struct ccid *c) {
+ ccid_send_data_block_internal (c, 0, 0);
+}
+
+static void ccid_send_data_block_time_extension(struct ccid *c) {
+ ccid_send_data_block_internal (c, CCID_CMD_STATUS_TIMEEXT, c->ccid_state == CCID_STATE_EXECUTE? 1: 0xff);
+}
+
+static void ccid_send_data_block_0x9000(struct ccid *c) {
+ uint8_t p[CCID_MSG_HEADER_SIZE+2];
+ size_t len = 2;
+
+ p[0] = CCID_DATA_BLOCK_RET;
+ p[1] = len & 0xFF;
+ p[2] = (len >> 8)& 0xFF;
+ p[3] = (len >> 16)& 0xFF;
+ p[4] = (len >> 24)& 0xFF;
+ p[5] = 0x00; /* Slot */
+ p[CCID_MSG_SEQ_OFFSET] = c->a->seq;
+ p[CCID_MSG_STATUS_OFFSET] = 0;
+ p[CCID_MSG_ERROR_OFFSET] = 0;
+ p[CCID_MSG_CHAIN_OFFSET] = 0;
+ p[CCID_MSG_CHAIN_OFFSET+1] = 0x90;
+ p[CCID_MSG_CHAIN_OFFSET+2] = 0x00;
+
+ memcpy (endp1_tx_buf, p, CCID_MSG_HEADER_SIZE + len);
+
+ c->epi->buf = NULL;
+ c->epi->tx_done = 1;
+ usb_tx_enable (endp1_tx_buf, CCID_MSG_HEADER_SIZE + len);
+ c->tx_busy = 1;
+}
+
+static void ccid_send_data_block_gr(struct ccid *c, size_t chunk_len) {
+ int tx_size = USB_LL_BUF_SIZE;
+ uint8_t p[CCID_MSG_HEADER_SIZE];
+ size_t len = chunk_len + 2;
+
+ p[0] = CCID_DATA_BLOCK_RET;
+ p[1] = len & 0xFF;
+ p[2] = (len >> 8)& 0xFF;
+ p[3] = (len >> 16)& 0xFF;
+ p[4] = (len >> 24)& 0xFF;
+ p[5] = 0x00; /* Slot */
+ p[CCID_MSG_SEQ_OFFSET] = c->a->seq;
+ p[CCID_MSG_STATUS_OFFSET] = 0;
+ p[CCID_MSG_ERROR_OFFSET] = 0;
+ p[CCID_MSG_CHAIN_OFFSET] = 0;
+
+ memcpy (endp1_tx_buf, p, CCID_MSG_HEADER_SIZE);
+
+ set_sw1sw2 (c, chunk_len);
+
+ if (chunk_len <= USB_LL_BUF_SIZE - CCID_MSG_HEADER_SIZE) {
+ int size_for_sw;
+
+ if (chunk_len <= USB_LL_BUF_SIZE - CCID_MSG_HEADER_SIZE - 2)
+ size_for_sw = 2;
+ else if (chunk_len == USB_LL_BUF_SIZE - CCID_MSG_HEADER_SIZE - 1)
+ size_for_sw = 1;
+ else
+ size_for_sw = 0;
+
+ memcpy (endp1_tx_buf+CCID_MSG_HEADER_SIZE, c->p, chunk_len);
+
+ if (size_for_sw)
+ memcpy (endp1_tx_buf+CCID_MSG_HEADER_SIZE+chunk_len, c->sw1sw2, size_for_sw);
+
+ tx_size = CCID_MSG_HEADER_SIZE + chunk_len + size_for_sw;
+ if (size_for_sw == 2) {
+ c->epi->buf = NULL;
+ if (tx_size < USB_LL_BUF_SIZE)
+ c->epi->tx_done = 1;
+ /* Don't set epi->tx_done = 1, when it requires ZLP */
+ }
+ else {
+ c->epi->buf = c->sw1sw2 + size_for_sw;
+ c->epi->cnt = size_for_sw;
+ c->epi->buf_len = 2 - size_for_sw;
+ c->epi->next_buf = no_buf;
+ }
+ }
+ else {
+ memcpy (endp1_tx_buf+CCID_MSG_HEADER_SIZE, c->p, USB_LL_BUF_SIZE - CCID_MSG_HEADER_SIZE);
+
+ c->epi->buf = c->p + USB_LL_BUF_SIZE - CCID_MSG_HEADER_SIZE;
+ c->epi->cnt = 0;
+ c->epi->buf_len = chunk_len - (USB_LL_BUF_SIZE - CCID_MSG_HEADER_SIZE);
+ c->epi->next_buf = get_sw1sw2;
+ }
+
+ c->p += chunk_len;
+ c->len -= chunk_len;
+ usb_tx_enable (endp1_tx_buf, tx_size);
+ c->tx_busy = 1;
+}
+
+static void ccid_send_params(struct ccid *c) {
+ uint8_t p[CCID_MSG_HEADER_SIZE];
+ const uint8_t params[] = {
+ 0x11, /* bmFindexDindex */
+ 0x11, /* bmTCCKST1 */
+ 0xFE, /* bGuardTimeT1 */
+ 0x55, /* bmWaitingIntegersT1 */
+ 0x03, /* bClockStop */
+ 0xFE, /* bIFSC */
+ 0 /* bNadValue */
+ };
+
+ p[0] = CCID_PARAMS_RET;
+ p[1] = 0x07; /* Length = 0x00000007 */
+ p[2] = 0;
+ p[3] = 0;
+ p[4] = 0;
+ p[5] = 0x00; /* Slot */
+ p[CCID_MSG_SEQ_OFFSET] = c->ccid_header.seq;
+ p[CCID_MSG_STATUS_OFFSET] = 0;
+ p[CCID_MSG_ERROR_OFFSET] = 0;
+ p[CCID_MSG_CHAIN_OFFSET] = 0x01; /* ProtocolNum: T=1 */
+
+ memcpy (endp1_tx_buf, p, CCID_MSG_HEADER_SIZE);
+ memcpy (endp1_tx_buf+CCID_MSG_HEADER_SIZE, params, sizeof params);
+
+ /* This is a single packet Bulk-IN transaction */
+ c->epi->buf = NULL;
+ c->epi->tx_done = 1;
+ usb_tx_enable (endp1_tx_buf, CCID_MSG_HEADER_SIZE + sizeof params);
+ c->tx_busy = 1;
+}
+
+static void ccid_error(struct ccid *c, int offset) {
+ uint8_t ccid_reply[CCID_MSG_HEADER_SIZE];
+
+ ccid_reply[0] = CCID_SLOT_STATUS_RET; /* Any value should be OK */
+ ccid_reply[1] = 0x00;
+ ccid_reply[2] = 0x00;
+ ccid_reply[3] = 0x00;
+ ccid_reply[4] = 0x00;
+ ccid_reply[5] = 0x00; /* Slot */
+ ccid_reply[CCID_MSG_SEQ_OFFSET] = c->ccid_header.seq;
+ if (c->ccid_state == CCID_STATE_NOCARD)
+ ccid_reply[CCID_MSG_STATUS_OFFSET] = 2; /* 2: No ICC present */
+ else if (c->ccid_state == CCID_STATE_START)
+ /* 1: ICC present but not activated */
+ ccid_reply[CCID_MSG_STATUS_OFFSET] = 1;
+ else
+ ccid_reply[CCID_MSG_STATUS_OFFSET] = 0; /* An ICC is present and active */
+ ccid_reply[CCID_MSG_STATUS_OFFSET] |= CCID_CMD_STATUS_ERROR; /* Failed */
+ ccid_reply[CCID_MSG_ERROR_OFFSET] = offset;
+ ccid_reply[CCID_MSG_CHAIN_OFFSET] = 0x00;
+
+ /* This is a single packet Bulk-IN transaction */
+ c->epi->buf = NULL;
+ c->epi->tx_done = 1;
+ memcpy (endp1_tx_buf, ccid_reply, CCID_MSG_HEADER_SIZE);
+ usb_tx_enable (endp1_tx_buf, CCID_MSG_HEADER_SIZE);
+ c->tx_busy = 1;
+}
+
+#define INS_GET_RESPONSE 0xc0
+
+static enum ccid_state ccid_handle_data(struct ccid *c)
+{
+ enum ccid_state next_state = c->ccid_state;
+
+ TU_LOG3("---- CCID STATE %d,msg_type %x,start %d\r\n",c->ccid_state,c->ccid_header.msg_type,CCID_STATE_START);
+ if (c->err != 0) {
+ ccid_reset(c);
+ ccid_error(c, CCID_OFFSET_DATA_LEN);
+ return next_state;
+ }
+ switch (c->ccid_state) {
+ case CCID_STATE_NOCARD:
+ if (c->ccid_header.msg_type == CCID_SLOT_STATUS)
+ ccid_send_status(c);
+ else {
+ DEBUG_INFO ("ERR00\r\n");
+ ccid_error(c, CCID_OFFSET_CMD_NOT_SUPPORTED);
+ }
+ break;
+ case CCID_STATE_START:
+ if (c->ccid_header.msg_type == CCID_POWER_ON) {
+ ccid_reset(c);
+ next_state = ccid_power_on(c);
+ }
+ else if (c->ccid_header.msg_type == CCID_POWER_OFF) {
+ ccid_reset(c);
+ next_state = ccid_power_off(c);
+ }
+ else if (c->ccid_header.msg_type == CCID_SLOT_STATUS)
+ ccid_send_status (c);
+ else {
+ DEBUG_INFO("ERR01\r\n");
+ ccid_error(c, CCID_OFFSET_CMD_NOT_SUPPORTED);
+ }
+ break;
+ case CCID_STATE_WAIT:
+ if (c->ccid_header.msg_type == CCID_POWER_ON) {
+ /* Not in the spec., but pcscd/libccid */
+ ccid_reset(c);
+ next_state = ccid_power_on(c);
+ }
+ else if (c->ccid_header.msg_type == CCID_POWER_OFF) {
+ ccid_reset(c);
+ next_state = ccid_power_off(c);
+ }
+ else if (c->ccid_header.msg_type == CCID_SLOT_STATUS)
+ ccid_send_status(c);
+ else if (c->ccid_header.msg_type == CCID_XFR_BLOCK) {
+ if (c->ccid_header.param == 0) {
+ if ((c->a->cmd_apdu_head[0] & 0x10) == 0) {
+ if (c->state == APDU_STATE_COMMAND_CHAINING) { /* command chaining finished */
+ c->p += c->a->cmd_apdu_head[4];
+ c->a->cmd_apdu_head[4] = 0;
+ DEBUG_INFO ("CMD chaning finished.\r\n");
+ }
+
+ if (c->a->cmd_apdu_head[1] == INS_GET_RESPONSE && c->state == APDU_STATE_RESULT_GET_RESPONSE) {
+ size_t len = c->a->expected_res_size;
+
+ if (c->len <= c->a->expected_res_size)
+ len = c->len;
+
+ ccid_send_data_block_gr (c, len);
+ if (c->len == 0)
+ c->state = APDU_STATE_RESULT;
+ c->ccid_state = CCID_STATE_WAIT;
+ DEBUG_INFO ("GET Response.\r\n");
+ }
+ else { /* Give this message to GPG thread */
+ c->state = APDU_STATE_COMMAND_RECEIVED;
+
+ c->a->sw = 0x9000;
+ c->a->res_apdu_data_len = 0;
+ c->a->res_apdu_data = &ccid_buffer[5];
+
+ uint32_t flag = EV_CMD_AVAILABLE;
+ queue_try_add(&c->card_comm, &flag);
+
+ next_state = CCID_STATE_EXECUTE;
+ }
+ }
+ else {
+ if (c->state == APDU_STATE_WAIT_COMMAND) { /* command chaining is started */
+ c->a->cmd_apdu_head[0] &= ~0x10;
+ memcpy (c->chained_cls_ins_p1_p2, c->a->cmd_apdu_head, 4);
+ c->state = APDU_STATE_COMMAND_CHAINING;
+ }
+
+ c->p += c->a->cmd_apdu_head[4];
+ c->len -= c->a->cmd_apdu_head[4];
+ ccid_send_data_block_0x9000 (c);
+ DEBUG_INFO ("CMD chaning...\r\n");
+ }
+ }
+ else { /* ICC block chaining is not supported. */
+ DEBUG_INFO ("ERR02\r\n");
+ ccid_error (c, CCID_OFFSET_PARAM);
+ }
+ }
+ else if (c->ccid_header.msg_type == CCID_SET_PARAMS || c->ccid_header.msg_type == CCID_GET_PARAMS || c->ccid_header.msg_type == CCID_RESET_PARAMS)
+ ccid_send_params(c);
+ else {
+ DEBUG_INFO ("ERR03\r\n");
+ DEBUG_BYTE (c->ccid_header.msg_type);
+ ccid_error (c, CCID_OFFSET_CMD_NOT_SUPPORTED);
+ }
+ break;
+ case CCID_STATE_EXECUTE:
+ case CCID_STATE_ACK_REQUIRED_0:
+ case CCID_STATE_ACK_REQUIRED_1:
+ if (c->ccid_header.msg_type == CCID_POWER_OFF)
+ next_state = ccid_power_off (c);
+ else if (c->ccid_header.msg_type == CCID_SLOT_STATUS)
+ ccid_send_status (c);
+ else {
+ DEBUG_INFO ("ERR04\r\n");
+ DEBUG_BYTE (c->ccid_header.msg_type);
+ ccid_error (c, CCID_OFFSET_CMD_NOT_SUPPORTED);
+ }
+ break;
+ default:
+ next_state = CCID_STATE_START;
+ DEBUG_INFO ("ERR10\r\n");
+ break;
+ }
+
+ return next_state;
+}
+
+static enum ccid_state ccid_handle_timeout(struct ccid *c) {
+ enum ccid_state next_state = c->ccid_state;
+ switch (c->ccid_state)
+ {
+ case CCID_STATE_EXECUTE:
+ case CCID_STATE_ACK_REQUIRED_0:
+ case CCID_STATE_ACK_REQUIRED_1:
+ ccid_send_data_block_time_extension(c);
+ break;
+ default:
+ break;
+ }
+
+ return next_state;
+}
+
+
+static void notify_icc(struct ep_out *epo) {
+ struct ccid *c = (struct ccid *)epo->priv;
+ c->err = epo->err;
+ uint32_t val = EV_RX_DATA_READY;
+ queue_try_add(&c->ccid_comm, &val);
+}
+
+static int end_ccid_rx(struct ep_out *epo, size_t orig_len) {
+ (void)orig_len;
+ if (epo->cnt < sizeof (struct ccid_header))
+ /* short packet, just ignore */
+ return 1;
+
+ /* icc message with no abdata */
+ return 0;
+}
+
+static int end_abdata(struct ep_out *epo, size_t orig_len) {
+ struct ccid *c = (struct ccid *)epo->priv;
+ size_t len = epo->cnt;
+
+ if (orig_len == USB_LL_BUF_SIZE && len < c->ccid_header.data_len)
+ /* more packet comes */
+ return 1;
+
+ if (len != c->ccid_header.data_len)
+ epo->err = 1;
+
+ return 0;
+}
+
+static int end_cmd_apdu_head(struct ep_out *epo, size_t orig_len) {
+ struct ccid *c = (struct ccid *)epo->priv;
+
+ (void)orig_len;
+
+ if (epo->cnt < 4 || epo->cnt != c->ccid_header.data_len) {
+ epo->err = 1;
+ return 0;
+ }
+
+ if ((c->state == APDU_STATE_COMMAND_CHAINING)
+ && (c->chained_cls_ins_p1_p2[0] != (c->a->cmd_apdu_head[0] & ~0x10)
+ || c->chained_cls_ins_p1_p2[1] != c->a->cmd_apdu_head[1]
+ || c->chained_cls_ins_p1_p2[2] != c->a->cmd_apdu_head[2]
+ || c->chained_cls_ins_p1_p2[3] != c->a->cmd_apdu_head[3]))
+ /*
+ * Handling exceptional request.
+ *
+ * Host stops sending command APDU using command chaining,
+ * and start another command APDU.
+ *
+ * Discard old one, and start handling new one.
+ */
+ {
+ c->state = APDU_STATE_WAIT_COMMAND;
+ c->p = c->a->cmd_apdu_data;
+ c->len = MAX_CMD_APDU_DATA_SIZE;
+ }
+
+ if (epo->cnt == 4)
+ /* No Lc and Le */
+ c->a->expected_res_size = 0;
+ else if (epo->cnt == 5) {
+ /* No Lc but Le */
+ c->a->expected_res_size = c->a->cmd_apdu_head[4];
+ if (c->a->expected_res_size == 0)
+ c->a->expected_res_size = 256;
+ c->a->cmd_apdu_head[4] = 0;
+ }
+ else if (epo->cnt == 9) { //extended
+ c->a->expected_res_size = (c->a->cmd_apdu_head[7] << 8) | c->a->cmd_apdu_head[8];
+ if (c->a->expected_res_size == 0)
+ c->a->expected_res_size = 65536;
+ }
+
+ c->a->cmd_apdu_data_len = 0;
+ return 0;
+}
+
+static int end_nomore_data(struct ep_out *epo, size_t orig_len) {
+ (void)epo;
+ if (orig_len == USB_LL_BUF_SIZE)
+ return 1;
+ else
+ return 0;
+}
+
+static int end_cmd_apdu_data(struct ep_out *epo, size_t orig_len) {
+ struct ccid *c = (struct ccid *)epo->priv;
+ size_t len = epo->cnt;
+
+ if (orig_len == USB_LL_BUF_SIZE && CMD_APDU_HEAD_SIZE + len < c->ccid_header.data_len)
+ /* more packet comes */
+ return 1;
+
+ if (CMD_APDU_HEAD_SIZE + len != c->ccid_header.data_len)
+ goto error;
+ //len is the length after lc (whole APDU = len+5)
+ if (c->a->cmd_apdu_head[4] == 0 && len >= 2) { //extended
+ if (len == 2) {
+ c->a->expected_res_size = (c->a->cmd_apdu_head[5] << 8) | c->a->cmd_apdu_head[6];
+ if (c->a->expected_res_size == 0)
+ c->a->expected_res_size = 0xffff+1;
+ }
+ else {
+ c->a->cmd_apdu_data_len = (c->a->cmd_apdu_data[0] << 8) | c->a->cmd_apdu_data[1];
+ len -= 2;
+ if (len < c->a->cmd_apdu_data_len)
+ goto error;
+ c->a->cmd_apdu_data += 2;
+ if (len == c->a->cmd_apdu_data_len) //no LE
+ c->a->expected_res_size = 0;
+ else {
+ if (len - c->a->cmd_apdu_data_len < 2)
+ goto error;
+ c->a->expected_res_size = (c->a->cmd_apdu_data[c->a->cmd_apdu_data_len] << 8) | c->a->cmd_apdu_data[c->a->cmd_apdu_data_len+1];
+ if (c->a->expected_res_size == 0)
+ c->a->expected_res_size = 0xffff+1;
+ }
+ }
+ }
+ else {
+
+ if (len == c->a->cmd_apdu_head[4])
+ /* No Le field*/
+ c->a->expected_res_size = 0;
+ else if (len == (size_t)c->a->cmd_apdu_head[4] + 1)
+ {
+ /* it has Le field*/
+ c->a->expected_res_size = epo->buf[-1];
+ if (c->a->expected_res_size == 0)
+ c->a->expected_res_size = 256;
+ len--;
+ }
+ else
+ {
+ error:
+ DEBUG_INFO("APDU header size error");
+ epo->err = 1;
+ return 0;
+ }
+
+ c->a->cmd_apdu_data_len += len;
+ }
+ return 0;
+}
+
+static void nomore_data(struct ep_out *epo, size_t len) {
+ (void)len;
+ epo->err = 1;
+ epo->end_rx = end_nomore_data;
+ epo->buf = NULL;
+ epo->buf_len = 0;
+ epo->cnt = 0;
+ epo->next_buf = nomore_data;
+ epo->ready = 0;
+}
+
+static void ccid_cmd_apdu_data(struct ep_out *epo, size_t len) {
+ struct ccid *c = (struct ccid *)epo->priv;
+
+ (void)len;
+ if (c->state == APDU_STATE_RESULT_GET_RESPONSE && c->a->cmd_apdu_head[1] != INS_GET_RESPONSE) {
+ /*
+ * Handling exceptional request.
+ *
+ * Host didn't finish receiving the whole response APDU by GET RESPONSE,
+ * but initiates another command.
+ */
+
+ c->state = APDU_STATE_WAIT_COMMAND;
+ c->p = c->a->cmd_apdu_data;
+ c->len = MAX_CMD_APDU_DATA_SIZE;
+ }
+ else if (c->state == APDU_STATE_COMMAND_CHAINING) {
+ if (c->chained_cls_ins_p1_p2[0] != (c->a->cmd_apdu_head[0] & ~0x10)
+ || c->chained_cls_ins_p1_p2[1] != c->a->cmd_apdu_head[1]
+ || c->chained_cls_ins_p1_p2[2] != c->a->cmd_apdu_head[2]
+ || c->chained_cls_ins_p1_p2[3] != c->a->cmd_apdu_head[3])
+ /*
+ * Handling exceptional request.
+ *
+ * Host stops sending command APDU using command chaining,
+ * and start another command APDU.
+ *
+ * Discard old one, and start handling new one.
+ */
+ {
+ c->state = APDU_STATE_WAIT_COMMAND;
+ c->p = c->a->cmd_apdu_data;
+ c->len = MAX_CMD_APDU_DATA_SIZE;
+ c->a->cmd_apdu_data_len = 0;
+ }
+ }
+
+ epo->end_rx = end_cmd_apdu_data;
+ epo->buf = c->p;
+ epo->buf_len = c->len;
+ epo->cnt = 0;
+ epo->next_buf = nomore_data;
+}
+
+static void ccid_abdata(struct ep_out *epo, size_t len) {
+ struct ccid *c = (struct ccid *)epo->priv;
+
+ (void)len;
+ c->a->seq = c->ccid_header.seq;
+ if (c->ccid_header.msg_type == CCID_XFR_BLOCK) {
+ c->a->seq = c->ccid_header.seq;
+ epo->end_rx = end_cmd_apdu_head;
+ epo->buf = c->a->cmd_apdu_head;
+ epo->buf_len = 5;
+ epo->cnt = 0;
+ epo->next_buf = ccid_cmd_apdu_data;
+ }
+ else {
+ epo->end_rx = end_abdata;
+ epo->buf = c->p;
+ epo->buf_len = c->len;
+ epo->cnt = 0;
+ epo->next_buf = nomore_data;
+ }
+}
+
+static void ccid_prepare_receive(struct ccid *c) {
+ c->epo->err = 0;
+ c->epo->buf = (uint8_t *)&c->ccid_header;
+ c->epo->buf_len = sizeof (struct ccid_header);
+ c->epo->cnt = 0;
+ c->epo->next_buf = ccid_abdata;
+ c->epo->end_rx = end_ccid_rx;
+ c->epo->ready = 1;
+}
+
+static void ccid_rx_ready(uint16_t len) {
+ /*
+ * If we support multiple CCID interfaces, we select endpoint object
+ * by EP_NUM. Because it has only single CCID interface now, it's
+ * hard-coded, here.
+ */
+ struct ep_out *epo = &endpoint_out;
+ int offset = 0;
+ int cont;
+ size_t orig_len = len;
+ while (epo->err == 0) {
+ if (len == 0)
+ break;
+ else if (len <= epo->buf_len) {
+ memcpy (epo->buf, endp1_rx_buf + offset, len);
+ epo->buf += len;
+ epo->cnt += len;
+ epo->buf_len -= len;
+ break;
+ }
+ else /* len > buf_len */ {
+ memcpy (epo->buf, endp1_rx_buf + offset, epo->buf_len);
+ len -= epo->buf_len;
+ offset += epo->buf_len;
+ epo->next_buf (epo, len); /* Update epo->buf, cnt, buf_len */
+ }
+ }
+
+ /*
+ * ORIG_LEN to distingush ZLP and the end of transaction
+ * (ORIG_LEN != USB_LL_BUF_SIZE)
+ */
+ cont = epo->end_rx (epo, orig_len);
+
+ if (cont == 0)
+ notify_icc (epo);
+ else
+ epo->ready = 1;
+}
+
+static void notify_tx(struct ep_in *epi) {
+ struct ccid *c = (struct ccid *)epi->priv;
+
+ /* The sequence of Bulk-IN transactions finished */
+ uint32_t flag = EV_TX_FINISHED;
+ queue_try_add(&c->ccid_comm, &flag);
+ c->tx_busy = 0;
+}
+
+static void ccid_tx_done () {
+ /*
+ * If we support multiple CCID interfaces, we select endpoint object
+ * by EP_NUM. Because it has only single CCID interface now, it's
+ * hard-coded, here.
+ */
+ struct ep_in *epi = &endpoint_in;
+ if (epi->buf == NULL){
+ if (epi->tx_done)
+ notify_tx (epi);
+ else {
+ epi->tx_done = 1;
+ usb_tx_enable (endp1_tx_buf, 0);
+ }
+ }
+ else {
+ int tx_size = 0;
+ size_t remain = USB_LL_BUF_SIZE;
+ int offset = 0;
+
+ while (epi->buf) {
+ if (epi->buf_len < remain) {
+ memcpy (endp1_tx_buf+offset, epi->buf, epi->buf_len);
+ offset += epi->buf_len;
+ remain -= epi->buf_len;
+ tx_size += epi->buf_len;
+ epi->next_buf (epi, remain); /* Update epi->buf, cnt, buf_len */
+ }
+ else {
+ memcpy (endp1_tx_buf+offset, epi->buf, remain);
+ epi->buf += remain;
+ epi->cnt += remain;
+ epi->buf_len -= remain;
+ tx_size += remain;
+ break;
+ }
+ }
+ if (tx_size < USB_LL_BUF_SIZE)
+ epi->tx_done = 1;
+
+ usb_tx_enable (endp1_tx_buf, tx_size);
+ }
+}
+
+static int usb_event_handle(struct ccid *c) {
+ TU_LOG3("!!! tx %d, vendor %d, cfg %d, rx %d\r\n",c->tx_busy,tud_vendor_n_write_available(0),CFG_TUD_VENDOR_TX_BUFSIZE,tud_vendor_available());
+ if (c->tx_busy == 1 && tud_vendor_n_write_available(0) == CFG_TUD_VENDOR_TX_BUFSIZE) {
+ ccid_tx_done ();
+ }
+ if (tud_vendor_available() && c->epo->ready) {
+ uint32_t count = tud_vendor_read(endp1_rx_buf, sizeof(endp1_rx_buf));
+ if (endp1_rx_buf[0] != 0x65)
+ DEBUG_PAYLOAD(endp1_rx_buf, count);
+ //DEBUG_PAYLOAD(endp1_rx_buf, count);
+ ccid_rx_ready(count);
+ }
+ return 0;
+}
+
+uint32_t timeout = USB_CCID_TIMEOUT;
+static uint32_t prev_millis = 0;
+
+void prepare_ccid() {
+ struct ep_in *epi = &endpoint_in;
+ struct ep_out *epo = &endpoint_out;
+ struct ccid *c = &ccid;
+ struct apdu *a = &apdu;
+
+ epi_init (epi, 1, c);
+ epo_init (epo, 2, c);
+
+ apdu_init(a);
+ ccid_init (c, epi, epo, a);
+}
+
+int process_apdu() {
+ led_set_blink(BLINK_PROCESSING);
+ if (!current_app) {
+ if (INS(apdu) == 0xA4 && P1(apdu) == 0x04 && (P2(apdu) == 0x00 || P2(apdu) == 0x4)) { //select by AID
+ for (int a = 0; a < num_apps; a++) {
+ if ((current_app = apps[a].select_aid(&apps[a]))) {
+ return set_res_sw(0x90,0x00);
+ }
+ }
+ }
+ return set_res_sw(0x6a, 0x82);
+ }
+ if (current_app->process_apdu)
+ return current_app->process_apdu();
+ return set_res_sw (0x6D, 0x00);
+}
+
+uint16_t set_res_sw(uint8_t sw1, uint8_t sw2) {
+ apdu.sw = (sw1 << 8) | sw2;
+ return make_uint16_t(sw1, sw2);
+}
+
+static void card_init(void) {
+ //gpg_data_scan (flash_do_start, flash_do_end);
+ low_flash_init_core1();
+}
+
+void card_thread() {
+ ccid_comm = (queue_t *)multicore_fifo_pop_blocking();
+ card_comm = (queue_t *)multicore_fifo_pop_blocking();
+
+ card_init ();
+
+ while (1) {
+ uint32_t m;
+ queue_remove_blocking(card_comm, &m);
+
+ if (m == EV_VERIFY_CMD_AVAILABLE || m == EV_MODIFY_CMD_AVAILABLE)
+ {
+ set_res_sw (0x6f, 0x00);
+ goto done;
+ }
+ else if (m == EV_EXIT) {
+ if (current_app && current_app->unload)
+ current_app->unload();
+ break;
+ }
+
+ process_apdu();
+
+ done:;
+ uint32_t flag = EV_EXEC_FINISHED;
+ queue_add_blocking(ccid_comm, &flag);
+ }
+
+ if (current_app && current_app->unload)
+ current_app->unload();
+}
+
+
+void ccid_task(void) {
+ struct ccid *c = &ccid;
+ if (tud_vendor_mounted()) {
+ // connected and there are data available
+ if ((c->epo->ready && tud_vendor_available()) || (tud_vendor_n_write_available(0) == CFG_TUD_VENDOR_TX_BUFSIZE && c->tx_busy == 1)) {
+ if (usb_event_handle (c) != 0) {
+ if (c->application) {
+ uint32_t flag = EV_EXIT;
+ queue_try_add(&c->ccid_comm, &flag);
+ c->application = 0;
+ }
+ prepare_ccid();
+ return;
+ }
+ }
+ if (timeout == 0) {
+ timeout = USB_CCID_TIMEOUT;
+ c->timeout_cnt++;
+ }
+ uint32_t m = 0x0;
+ bool has_m = queue_try_remove(&c->ccid_comm, &m);
+ if (m != 0)
+ TU_LOG3("\r\n ------ M = %d\r\n",m);
+ if (has_m) {
+ if (m == EV_CARD_CHANGE) {
+ if (c->ccid_state == CCID_STATE_NOCARD)
+ /* Inserted! */
+ c->ccid_state = CCID_STATE_START;
+ else { /* Removed! */
+ if (c->application) {
+ uint32_t flag = EV_EXIT;
+ queue_try_add(&c->card_comm, &flag);
+ c->application = 0;
+ }
+ c->ccid_state = CCID_STATE_NOCARD;
+ }
+ //ccid_notify_slot_change (c);
+ }
+ else if (m == EV_RX_DATA_READY) {
+ c->ccid_state = ccid_handle_data(c);
+ timeout = 0;
+ c->timeout_cnt = 0;
+ }
+ else if (m == EV_EXEC_FINISHED) {
+ if (c->ccid_state == CCID_STATE_EXECUTE) {
+ exec_done:
+ if (c->a->sw == GPG_THREAD_TERMINATED) {
+ c->sw1sw2[0] = 0x90;
+ c->sw1sw2[1] = 0x00;
+ c->state = APDU_STATE_RESULT;
+ ccid_send_data_block(c);
+ c->ccid_state = CCID_STATE_EXITED;
+ c->application = 0;
+ return;
+ }
+
+ c->a->cmd_apdu_data_len = 0;
+ c->sw1sw2[0] = c->a->sw >> 8;
+ c->sw1sw2[1] = c->a->sw & 0xff;
+ if (c->a->res_apdu_data_len <= c->a->expected_res_size) {
+ c->state = APDU_STATE_RESULT;
+ ccid_send_data_block(c);
+ c->ccid_state = CCID_STATE_WAIT;
+ }
+ else {
+ c->state = APDU_STATE_RESULT_GET_RESPONSE;
+ c->p = c->a->res_apdu_data;
+ c->len = c->a->res_apdu_data_len;
+ ccid_send_data_block_gr(c, c->a->expected_res_size);
+ c->ccid_state = CCID_STATE_WAIT;
+ }
+ }
+ else {
+ DEBUG_INFO ("ERR05\r\n");
+ }
+ led_set_blink(BLINK_MOUNTED);
+ }
+ else if (m == EV_TX_FINISHED){
+ if (c->state == APDU_STATE_RESULT)
+ ccid_reset(c);
+ else
+ c->tx_busy = 0;
+ if (c->state == APDU_STATE_WAIT_COMMAND || c->state == APDU_STATE_COMMAND_CHAINING || c->state == APDU_STATE_RESULT_GET_RESPONSE)
+ ccid_prepare_receive(c);
+ }
+ else if (m == EV_PRESS_BUTTON) {
+ wait_button();
+ uint32_t flag = EV_BUTTON_PRESSED;
+ queue_try_add(&c->card_comm, &flag);
+ }
+ }
+ else {
+ timeout -= MIN(board_millis()-prev_millis,timeout);
+ if (timeout == 0) {
+ if (c->timeout_cnt == 7 && c->ccid_state == CCID_STATE_ACK_REQUIRED_1) {
+ c->a->sw = GPG_ACK_TIMEOUT;
+ c->a->res_apdu_data_len = 0;
+ c->a->sw = GPG_ACK_TIMEOUT;
+ c->a->res_apdu_data_len = 0;
+
+ goto exec_done;
+ }
+ else
+ c->ccid_state = ccid_handle_timeout(c);
+ }
+ }
+ }
+}
+
+void tud_mount_cb() {
+ ccid_prepare_receive (&ccid);
+}
+
+void led_blinking_task() {
+#ifdef PICO_DEFAULT_LED_PIN
+ static uint32_t start_ms = 0;
+ static uint8_t led_state = false;
+ static uint8_t led_color = PICO_DEFAULT_LED_PIN;
+#ifdef PICO_DEFAULT_LED_PIN_INVERTED
+ uint32_t interval = !led_state ? blink_interval_ms & 0xffff : blink_interval_ms >> 16;
+#else
+ uint32_t interval = led_state ? blink_interval_ms & 0xffff : blink_interval_ms >> 16;
+#endif
+
+
+ // Blink every interval ms
+ if (board_millis() - start_ms < interval)
+ return; // not enough time
+ start_ms += interval;
+
+ gpio_put(led_color, led_state);
+ led_state ^= 1; // toggle
+#endif
+}
+
+void led_off_all() {
+#ifdef PIMORONI_TINY2040
+ gpio_put(TINY2040_LED_R_PIN, 1);
+ gpio_put(TINY2040_LED_G_PIN, 1);
+ gpio_put(TINY2040_LED_B_PIN, 1);
+#else
+#ifdef PICO_DEFAULT_LED_PIN
+ gpio_put(PICO_DEFAULT_LED_PIN, 0);
+#endif
+#endif
+}
+
+void init_rtc() {
+
+ rtc_init();
+ datetime_t dt = {
+ .year = 2020,
+ .month = 1,
+ .day = 1,
+ .dotw = 3, // 0 is Sunday, so 5 is Friday
+ .hour = 00,
+ .min = 00,
+ .sec = 00
+ };
+ rtc_set_datetime(&dt);
+}
+
+extern void neug_task();
+
+pico_unique_board_id_t unique_id;
+
+void execute_tasks() {
+ prev_millis = board_millis();
+ ccid_task();
+ tud_task(); // tinyusb device task
+ led_blinking_task();
+}
+
+int main(void) {
+ struct apdu *a = &apdu;
+ struct ccid *c = &ccid;
+
+ board_init();
+
+#ifdef PIMORONI_TINY2040
+ gpio_init(TINY2040_LED_R_PIN);
+ gpio_set_dir(TINY2040_LED_R_PIN, GPIO_OUT);
+ gpio_init(TINY2040_LED_G_PIN);
+ gpio_set_dir(TINY2040_LED_G_PIN, GPIO_OUT);
+ gpio_init(TINY2040_LED_B_PIN);
+ gpio_set_dir(TINY2040_LED_B_PIN, GPIO_OUT);
+#else
+#ifdef PICO_DEFAULT_LED_PIN
+ gpio_init(PICO_DEFAULT_LED_PIN);
+ gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
+#endif
+#endif
+
+
+ led_off_all();
+
+ tusb_init();
+
+ prepare_ccid();
+
+ random_init();
+
+ low_flash_init();
+
+ init_rtc();
+
+ while (1) {
+ execute_tasks();
+ neug_task();
+ do_flash();
+ }
+
+ return 0;
+}
\ No newline at end of file
diff --git a/src/ccid/ccid2040.h b/src/ccid/ccid2040.h
new file mode 100644
index 0000000..194454b
--- /dev/null
+++ b/src/ccid/ccid2040.h
@@ -0,0 +1,250 @@
+/*
+ * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid).
+ * Copyright (c) 2022 Pol Henarejos.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef _CCID2040_H_
+#define _CCID2040_H_
+
+#include "ccid.h"
+#include "tusb.h"
+#include "file.h"
+#include "pico/unique_id.h"
+#include "pico/util/queue.h"
+
+#define USB_REQ_CCID 0xA1
+
+typedef struct app {
+ const uint8_t *aid;
+ int (*process_apdu)();
+ struct app* (*select_aid)();
+ int (*unload)();
+} app_t;
+
+extern int register_app(app_t * (*)());
+
+extern const uint8_t historical_bytes[];
+
+#define DEBUG_PAYLOAD(p,s) { \
+ printf("Payload %s (%d bytes):\r\n", #p,s);\
+ for (int i = 0; i < s; i += 16) {\
+ printf("%07Xh : ",i+p);\
+ for (int j = 0; j < 16; j++) {\
+ if (j < s-i) printf("%02X ",(p)[i+j]);\
+ else printf(" ");\
+ if (j == 7) printf(" ");\
+ } printf(": "); \
+ for (int j = 0; j < MIN(16,s-i); j++) {\
+ printf("%c",(p)[i+j] == 0x0a || (p)[i+j] == 0x0d ? '\\' : (p)[i+j]);\
+ if (j == 7) printf(" ");\
+ }\
+ printf("\r\n");\
+ } printf("\r\n"); \
+ }
+
+struct apdu {
+ uint8_t seq;
+
+ /* command APDU */
+ uint8_t *cmd_apdu_head; /* CLS INS P1 P2 [ internal Lc ] */
+ uint8_t *cmd_apdu_data;
+ size_t cmd_apdu_data_len; /* Nc, calculated by Lc field */
+ size_t expected_res_size; /* Ne, calculated by Le field */
+
+ /* response APDU */
+ uint16_t sw;
+ uint16_t res_apdu_data_len;
+ uint8_t *res_apdu_data;
+};
+
+#define MAX_CMD_APDU_DATA_SIZE (24+4+512*4)
+#define MAX_RES_APDU_DATA_SIZE (5+9+512*4)
+#define CCID_MSG_HEADER_SIZE 10
+#define USB_LL_BUF_SIZE 64
+
+/* CCID thread */
+#define EV_CARD_CHANGE 1
+#define EV_TX_FINISHED 2 /* CCID Tx finished */
+#define EV_EXEC_ACK_REQUIRED 4 /* OpenPGPcard Execution ACK required */
+#define EV_EXEC_FINISHED 8 /* OpenPGPcard Execution finished */
+#define EV_RX_DATA_READY 16 /* USB Rx data available */
+#define EV_PRESS_BUTTON 32
+
+/* SC HSM thread */
+#define EV_MODIFY_CMD_AVAILABLE 1
+#define EV_VERIFY_CMD_AVAILABLE 2
+#define EV_CMD_AVAILABLE 4
+#define EV_EXIT 8
+#define EV_BUTTON_PRESSED 16
+
+//Variables set by core1
+extern queue_t *ccid_comm;
+extern queue_t *card_comm;
+
+enum ccid_state {
+ CCID_STATE_NOCARD, /* No card available */
+ CCID_STATE_START, /* Initial */
+ CCID_STATE_WAIT, /* Waiting APDU */
+
+ CCID_STATE_EXECUTE, /* Executing command */
+ CCID_STATE_ACK_REQUIRED_0, /* Ack required (executing)*/
+ CCID_STATE_ACK_REQUIRED_1, /* Waiting user's ACK (execution finished) */
+
+ CCID_STATE_EXITED, /* CCID Thread Terminated */
+ CCID_STATE_EXEC_REQUESTED, /* Exec requested */
+};
+
+#define CLA(a) a.cmd_apdu_head[0]
+#define INS(a) a.cmd_apdu_head[1]
+#define P1(a) a.cmd_apdu_head[2]
+#define P2(a) a.cmd_apdu_head[3]
+
+#define res_APDU apdu.res_apdu_data
+#define res_APDU_size apdu.res_apdu_data_len
+
+extern struct apdu apdu;
+
+uint16_t set_res_sw (uint8_t sw1, uint8_t sw2);
+
+
+static inline const uint16_t make_uint16_t(uint8_t b1, uint8_t b2) {
+ return (b1 << 8) | b2;
+}
+static inline const uint16_t get_uint16_t(const uint8_t *b, uint16_t offset) {
+ return make_uint16_t(b[offset], b[offset+1]);
+}
+static inline const void put_uint16_t(uint16_t n, uint8_t *b) {
+ *b++ = (n >> 8) & 0xff;
+ *b = n & 0xff;
+}
+
+
+#ifdef DEBUG
+void stdout_init (void);
+#define DEBUG_MORE 1
+/*
+ * Debug functions in debug.c
+ */
+void put_byte (uint8_t b);
+void put_byte_with_no_nl (uint8_t b);
+void put_short (uint16_t x);
+void put_word (uint32_t x);
+void put_int (uint32_t x);
+void put_string (const char *s);
+void put_binary (const char *s, int len);
+
+#define DEBUG_INFO(msg) put_string (msg)
+#define DEBUG_WORD(w) put_word (w)
+#define DEBUG_SHORT(h) put_short (h)
+#define DEBUG_BYTE(b) put_byte (b)
+#define DEBUG_BINARY(s,len) put_binary ((const char *)s,len)
+#else
+#define DEBUG_INFO(msg)
+#define DEBUG_WORD(w)
+#define DEBUG_SHORT(h)
+#define DEBUG_BYTE(b)
+#define DEBUG_BINARY(s,len)
+#endif
+
+extern int flash_write_data_to_file(file_t *file, const uint8_t *data, uint16_t len);
+extern void low_flash_available();
+extern int flash_clear_file(file_t *file);
+
+extern pico_unique_board_id_t unique_id;
+
+enum {
+ BLINK_NOT_MOUNTED = (250 << 16) | 250,
+ BLINK_MOUNTED = (250 << 16) | 250,
+ BLINK_SUSPENDED = (500 << 16) | 1000,
+ BLINK_PROCESSING = (50 << 16) | 50,
+
+ BLINK_ALWAYS_ON = UINT32_MAX,
+ BLINK_ALWAYS_OFF = 0
+};
+extern void led_set_blink(uint32_t mode);
+
+
+#define SW_BYTES_REMAINING_00() set_res_sw (0x61, 0x00)
+#define SW_WARNING_STATE_UNCHANGED() set_res_sw (0x62, 0x00)
+#define SW_WARNING_CORRUPTED() set_res_sw (0x62, 0x81)
+#define SW_WARNING_EOF() set_res_sw (0x62, 0x82)
+#define SW_WARNING_EF_DEACTIVATED() set_res_sw (0x62, 0x83)
+#define SW_WARNING_WRONG_FCI() set_res_sw (0x62, 0x84)
+#define SW_WARNING_EF_TERMINATED() set_res_sw (0x62, 0x85)
+
+#define SW_WARNING_NOINFO() set_res_sw (0x63, 0x00)
+#define SW_WARNING_FILLUP() set_res_sw (0x63, 0x81)
+
+#define SW_EXEC_ERROR() set_res_sw (0x64, 0x00)
+
+#define SW_MEMORY_FAILURE() set_res_sw (0x65, 0x81)
+
+#define SW_SECURE_MESSAGE_EXEC_ERROR() set_res_sw (0x66, 0x00)
+
+#define SW_WRONG_LENGTH() set_res_sw (0x67, 0x00)
+#define SW_WRONG_DATA() set_res_sw (0x67, 0x00)
+
+#define SW_LOGICAL_CHANNEL_NOT_SUPPORTED() set_res_sw (0x68, 0x81)
+#define SW_SECURE_MESSAGING_NOT_SUPPORTED() set_res_sw (0x68, 0x82)
+
+#define SW_COMMAND_INCOMPATIBLE() set_res_sw (0x69, 0x81)
+#define SW_SECURITY_STATUS_NOT_SATISFIED() set_res_sw (0x69, 0x82)
+#define SW_PIN_BLOCKED() set_res_sw (0x69, 0x83)
+#define SW_DATA_INVALID() set_res_sw (0x69, 0x84)
+#define SW_CONDITIONS_NOT_SATISFIED() set_res_sw (0x69, 0x85)
+#define SW_COMMAND_NOT_ALLOWED() set_res_sw (0x69, 0x86)
+#define SW_SECURE_MESSAGING_MISSING_DO() set_res_sw (0x69, 0x87)
+#define SW_SECURE_MESSAGING_INCORRECT_DO() set_res_sw (0x69, 0x88)
+#define SW_APPLET_SELECT_FAILED() set_res_sw (0x69, 0x99)
+
+#define SW_INCORRECT_PARAMS() set_res_sw (0x6A, 0x80)
+#define SW_FUNC_NOT_SUPPORTED() set_res_sw (0x6A, 0x81)
+#define SW_FILE_NOT_FOUND() set_res_sw (0x6A, 0x82)
+#define SW_RECORD_NOT_FOUND() set_res_sw (0x6A, 0x83)
+#define SW_FILE_FULL() set_res_sw (0x6A, 0x84)
+#define SW_WRONG_NE() set_res_sw (0x6A, 0x85)
+#define SW_INCORRECT_P1P2() set_res_sw (0x6A, 0x86)
+#define SW_WRONG_NC() set_res_sw (0x6A, 0x87)
+#define SW_REFERENCE_NOT_FOUND() set_res_sw (0x6A, 0x88)
+#define SW_FILE_EXISTS() set_res_sw (0x6A, 0x89)
+
+#define SW_WRONG_P1P2() set_res_sw (0x6B, 0x00)
+
+#define SW_CORRECT_LENGTH_00() set_res_sw (0x6C, 0x00)
+
+#define SW_INS_NOT_SUPPORTED() set_res_sw (0x6D, 0x00)
+
+#define SW_CLA_NOT_SUPPORTED() set_res_sw (0x6E, 0x00)
+
+#define SW_UNKNOWN() set_res_sw (0x6F, 0x00)
+
+#define SW_OK() set_res_sw (0x90, 0x00)
+
+#define CCID_OK 0
+#define CCID_ERR_NO_MEMORY -1000
+#define CCID_ERR_MEMORY_FATAL -1001
+#define CCID_ERR_NULL_PARAM -1002
+#define CCID_ERR_FILE_NOT_FOUND -1003
+#define CCID_ERR_BLOCKED -1004
+#define CCID_NO_LOGIN -1005
+#define CCID_EXEC_ERROR -1006
+#define CCID_WRONG_LENGTH -1007
+#define CCID_WRONG_DATA -1008
+#define CCID_WRONG_DKEK -1009
+#define CCID_WRONG_SIGNATURE -1010
+#define CCID_WRONG_PADDING -1011
+#define CCID_VERIFICATION_FAILED -1012
+
+#endif
\ No newline at end of file
diff --git a/src/ccid/version.h b/src/ccid/version.h
new file mode 100644
index 0000000..c031e22
--- /dev/null
+++ b/src/ccid/version.h
@@ -0,0 +1,27 @@
+/*
+ * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid).
+ * Copyright (c) 2022 Pol Henarejos.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef __VERSION_H_
+#define __VERSION_H_
+
+#define HSM_VERSION 0x010C
+
+#define HSM_VERSION_MAJOR ((HSM_VERSION >> 8) & 0xff)
+#define HSM_VERSION_MINOR (HSM_VERSION & 0xff)
+
+#endif
+
diff --git a/src/fs/file.c b/src/fs/file.c
new file mode 100644
index 0000000..49922f1
--- /dev/null
+++ b/src/fs/file.c
@@ -0,0 +1,294 @@
+/*
+ * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid).
+ * Copyright (c) 2022 Pol Henarejos.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "file.h"
+#include "tusb.h"
+#include "ccid2040.h"
+#include
+
+extern const uintptr_t end_data_pool;
+extern const uintptr_t start_data_pool;
+extern int flash_write_data_to_file(file_t *file, const uint8_t *data, uint16_t len);
+extern int flash_program_halfword (uintptr_t addr, uint16_t data);
+extern int flash_program_word (uintptr_t addr, uint32_t data);
+extern int flash_program_uintptr (uintptr_t addr, uintptr_t data);
+extern int flash_program_block(uintptr_t addr, const uint8_t *data, size_t len);
+extern uintptr_t flash_read_uintptr(uintptr_t addr);
+extern uint16_t flash_read_uint16(uintptr_t addr);
+extern uint8_t flash_read_uint8(uintptr_t addr);
+extern uint8_t *flash_read(uintptr_t addr);
+extern void low_flash_available();
+
+//puts FCI in the RAPDU
+void process_fci(const file_t *pe) {
+ uint8_t *p = res_APDU;
+ uint8_t buf[64];
+ res_APDU_size = 0;
+ res_APDU[res_APDU_size++] = 0x6f;
+ res_APDU[res_APDU_size++] = 0x00; //computed later
+
+ res_APDU[res_APDU_size++] = 0x81;
+ res_APDU[res_APDU_size++] = 2;
+ if (pe->data) {
+ if ((pe->type & FILE_DATA_FUNC) == FILE_DATA_FUNC) {
+ uint16_t len = ((int (*)(const file_t *, int))(pe->data))(pe, 0);
+ res_APDU[res_APDU_size++] = (len >> 8) & 0xff;
+ res_APDU[res_APDU_size++] = len & 0xff;
+ }
+ else {
+ res_APDU[res_APDU_size++] = pe->data[1];
+ res_APDU[res_APDU_size++] = pe->data[0];
+ }
+ }
+ else {
+ memset(res_APDU+res_APDU_size, 0, 2);
+ res_APDU_size += 2;
+ }
+
+ res_APDU[res_APDU_size++] = 0x82;
+ res_APDU[res_APDU_size++] = 1;
+ res_APDU[res_APDU_size] = 0;
+ if (pe->type == FILE_TYPE_INTERNAL_EF)
+ res_APDU[res_APDU_size++] |= 0x08;
+ else if (pe->type == FILE_TYPE_WORKING_EF)
+ res_APDU[res_APDU_size++] |= pe->ef_structure & 0x7;
+ else if (pe->type == FILE_TYPE_DF)
+ res_APDU[res_APDU_size++] |= 0x38;
+
+ res_APDU[res_APDU_size++] = 0x83;
+ res_APDU[res_APDU_size++] = 2;
+ put_uint16_t(pe->fid, res_APDU+res_APDU_size);
+ res_APDU_size += 2;
+ res_APDU[1] = res_APDU_size-2;
+}
+
+#define MAX_DYNAMIC_FILES 64
+uint16_t dynamic_files = 0;
+file_t dynamic_file[MAX_DYNAMIC_FILES];
+
+bool card_terminated = false;
+
+bool is_parent(const file_t *child, const file_t *parent) {
+ if (child == parent)
+ return true;
+ if (child == MF)
+ return false;
+ return is_parent(&file_entries[child->parent], parent);
+}
+
+file_t *get_parent(file_t *f) {
+ return &file_entries[f->parent];
+}
+
+file_t *search_by_name(uint8_t *name, uint16_t namelen) {
+ for (file_t *p = file_entries; p != file_last; p++) {
+ if (p->name && *p->name == apdu.cmd_apdu_data_len && memcmp(p->name+1, name, namelen) == 0) {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+file_t *search_by_fid(const uint16_t fid, const file_t *parent, const uint8_t sp) {
+
+ for (file_t *p = file_entries; p != file_last; p++) {
+ if (p->fid != 0x0000 && p->fid == fid) {
+ if (!parent || (parent && is_parent(p, parent))) {
+ if (!sp || sp == SPECIFY_ANY || (((sp & SPECIFY_EF) && (p->type & FILE_TYPE_INTERNAL_EF)) || ((sp & SPECIFY_DF) && p->type == FILE_TYPE_DF)))
+ return p;
+ }
+ }
+ }
+ return NULL;
+}
+
+uint8_t make_path_buf(const file_t *pe, uint8_t *buf, uint8_t buflen, const file_t *top) {
+ if (!buflen)
+ return 0;
+ if (pe == top) //MF or relative DF
+ return 0;
+ put_uint16_t(pe->fid, buf);
+ return make_path_buf(&file_entries[pe->parent], buf+2, buflen-2, top)+2;
+}
+
+uint8_t make_path(const file_t *pe, const file_t *top, uint8_t *path) {
+ uint8_t buf[MAX_DEPTH*2], *p = path;
+ put_uint16_t(pe->fid, buf);
+ uint8_t depth = make_path_buf(&file_entries[pe->parent], buf+2, sizeof(buf)-2, top)+2;
+ for (int d = depth-2; d >= 0; d -= 2) {
+ memcpy(p, buf+d, 2);
+ p += 2;
+ }
+ return depth;
+}
+
+file_t *search_by_path(const uint8_t *pe_path, uint8_t pathlen, const file_t *parent) {
+ uint8_t path[MAX_DEPTH*2];
+ if (pathlen > sizeof(path)) {
+ return NULL;
+ }
+ for (file_t *p = file_entries; p != file_last; p++) {
+ uint8_t depth = make_path(p, parent, path);
+ if (pathlen == depth && memcmp(path, pe_path, depth) == 0)
+ return p;
+ }
+ return NULL;
+}
+
+file_t *currentEF = NULL;
+file_t *currentDF = NULL;
+const file_t *selected_applet = NULL;
+bool isUserAuthenticated = false;
+
+bool authenticate_action(const file_t *ef, uint8_t op) {
+ uint8_t acl = ef->acl[op];
+ if (acl == 0x0)
+ return true;
+ else if (acl == 0xff)
+ return false;
+ else if (acl == 0x90 || acl & 0x9F == 0x10) {
+ // PIN required.
+ if(isUserAuthenticated) {
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ return false;
+}
+
+void initialize_chain(file_chain_t **chain) {
+ file_chain_t *next;
+ for (file_chain_t *f = *chain; f; f = next) {
+ next = f->next;
+ free(f);
+ }
+ *chain = NULL;
+}
+
+void initialize_flash(bool hard) {
+ if (hard) {
+ const uint8_t empty[8] = { 0 };
+ flash_program_block(end_data_pool, empty, sizeof(empty));
+ low_flash_available();
+ }
+ for (file_t *f = file_entries; f != file_last; f++) {
+ if ((f->type & FILE_DATA_FLASH) == FILE_DATA_FLASH)
+ f->data = NULL;
+ }
+ dynamic_files = 0;
+}
+
+void scan_flash() {
+ initialize_flash(false); //soft initialization
+ if (*(uintptr_t *)end_data_pool == 0xffffffff && *(uintptr_t *)(end_data_pool+sizeof(uintptr_t)) == 0xffffffff)
+ {
+ printf("First initialization (or corrupted!)\r\n");
+ const uint8_t empty[8] = { 0 };
+ flash_program_block(end_data_pool, empty, sizeof(empty));
+ //low_flash_available();
+ //wait_flash_finish();
+ }
+ printf("SCAN\r\n");
+
+ uintptr_t base = flash_read_uintptr(end_data_pool);
+ for (uintptr_t base = flash_read_uintptr(end_data_pool); base >= start_data_pool; base = flash_read_uintptr(base)) {
+ if (base == 0x0) //all is empty
+ break;
+
+ uint16_t fid = flash_read_uint16(base+sizeof(uintptr_t)+sizeof(uintptr_t));
+ printf("[%x] scan fid %x, len %d\r\n",base,fid,flash_read_uint16(base+sizeof(uintptr_t)+sizeof(uintptr_t)+sizeof(uint16_t)));
+ file_t *file = (file_t *)search_by_fid(fid, NULL, SPECIFY_EF);
+ if (file)
+ file->data = (uint8_t *)(base+sizeof(uintptr_t)+sizeof(uintptr_t)+sizeof(uint16_t));
+ if (flash_read_uintptr(base) == 0x0) {
+ break;
+ }
+ }
+}
+
+uint8_t *file_read(const uint8_t *addr) {
+ return flash_read((uintptr_t)addr);
+}
+uint16_t file_read_uint16(const uint8_t *addr) {
+ return flash_read_uint16((uintptr_t)addr);
+}
+uint8_t file_read_uint8(const uint8_t *addr) {
+ return flash_read_uint8((uintptr_t)addr);
+}
+
+file_t *search_dynamic_file(uint16_t fid) {
+ for (int i = 0; i < dynamic_files; i++) {
+ if (dynamic_file[i].fid == fid)
+ return &dynamic_file[i];
+ }
+ return NULL;
+}
+
+int delete_dynamic_file(file_t *f) {
+ for (int i = 0; i < dynamic_files; i++) {
+ if (dynamic_file[i].fid == f->fid) {
+ for (int j = i+1; j < dynamic_files; j++)
+ memcpy(&dynamic_file[j-1], &dynamic_file[j], sizeof(file_t));
+ dynamic_files--;
+ return CCID_OK;
+ }
+ }
+ return CCID_ERR_FILE_NOT_FOUND;
+}
+
+file_t *file_new(uint16_t fid) {
+ file_t *f;
+ if ((f = search_dynamic_file(fid)))
+ return f;
+ if (dynamic_files == MAX_DYNAMIC_FILES)
+ return NULL;
+ f = &dynamic_file[dynamic_files];
+ dynamic_files++;
+ file_t file = {
+ .fid = fid,
+ .parent = 5,
+ .name = NULL,
+ .type = FILE_TYPE_WORKING_EF,
+ .ef_structure = FILE_EF_TRANSPARENT,
+ .data = NULL,
+ .acl = {0}
+ };
+ memcpy(f, &file, sizeof(file_t));
+ //memset((uint8_t *)f->acl, 0x90, sizeof(f->acl));
+ return f;
+}
+
+file_chain_t *add_file_to_chain(file_t *file, file_chain_t **chain) {
+ if (search_file_chain(file->fid, *chain))
+ return NULL;
+ file_chain_t *fc = (file_chain_t *)malloc(sizeof(file_chain_t));
+ fc->file = file;
+ fc->next = *chain;
+ *chain = fc;
+ return fc;
+}
+
+file_t *search_file_chain(uint16_t fid, file_chain_t *chain) {
+ for (file_chain_t *fc = chain; fc; fc = fc->next) {
+ if (fid == fc->file->fid) {
+ return fc->file;
+ }
+ }
+ return NULL;
+}
\ No newline at end of file
diff --git a/src/fs/file.h b/src/fs/file.h
new file mode 100644
index 0000000..a64f6f6
--- /dev/null
+++ b/src/fs/file.h
@@ -0,0 +1,124 @@
+/*
+ * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid).
+ * Copyright (c) 2022 Pol Henarejos.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+#ifndef _FILE_H_
+#define _FILE_H_
+
+#include
+#include "pico/stdlib.h"
+
+#define FILE_TYPE_UNKNOWN 0x00
+#define FILE_TYPE_DF 0x04
+#define FILE_TYPE_INTERNAL_EF 0x03
+#define FILE_TYPE_WORKING_EF 0x01
+#define FILE_TYPE_BSO 0x10
+#define FILE_PERSISTENT 0x20
+#define FILE_DATA_FLASH 0x40
+#define FILE_DATA_FUNC 0x80
+
+/* EF structures */
+#define FILE_EF_UNKNOWN 0x00
+#define FILE_EF_TRANSPARENT 0x01
+#define FILE_EF_LINEAR_FIXED 0x02
+#define FILE_EF_LINEAR_FIXED_TLV 0x03
+#define FILE_EF_LINEAR_VARIABLE 0x04
+#define FILE_EF_LINEAR_VARIABLE_TLV 0x05
+#define FILE_EF_CYCLIC 0x06
+#define FILE_EF_CYCLIC_TLV 0x07
+
+#define ACL_OP_DELETE_SELF 0x00
+#define ACL_OP_CREATE_DF 0x01
+#define ACL_OP_CREATE_EF 0x02
+#define ACL_OP_DELETE_CHILD 0x03
+#define ACL_OP_WRITE 0x04
+#define ACL_OP_UPDATE_ERASE 0x05
+#define ACL_OP_READ_SEARCH 0x06
+
+#define SPECIFY_EF 0x1
+#define SPECIFY_DF 0x2
+#define SPECIFY_ANY 0x3
+
+#define EF_DKEK 0x108F
+#define EF_PRKDFS 0x6040
+#define EF_PUKDFS 0x6041
+#define EF_CDFS 0x6042
+#define EF_AODFS 0x6043
+#define EF_DODFS 0x6044
+#define EF_SKDFS 0x6045
+#define EF_DEVOPS 0x100E
+
+#define MAX_DEPTH 4
+
+typedef struct file
+{
+ const uint16_t fid;
+ const uint8_t parent; //entry number in the whole table!!
+ const uint8_t *name;
+ const uint8_t type;
+ const uint8_t ef_structure;
+ uint8_t *data; //should include 2 bytes len at begining
+ const uint8_t acl[7];
+} __attribute__((packed)) file_t;
+
+typedef struct file_chain
+{
+ file_t *file;
+ struct file_chain *next;
+} file_chain_t;
+
+extern file_t *currentEF;
+extern file_t *currentDF;
+extern const file_t *selected_applet;
+
+extern const file_t *MF;
+extern const file_t *file_last;
+extern const file_t *file_openpgp;
+extern const file_t *file_sc_hsm;
+extern bool card_terminated;
+extern file_t *file_pin1;
+extern file_t *file_retries_pin1;
+extern file_t *file_sopin;
+extern file_t *file_retries_sopin;
+
+extern file_t *search_by_fid(const uint16_t fid, const file_t *parent, const uint8_t sp);
+extern file_t *search_by_name(uint8_t *name, uint16_t namelen);
+extern file_t *search_by_path(const uint8_t *pe_path, uint8_t pathlen, const file_t *parent);
+extern bool authenticate_action(const file_t *ef, uint8_t op);
+extern void process_fci(const file_t *pe);
+extern void scan_flash();
+extern void initialize_flash(bool);
+
+extern file_t file_entries[];
+
+extern uint8_t *file_read(const uint8_t *addr);
+extern uint16_t file_read_uint16(const uint8_t *addr);
+extern uint8_t file_read_uint8(const uint8_t *addr);
+extern file_t *file_new(uint16_t);
+file_t *get_parent(file_t *f);
+
+extern uint16_t dynamic_files;
+extern file_t dynamic_file[];
+extern file_t *search_dynamic_file(uint16_t);
+extern int delete_dynamic_file(file_t *f);
+
+extern file_chain_t *add_file_to_chain(file_t *file, file_chain_t **chain);
+extern file_t *search_file_chain(uint16_t fid, file_chain_t *chain);
+extern bool isUserAuthenticated;
+
+#endif
+
diff --git a/src/fs/flash.c b/src/fs/flash.c
new file mode 100644
index 0000000..e69fa99
--- /dev/null
+++ b/src/fs/flash.c
@@ -0,0 +1,133 @@
+/*
+ * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid).
+ * Copyright (c) 2022 Pol Henarejos.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+#include
+#include
+
+#include "pico/stdlib.h"
+#include "hardware/flash.h"
+#include "ccid2040.h"
+#include "tusb.h"
+#include "file.h"
+
+/*
+ * ------------------------------------------------------
+ * | |
+ * | next_addr | prev_addr | fid | data (len + payload) |
+ * | |
+ * ------------------------------------------------------
+ */
+#define FLASH_TARGET_OFFSET (PICO_FLASH_SIZE_BYTES >> 1) // DATA starts at the mid of flash
+#define FLASH_DATA_HEADER_SIZE (sizeof(uintptr_t)+sizeof(uint32_t))
+#define FLASH_PERMANENT_REGION (4*FLASH_SECTOR_SIZE) // 4 sectors (16kb) of permanent memory
+
+//To avoid possible future allocations, data region starts at the end of flash and goes upwards to the center region
+
+const uintptr_t start_data_pool = (XIP_BASE + FLASH_TARGET_OFFSET);
+const uintptr_t end_data_pool = (XIP_BASE + PICO_FLASH_SIZE_BYTES)-FLASH_DATA_HEADER_SIZE-FLASH_PERMANENT_REGION; //This is a fixed value. DO NOT CHANGE
+#define FLASH_ADDR_DATA_STORAGE_START start_data_pool
+
+extern int flash_program_block(uintptr_t addr, const uint8_t *data, size_t len);
+extern int flash_program_halfword (uintptr_t addr, uint16_t data);
+extern int flash_program_uintptr(uintptr_t, uintptr_t);
+extern uintptr_t flash_read_uintptr(uintptr_t addr);
+extern uint16_t flash_read_uint16(uintptr_t addr);
+
+extern void low_flash_available();
+
+uintptr_t allocate_free_addr(uint16_t size) {
+ if (size > FLASH_SECTOR_SIZE)
+ return 0x0; //ERROR
+ size_t real_size = size+sizeof(uint16_t)+sizeof(uintptr_t)+sizeof(uint16_t)+sizeof(uintptr_t); //len+len size+next address+fid+prev_addr size
+ uintptr_t next_base = 0x0;
+ for (uintptr_t base = end_data_pool; base >= start_data_pool; base = next_base) {
+ uintptr_t addr_alg = base & -FLASH_SECTOR_SIZE; //start address of sector
+ uintptr_t potential_addr = base-real_size;
+ next_base = flash_read_uintptr(base);
+ //printf("nb %x %x %x %x\r\n",base,next_base,addr_alg,potential_addr);
+ //printf("fid %x\r\n",flash_read_uint16(next_base+sizeof(uintptr_t)));
+ if (next_base == 0x0) { //we are at the end
+ //now we check if we fit in the current sector
+ if (addr_alg <= potential_addr) //it fits in the current sector
+ {
+ flash_program_uintptr(potential_addr, 0x0);
+ flash_program_uintptr(potential_addr+sizeof(uintptr_t), base);
+ flash_program_uintptr(base, potential_addr);
+ return potential_addr;
+ }
+ else if (addr_alg-FLASH_SECTOR_SIZE >= start_data_pool) { //check whether it fits in the next sector, so we take addr_aligned as the base
+ potential_addr = addr_alg-real_size;
+ flash_program_uintptr(potential_addr, 0x0);
+ flash_program_uintptr(potential_addr+sizeof(uintptr_t), base);
+ flash_program_uintptr(base, potential_addr);
+ return potential_addr;
+ }
+ return 0x0;
+ }
+ //we check if |base-(next_addr+size_next_addr)| > |base-potential_addr| only if fid != 1xxx (not size blocked)
+ else if (addr_alg <= potential_addr && base-(next_base+flash_read_uint16(next_base+sizeof(uintptr_t)+sizeof(uintptr_t)+sizeof(uint16_t))+2*sizeof(uint16_t)+2*sizeof(uintptr_t)) > base-potential_addr && flash_read_uint16(next_base+sizeof(uintptr_t)) & 0x1000 != 0x1000) {
+ flash_program_uintptr(potential_addr, next_base);
+ flash_program_uintptr(potential_addr+sizeof(uintptr_t), base);
+ flash_program_uintptr(base, potential_addr);
+ return potential_addr;
+ }
+ }
+ return 0x0; //probably never reached
+}
+
+int flash_clear_file(file_t *file) {
+ uintptr_t base_addr = (uintptr_t)(file->data-sizeof(uintptr_t)-sizeof(uint16_t)-sizeof(uintptr_t));
+ uintptr_t prev_addr = flash_read_uintptr(base_addr+sizeof(uintptr_t));
+ uintptr_t next_addr = flash_read_uintptr(base_addr);
+ //printf("nc %x->%x %x->%x\r\n",prev_addr,flash_read_uintptr(prev_addr),base_addr,next_addr);
+ flash_program_uintptr(prev_addr, next_addr);
+ flash_program_halfword((uintptr_t)file->data, 0);
+ if (next_addr > 0)
+ flash_program_uintptr(next_addr+sizeof(uintptr_t), prev_addr);
+ //printf("na %x->%x\r\n",prev_addr,flash_read_uintptr(prev_addr));
+ return CCID_OK;
+}
+
+int flash_write_data_to_file(file_t *file, const uint8_t *data, uint16_t len) {
+ if (!file)
+ return CCID_ERR_NULL_PARAM;
+ if (len > FLASH_SECTOR_SIZE)
+ return CCID_ERR_NO_MEMORY;
+ if (file->data) { //already in flash
+ uint16_t size_file_flash = flash_read_uint16((uintptr_t)file->data);
+ if (len <= size_file_flash) { //it fits, no need to move it
+ flash_program_halfword((uintptr_t)file->data, len);
+ if (data)
+ flash_program_block((uintptr_t)file->data+sizeof(uint16_t), data, len);
+ return CCID_OK;
+ }
+ else { //we clear the old file
+ flash_clear_file(file);
+ }
+ }
+ uintptr_t new_addr = allocate_free_addr(len);
+ //printf("na %x\r\n",new_addr);
+ if (new_addr == 0x0)
+ return CCID_ERR_NO_MEMORY;
+ file->data = (uint8_t *)new_addr+sizeof(uintptr_t)+sizeof(uint16_t)+sizeof(uintptr_t); //next addr+fid+prev addr
+ flash_program_halfword(new_addr+sizeof(uintptr_t)+sizeof(uintptr_t), file->fid);
+ flash_program_halfword((uintptr_t)file->data, len);
+ if (data)
+ flash_program_block((uintptr_t)file->data+sizeof(uint16_t), data, len);
+ return CCID_OK;
+}
diff --git a/src/fs/low_flash.c b/src/fs/low_flash.c
new file mode 100644
index 0000000..2e0d823
--- /dev/null
+++ b/src/fs/low_flash.c
@@ -0,0 +1,247 @@
+/*
+ * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid).
+ * Copyright (c) 2022 Pol Henarejos.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+#include
+#include
+#include
+
+#include "pico/stdlib.h"
+#include "hardware/flash.h"
+#include "hardware/sync.h"
+#include "pico/mutex.h"
+#include "pico/sem.h"
+#include "pico/multicore.h"
+#include "ccid2040.h"
+#include
+
+#define TOTAL_FLASH_PAGES 4
+
+typedef struct page_flash {
+ uint8_t page[FLASH_SECTOR_SIZE];
+ uintptr_t address;
+ bool ready;
+ bool erase;
+ size_t page_size; //this param is for easy erase. It allows to erase with a single call. IT DOES NOT APPLY TO WRITE
+} page_flash_t;
+
+static page_flash_t flash_pages[TOTAL_FLASH_PAGES];
+
+static mutex_t mtx_flash;
+static semaphore_t sem_wait;
+
+static uint8_t ready_pages = 0;
+
+bool flash_available = false;
+static bool locked_out = false;
+
+
+//this function has to be called from the core 0
+void do_flash()
+{
+ if (mutex_try_enter(&mtx_flash, NULL) == true) {
+ if (locked_out == true && flash_available == true && ready_pages > 0) {
+ //printf(" DO_FLASH AVAILABLE\r\n");
+ for (int r = 0; r < TOTAL_FLASH_PAGES; r++) {
+ if (flash_pages[r].ready == true) {
+ //printf("WRITTING %X\r\n",flash_pages[r].address-XIP_BASE);
+ while (multicore_lockout_start_timeout_us(1000) == false);
+ //printf("WRITTING %X\r\n",flash_pages[r].address-XIP_BASE);
+ uint32_t ints = save_and_disable_interrupts();
+ flash_range_erase(flash_pages[r].address-XIP_BASE, FLASH_SECTOR_SIZE);
+ flash_range_program(flash_pages[r].address-XIP_BASE, flash_pages[r].page, FLASH_SECTOR_SIZE);
+ restore_interrupts (ints);
+ while (multicore_lockout_end_timeout_us(1000) == false);
+ //printf("WRITEN %X !\r\n",flash_pages[r].address);
+
+ flash_pages[r].ready = false;
+ ready_pages--;
+ }
+ else if (flash_pages[r].erase == true) {
+ while (multicore_lockout_start_timeout_us(1000) == false);
+ //printf("WRITTING\r\n");
+ flash_range_erase(flash_pages[r].address-XIP_BASE, flash_pages[r].page_size ? ((int)(flash_pages[r].page_size/FLASH_SECTOR_SIZE))*FLASH_SECTOR_SIZE : FLASH_SECTOR_SIZE);
+ while (multicore_lockout_end_timeout_us(1000) == false);
+ flash_pages[r].erase = false;
+ ready_pages--;
+ }
+ }
+ flash_available = false;
+ if (ready_pages != 0) {
+ DEBUG_INFO("ERROR: DO FLASH DOES NOT HAVE ZERO PAGES");
+ }
+ }
+ mutex_exit(&mtx_flash);
+ }
+ sem_release(&sem_wait);
+}
+
+//this function has to be called from the core 0
+void low_flash_init() {
+ mutex_init(&mtx_flash);
+ sem_init(&sem_wait, 0, 1);
+ memset(flash_pages, 0, sizeof(page_flash_t)*TOTAL_FLASH_PAGES);
+}
+
+void low_flash_init_core1() {
+ mutex_enter_blocking(&mtx_flash);
+ multicore_lockout_victim_init();
+ locked_out = true;
+ mutex_exit(&mtx_flash);
+}
+
+void wait_flash_finish() {
+ sem_acquire_blocking(&sem_wait); //blocks until released
+ //wake up
+ sem_acquire_blocking(&sem_wait); //decrease permits
+}
+
+void low_flash_available() {
+ mutex_enter_blocking(&mtx_flash);
+ flash_available = true;
+ mutex_exit(&mtx_flash);
+}
+
+page_flash_t *find_free_page(uintptr_t addr) {
+ uintptr_t addr_alg = addr & -FLASH_SECTOR_SIZE;
+ page_flash_t *p = NULL;
+ for (int r = 0; r < TOTAL_FLASH_PAGES; r++)
+ {
+ if ((!flash_pages[r].ready && !flash_pages[r].erase) || flash_pages[r].address == addr_alg) //first available
+ {
+ p = &flash_pages[r];
+ if (!flash_pages[r].ready && !flash_pages[r].erase)
+ {
+ memcpy(p->page, (uint8_t *)addr_alg, FLASH_SECTOR_SIZE);
+ ready_pages++;
+ p->address = addr_alg;
+ p->ready = true;
+ }
+ return p;
+ }
+ }
+ return NULL;
+}
+
+int flash_program_block(uintptr_t addr, const uint8_t *data, size_t len) {
+ uintptr_t addr_alg = addr & -FLASH_SECTOR_SIZE;
+ page_flash_t *p = NULL;
+
+ if (!data || len == 0)
+ return CCID_ERR_NULL_PARAM;
+
+ mutex_enter_blocking(&mtx_flash);
+ if (ready_pages == TOTAL_FLASH_PAGES) {
+ mutex_exit(&mtx_flash);
+ DEBUG_INFO("ERROR: ALL FLASH PAGES CACHED\r\n");
+ return CCID_ERR_NO_MEMORY;
+ }
+ if (!(p = find_free_page(addr)))
+ {
+ mutex_exit(&mtx_flash);
+ DEBUG_INFO("ERROR: FLASH CANNOT FIND A PAGE (rare error)\r\n");
+ return CCID_ERR_MEMORY_FATAL;
+ }
+ memcpy(&p->page[addr&(FLASH_SECTOR_SIZE-1)], data, len);
+ //printf("Flash: modified page %X with data %x at [%x] (top page %X)\r\n",addr_alg,data,addr&(FLASH_SECTOR_SIZE-1),addr);
+ mutex_exit(&mtx_flash);
+ return CCID_OK;
+}
+
+int flash_program_halfword (uintptr_t addr, uint16_t data) {
+ return flash_program_block(addr, (const uint8_t *)&data, sizeof(uint16_t));
+}
+
+int flash_program_word (uintptr_t addr, uint32_t data) {
+ return flash_program_block(addr, (const uint8_t *)&data, sizeof(uint32_t));
+}
+
+int flash_program_uintptr (uintptr_t addr, uintptr_t data) {
+ return flash_program_block(addr, (const uint8_t *)&data, sizeof(uintptr_t));
+}
+
+uint8_t *flash_read(uintptr_t addr) {
+ uintptr_t addr_alg = addr & -FLASH_SECTOR_SIZE;
+ mutex_enter_blocking(&mtx_flash);
+ if (ready_pages > 0) {
+ for (int r = 0; r < TOTAL_FLASH_PAGES; r++)
+ {
+ if (flash_pages[r].ready && flash_pages[r].address == addr_alg) {
+ uint8_t *v = &flash_pages[r].page[addr&(FLASH_SECTOR_SIZE-1)];
+ mutex_exit(&mtx_flash);
+ return v;
+ }
+ }
+ }
+ uint8_t *v = (uint8_t *)addr;
+ mutex_exit(&mtx_flash);
+ return v;
+}
+
+uintptr_t flash_read_uintptr(uintptr_t addr) {
+ uint8_t *p = flash_read(addr);
+ uintptr_t v = 0x0;
+ for (int i = 0; i < sizeof(uintptr_t); i++) {
+ v |= (uintptr_t)p[i]<<(8*i);
+ }
+ return v;
+}
+uint16_t flash_read_uint16(uintptr_t addr) {
+ uint8_t *p = flash_read(addr);
+ uint16_t v = 0x0;
+ for (int i = 0; i < sizeof(uint16_t); i++) {
+ v |= p[i]<<(8*i);
+ }
+ return v;
+}
+uint8_t flash_read_uint8(uintptr_t addr) {
+ return *flash_read(addr);
+}
+
+int flash_erase_page (uintptr_t addr, size_t page_size) {
+ uintptr_t addr_alg = addr & -FLASH_SECTOR_SIZE;
+ page_flash_t *p = NULL;
+
+ mutex_enter_blocking(&mtx_flash);
+ if (ready_pages == TOTAL_FLASH_PAGES) {
+ mutex_exit(&mtx_flash);
+ DEBUG_INFO("ERROR: ALL FLASH PAGES CACHED\r\n");
+ return CCID_ERR_NO_MEMORY;
+ }
+ if (!(p = find_free_page(addr))) {
+ DEBUG_INFO("ERROR: FLASH CANNOT FIND A PAGE (rare error)\r\n");
+ mutex_exit(&mtx_flash);
+ return CCID_ERR_MEMORY_FATAL;
+ }
+ p->erase = true;
+ p->ready = false;
+ p->page_size = page_size;
+ mutex_exit(&mtx_flash);
+
+ return CCID_OK;
+}
+
+bool flash_check_blank(const uint8_t *p_start, size_t size)
+{
+ const uint8_t *p;
+
+ for (p = p_start; p < p_start + size; p++) {
+ if (*p != 0xff)
+ return false;
+ }
+ return true;
+}
diff --git a/src/rng/neug.c b/src/rng/neug.c
new file mode 100644
index 0000000..63c731a
--- /dev/null
+++ b/src/rng/neug.c
@@ -0,0 +1,182 @@
+/*
+ * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid).
+ * Copyright (c) 2022 Pol Henarejos.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+//Part of the code is taken from GnuK (GPLv3)
+
+
+#include
+#include
+#include
+#include "pico/stdlib.h"
+
+#include "neug.h"
+#include "hardware/structs/rosc.h"
+#include "hardware/gpio.h"
+#include "hardware/adc.h"
+#include "bsp/board.h"
+#include "pico/unique_id.h"
+
+void adc_start() {
+ adc_init();
+ adc_gpio_init(27);
+ adc_select_input(1);
+}
+
+void adc_stop() {
+}
+
+static uint64_t random_word = 0xcbf29ce484222325;
+static uint8_t ep_round = 0;
+
+static void ep_init() {
+ random_word = 0xcbf29ce484222325;
+ ep_round = 0;
+}
+
+/* Here, we assume a little endian architecture. */
+static int ep_process () {
+ if (ep_round == 0) {
+ ep_init();
+ }
+ uint64_t word = 0x0;
+ for (int n = 0; n < 64; n++) {
+ uint8_t bit1, bit2;
+ do
+ {
+ bit1 = rosc_hw->randombit&0xff;
+ //sleep_ms(1);
+ bit2 = rosc_hw->randombit&0xff;
+ } while(bit1 == bit2);
+ word = (word << 1) | bit1;
+ }
+ random_word ^= word^board_millis()^adc_read();
+ random_word *= 0x00000100000001B3;
+ if (++ep_round == 8) {
+ ep_round = 0;
+ return 2; //2 words
+ }
+ return 0;
+}
+
+static const uint32_t *ep_output() {
+ return (uint32_t *)&random_word;
+}
+
+struct rng_rb {
+ uint32_t *buf;
+ uint8_t head, tail;
+ uint8_t size;
+ unsigned int full :1;
+ unsigned int empty :1;
+};
+
+static void rb_init(struct rng_rb *rb, uint32_t *p, uint8_t size) {
+ rb->buf = p;
+ rb->size = size;
+ rb->head = rb->tail = 0;
+ rb->full = 0;
+ rb->empty = 1;
+}
+
+static void rb_add(struct rng_rb *rb, uint32_t v) {
+ rb->buf[rb->tail++] = v;
+ if (rb->tail == rb->size)
+ rb->tail = 0;
+ if (rb->tail == rb->head)
+ rb->full = 1;
+ rb->empty = 0;
+}
+
+static uint32_t rb_del(struct rng_rb *rb) {
+ uint32_t v = rb->buf[rb->head++];
+
+ if (rb->head == rb->size)
+ rb->head = 0;
+ if (rb->head == rb->tail)
+ rb->empty = 1;
+ rb->full = 0;
+
+ return v;
+}
+
+static struct rng_rb the_ring_buffer;
+
+void *neug_task() {
+ struct rng_rb *rb = &the_ring_buffer;
+
+ int n;
+
+ if ((n = ep_process())) {
+ int i;
+ const uint32_t *vp;
+
+ vp = ep_output();
+
+ for (i = 0; i < n; i++) {
+ rb_add (rb, *vp++);
+ if (rb->full)
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+void neug_init(uint32_t *buf, uint8_t size) {
+ pico_unique_board_id_t unique_id;
+ pico_get_unique_board_id(&unique_id);
+ const uint32_t *u = (const uint32_t *)unique_id.id;
+ struct rng_rb *rb = &the_ring_buffer;
+ int i;
+
+ rb_init(rb, buf, size);
+
+ adc_start();
+
+ ep_init();
+}
+
+void neug_flush(void) {
+ struct rng_rb *rb = &the_ring_buffer;
+
+ while (!rb->empty)
+ rb_del (rb);
+}
+
+uint32_t neug_get(int kick) {
+ struct rng_rb *rb = &the_ring_buffer;
+ uint32_t v;
+
+ while (rb->empty)
+ neug_task();
+ v = rb_del(rb);
+
+ return v;
+}
+
+void neug_wait_full(void) { //should be called only on core1
+ struct rng_rb *rb = &the_ring_buffer;
+
+ while (!rb->full) {
+ sleep_ms(1);
+ }
+}
+
+void neug_fini(void) {
+ neug_get(1);
+}
+
diff --git a/src/rng/neug.h b/src/rng/neug.h
new file mode 100644
index 0000000..2ced6e3
--- /dev/null
+++ b/src/rng/neug.h
@@ -0,0 +1,29 @@
+/*
+ * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid).
+ * Copyright (c) 2022 Pol Henarejos.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef _NEUG_H_
+#define _NEUG_H_
+
+#define NEUG_PRE_LOOP 32
+
+void neug_init(uint32_t *buf, uint8_t size);
+uint32_t neug_get();
+void neug_flush(void);
+void neug_wait_full(void);
+void neug_fini(void);
+
+#endif
\ No newline at end of file
diff --git a/src/rng/random.c b/src/rng/random.c
new file mode 100644
index 0000000..62531b5
--- /dev/null
+++ b/src/rng/random.c
@@ -0,0 +1,109 @@
+/*
+ * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid).
+ * Copyright (c) 2022 Pol Henarejos.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+#include
+#include
+
+#include "neug.h"
+
+#define RANDOM_BYTES_LENGTH 32
+static uint32_t random_word[RANDOM_BYTES_LENGTH/sizeof (uint32_t)];
+
+void random_init(void) {
+ int i;
+
+ neug_init(random_word, RANDOM_BYTES_LENGTH/sizeof (uint32_t));
+
+ for (i = 0; i < NEUG_PRE_LOOP; i++)
+ neug_get();
+}
+
+void random_fini(void) {
+ neug_fini ();
+}
+
+/*
+ * Return pointer to random 32-byte
+ */
+void random_bytes_free (const uint8_t *p);
+#define MAX_RANDOM_BUFFER 1024
+const uint8_t * random_bytes_get(size_t len) {
+ if (len > MAX_RANDOM_BUFFER)
+ return NULL;
+ static uint32_t return_word[MAX_RANDOM_BUFFER/sizeof(uint32_t)];
+ for (int ix = 0; ix < len; ix += RANDOM_BYTES_LENGTH) {
+ neug_wait_full();
+ memcpy(return_word+ix/sizeof(uint32_t), random_word, RANDOM_BYTES_LENGTH);
+ random_bytes_free((const uint8_t *)random_word);
+ }
+ return (const uint8_t *)return_word;
+}
+
+/*
+ * Free pointer to random 32-byte
+ */
+void random_bytes_free(const uint8_t *p) {
+ (void)p;
+ memset(random_word, 0, RANDOM_BYTES_LENGTH);
+ neug_flush();
+}
+
+/*
+ * Return 4-byte salt
+ */
+void random_get_salt(uint8_t *p) {
+ uint32_t rnd;
+
+ rnd = neug_get();
+ memcpy(p, &rnd, sizeof (uint32_t));
+ rnd = neug_get();
+ memcpy(p + sizeof (uint32_t), &rnd, sizeof (uint32_t));
+}
+
+
+/*
+ * Random byte iterator
+ */
+int random_gen(void *arg, unsigned char *out, size_t out_len) {
+ uint8_t *index_p = (uint8_t *)arg;
+ uint8_t index = index_p ? *index_p : 0;
+ size_t n;
+
+ while (out_len) {
+ neug_wait_full();
+
+ n = RANDOM_BYTES_LENGTH - index;
+ if (n > out_len)
+ n = out_len;
+
+ memcpy(out, ((unsigned char *)random_word) + index, n);
+ out += n;
+ out_len -= n;
+ index += n;
+
+ if (index >= RANDOM_BYTES_LENGTH) {
+ index = 0;
+ neug_flush();
+ }
+ }
+
+ if (index_p)
+ *index_p = index;
+
+ return 0;
+}
diff --git a/src/rng/random.h b/src/rng/random.h
new file mode 100644
index 0000000..446edbf
--- /dev/null
+++ b/src/rng/random.h
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid).
+ * Copyright (c) 2022 Pol Henarejos.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+#ifndef _RANDOM_H_
+#define _RANDOM_H_
+
+#include "stdlib.h"
+#include "pico/stdlib.h"
+
+void random_init (void);
+void random_fini (void);
+
+/* 32-byte random bytes */
+const uint8_t *random_bytes_get (size_t);
+void random_bytes_free (const uint8_t *p);
+
+/* 8-byte salt */
+void random_get_salt (uint8_t *p);
+
+/* iterator returning a byta at a time */
+int random_gen (void *arg, unsigned char *output, size_t output_len);
+
+#endif
\ No newline at end of file
diff --git a/src/usb/ccid.h b/src/usb/ccid.h
new file mode 100644
index 0000000..3090e02
--- /dev/null
+++ b/src/usb/ccid.h
@@ -0,0 +1,73 @@
+/*
+ * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid).
+ * Copyright (c) 2022 Pol Henarejos.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef _CCID_H_
+#define _CCID_H_
+
+struct ccid_class_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint16_t bcdCCID;
+ uint8_t bMaxSlotIndex;
+ uint8_t bVoltageSupport;
+ uint32_t dwProtocols;
+ uint32_t dwDefaultClock;
+ uint32_t dwMaximumClock;
+ uint8_t bNumClockSupport;
+ uint32_t dwDataRate;
+ uint32_t dwMaxDataRate;
+ uint8_t bNumDataRatesSupported;
+ uint32_t dwMaxIFSD;
+ uint32_t dwSynchProtocols;
+ uint32_t dwMechanical;
+ uint32_t dwFeatures;
+ uint32_t dwMaxCCIDMessageLength;
+ uint8_t bClassGetResponse;
+ uint8_t bclassEnvelope;
+ uint16_t wLcdLayout;
+ uint8_t bPINSupport;
+ uint8_t bMaxCCIDBusySlots;
+} __attribute__ ((__packed__));
+
+static const struct ccid_class_descriptor desc_ccid = {
+ .bLength = sizeof(struct ccid_class_descriptor),
+ .bDescriptorType = 0x21,
+ .bcdCCID = (0x0110),
+ .bMaxSlotIndex = 0,
+ .bVoltageSupport = 0x01, // 5.0V
+ .dwProtocols = (
+ 0x01| // T=0
+ 0x02), // T=1
+ .dwDefaultClock = (0xDFC),
+ .dwMaximumClock = (0xDFC),
+ .bNumClockSupport = 0,
+ .dwDataRate = (0x2580),
+ .dwMaxDataRate = (0x2580),
+ .bNumDataRatesSupported = 0,
+ .dwMaxIFSD = (0xFE), // IFSD is handled by the real reader driver
+ .dwSynchProtocols = (0),
+ .dwMechanical = (0),
+ .dwFeatures = 0x40840, //USB-ICC, short & extended APDU
+ .dwMaxCCIDMessageLength = 65544+10,
+ .bClassGetResponse = 0xFF,
+ .bclassEnvelope = 0xFF,
+ .wLcdLayout = 0x0,
+ .bPINSupport = 0x0,
+ .bMaxCCIDBusySlots = 0x01,
+};
+
+#endif
\ No newline at end of file
diff --git a/src/usb/tusb_config.h b/src/usb/tusb_config.h
new file mode 100644
index 0000000..69b5534
--- /dev/null
+++ b/src/usb/tusb_config.h
@@ -0,0 +1,119 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef _TUSB_CONFIG_H_
+#define _TUSB_CONFIG_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------
+// COMMON CONFIGURATION
+//--------------------------------------------------------------------
+
+// defined by board.mk
+#ifndef CFG_TUSB_MCU
+ #error CFG_TUSB_MCU must be defined
+#endif
+
+// RHPort number used for device can be defined by board.mk, default to port 0
+#ifndef BOARD_DEVICE_RHPORT_NUM
+ #define BOARD_DEVICE_RHPORT_NUM 0
+#endif
+
+// RHPort max operational speed can defined by board.mk
+// Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed
+#ifndef BOARD_DEVICE_RHPORT_SPEED
+ #if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \
+ CFG_TUSB_MCU == OPT_MCU_NUC505 || CFG_TUSB_MCU == OPT_MCU_CXD56 || CFG_TUSB_MCU == OPT_MCU_SAMX7X)
+ #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED
+ #else
+ #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED
+ #endif
+#endif
+
+// Device mode with rhport and speed defined by board.mk
+#if BOARD_DEVICE_RHPORT_NUM == 0
+ #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
+#elif BOARD_DEVICE_RHPORT_NUM == 1
+ #define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
+#else
+ #error "Incorrect RHPort configuration"
+#endif
+
+#ifndef CFG_TUSB_OS
+#define CFG_TUSB_OS OPT_OS_PICO
+#endif
+
+// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
+// #define CFG_TUSB_DEBUG 0
+
+/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
+ * Tinyusb use follows macros to declare transferring memory so that they can be put
+ * into those specific section.
+ * e.g
+ * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
+ * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
+ */
+#ifndef CFG_TUSB_MEM_SECTION
+#define CFG_TUSB_MEM_SECTION
+#endif
+
+#ifndef CFG_TUSB_MEM_ALIGN
+#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
+#endif
+
+//--------------------------------------------------------------------
+// DEVICE CONFIGURATION
+//--------------------------------------------------------------------
+
+#ifndef CFG_TUD_ENDPOINT0_SIZE
+#define CFG_TUD_ENDPOINT0_SIZE 64
+#endif
+
+//------------- CLASS -------------//
+#define CFG_TUD_HID 0
+#define CFG_TUD_CDC 0
+#define CFG_TUD_MSC 0
+#define CFG_TUD_MIDI 0
+#define CFG_TUD_VENDOR 1
+
+// HID buffer size Should be sufficient to hold ID (if any) + Data
+#define CFG_TUD_HID_EP_BUFSIZE 16
+
+#define CFG_TUD_VENDOR_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#define CFG_TUD_VENDOR_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+
+#include "pico/types.h"
+
+static inline uint16_t tu_u32_high16(uint32_t ui32) { return (uint16_t) (ui32 >> 16); }
+static inline uint16_t tu_u32_low16 (uint32_t ui32) { return (uint16_t) (ui32 & 0x0000ffffu); }
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_CONFIG_H_ */
diff --git a/src/usb/usb_descriptors.c b/src/usb/usb_descriptors.c
new file mode 100644
index 0000000..7a8b376
--- /dev/null
+++ b/src/usb/usb_descriptors.c
@@ -0,0 +1,209 @@
+/*
+ * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid).
+ * Copyright (c) 2022 Pol Henarejos.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "tusb.h"
+#include "usb_descriptors.h"
+#include "ccid.h"
+#include "pico/unique_id.h"
+#include "version.h"
+
+
+#ifndef USB_VID
+#define USB_VID 0xFEFF
+#endif
+#ifndef USB_PID
+#define USB_PID 0xFCFD
+#endif
+
+#define USB_BCD 0x0200
+
+#define USB_CONFIG_ATT_ONE TU_BIT(7)
+
+#define MAX_USB_POWER 1
+
+
+//--------------------------------------------------------------------+
+// Device Descriptors
+//--------------------------------------------------------------------+
+tusb_desc_device_t const desc_device =
+{
+ .bLength = sizeof(tusb_desc_device_t),
+ .bDescriptorType = TUSB_DESC_DEVICE,
+ .bcdUSB = (USB_BCD),
+
+ .bDeviceClass = 0x00,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+
+ .idVendor = (USB_VID),
+ .idProduct = (USB_PID),
+ .bcdDevice = HSM_VERSION,
+
+ .iManufacturer = 1,
+ .iProduct = 2,
+ .iSerialNumber = 3,
+
+ .bNumConfigurations = 1
+};
+
+uint8_t const * tud_descriptor_device_cb(void)
+{
+ return (uint8_t const *) &desc_device;
+}
+
+tusb_desc_interface_t const desc_interface =
+{
+ .bLength = sizeof(tusb_desc_interface_t),
+ .bDescriptorType = TUSB_DESC_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = TUSB_CLASS_SMART_CARD,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ .iInterface = 5,
+};
+
+//--------------------------------------------------------------------+
+// Configuration Descriptor
+//--------------------------------------------------------------------+
+
+tusb_desc_configuration_t const desc_config =
+{
+ .bLength = sizeof(tusb_desc_configuration_t),
+ .bDescriptorType = TUSB_DESC_CONFIGURATION,
+ .wTotalLength = (sizeof(tusb_desc_configuration_t) + sizeof(tusb_desc_interface_t) + sizeof(struct ccid_class_descriptor) + 2*sizeof(tusb_desc_endpoint_t)),
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = 4,
+ .bmAttributes = USB_CONFIG_ATT_ONE | TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP,
+ .bMaxPower = TUSB_DESC_CONFIG_POWER_MA(MAX_USB_POWER+1),
+};
+
+tusb_desc_endpoint_t const desc_ep1 =
+{
+ .bLength = sizeof(tusb_desc_endpoint_t),
+ .bDescriptorType = TUSB_DESC_ENDPOINT,
+ .bEndpointAddress = TUSB_DIR_IN_MASK | 1,
+ .bmAttributes.xfer = TUSB_XFER_BULK,
+ .wMaxPacketSize.size = (64),
+ .bInterval = 0
+};
+
+tusb_desc_endpoint_t const desc_ep2 =
+{
+ .bLength = sizeof(tusb_desc_endpoint_t),
+ .bDescriptorType = TUSB_DESC_ENDPOINT,
+ .bEndpointAddress = 2,
+ .bmAttributes.xfer = TUSB_XFER_BULK,
+ .wMaxPacketSize.size = (64),
+ .bInterval = 0
+};
+
+static uint8_t desc_config_extended[sizeof(tusb_desc_configuration_t) + sizeof(tusb_desc_interface_t) + sizeof(struct ccid_class_descriptor) + 2*sizeof(tusb_desc_endpoint_t)];
+
+uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
+{
+ (void) index; // for multiple configurations
+
+ static uint8_t initd = 0;
+ if (initd == 0)
+ {
+ uint8_t *p = desc_config_extended;
+ memcpy(p, &desc_config, sizeof(tusb_desc_configuration_t)); p += sizeof(tusb_desc_configuration_t);
+ memcpy(p, &desc_interface, sizeof(tusb_desc_interface_t)); p += sizeof(tusb_desc_interface_t);
+ memcpy(p, &desc_ccid, sizeof(struct ccid_class_descriptor)); p += sizeof(struct ccid_class_descriptor);
+ memcpy(p, &desc_ep1, sizeof(tusb_desc_endpoint_t)); p += sizeof(tusb_desc_endpoint_t);
+ memcpy(p, &desc_ep2, sizeof(tusb_desc_endpoint_t)); p += sizeof(tusb_desc_endpoint_t);
+ initd = 1;
+ }
+ return (const uint8_t *)desc_config_extended;
+}
+
+#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN)
+
+#define MS_OS_20_DESC_LEN 0xB2
+
+uint8_t const desc_bos[] =
+{
+ // total length, number of device caps
+ TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 2)
+};
+
+uint8_t const * tud_descriptor_bos_cb(void)
+{
+ return desc_bos;
+}
+
+//--------------------------------------------------------------------+
+// String Descriptors
+//--------------------------------------------------------------------+
+
+// array of pointer to string descriptors
+char const* string_desc_arr [] =
+{
+ (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
+ "Pol Henarejos", // 1: Manufacturer
+ "Pico HSM", // 2: Product
+ "11223344", // 3: Serials, should use chip ID
+ "Pico HSM Config", // 4: Vendor Interface
+ "Pico HSM Interface"
+};
+
+static uint16_t _desc_str[32];
+
+uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
+{
+ (void) langid;
+
+ uint8_t chr_count;
+
+ if (index == 0) {
+ memcpy(&_desc_str[1], string_desc_arr[0], 2);
+ chr_count = 1;
+ }
+ else {
+ // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
+ // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
+
+ if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) )
+ return NULL;
+
+ const char* str = string_desc_arr[index];
+ char unique_id_str[2 * PICO_UNIQUE_BOARD_ID_SIZE_BYTES + 1];
+ if (index == 3) {
+ pico_unique_board_id_t unique_id;
+ pico_get_unique_board_id(&unique_id);
+ pico_get_unique_board_id_string(unique_id_str, 2 * PICO_UNIQUE_BOARD_ID_SIZE_BYTES + 1);
+ str = unique_id_str;
+ }
+
+ chr_count = strlen(str);
+ if ( chr_count > 31 )
+ chr_count = 31;
+
+ // Convert ASCII string into UTF-16
+ for(uint8_t i=0; i.
+ */
+
+#ifndef USB_DESCRIPTORS_H_
+#define USB_DESCRIPTORS_H_
+
+enum
+{
+ VENDOR_REQUEST_WEBUSB = 1,
+ VENDOR_REQUEST_MICROSOFT = 2
+};
+
+extern uint8_t const desc_ms_os_20[];
+
+#endif /* USB_DESCRIPTORS_H_ */