diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f089e4..6b888bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ # - # This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid). + # This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). # Copyright (c) 2022 Pol Henarejos. # # This program is free software: you can redistribute it and/or modify @@ -19,14 +19,14 @@ cmake_minimum_required(VERSION 3.13) include(pico_sdk_import.cmake) -project(pico_ccid C CXX ASM) +project(pico_hsm_sdk C CXX ASM) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) pico_sdk_init() -add_executable(pico_ccid) +add_executable(pico_hsm_sdk) if (NOT DEFINED USB_VID) set(USB_VID 0xFEFF) @@ -36,36 +36,65 @@ if (NOT DEFINED USB_PID) set(USB_PID 0xFCFD) endif() add_definitions(-DUSB_PID=${USB_PID}) +if (NOT DEFINED DEBUG_APDU) + set(DEBUG_APDU 0) +endif() +if (NOT DEFINED HSM_DRIVER) + set(HSM_DRIVER "ccid") +endif() +add_definitions(-DDEBUG_APDU=${DEBUG_APDU}) configure_file(${CMAKE_CURRENT_LIST_DIR}/config/mbedtls_config.h ${CMAKE_CURRENT_LIST_DIR}/mbedtls/include/mbedtls COPYONLY) -target_sources(pico_ccid PUBLIC +message(STATUS "HSM driver: ${HSM_DRIVER}") + + +target_sources(pico_hsm_sdk PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/src/main.c ${CMAKE_CURRENT_LIST_DIR}/src/usb/usb.c - ${CMAKE_CURRENT_LIST_DIR}/src/usb/usb_descriptors.c - ${CMAKE_CURRENT_LIST_DIR}/src/ccid/ccid2040.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 - ${CMAKE_CURRENT_LIST_DIR}/src/ccid/eac.c - ${CMAKE_CURRENT_LIST_DIR}/src/ccid/crypto_utils.c - ${CMAKE_CURRENT_LIST_DIR}/src/ccid/asn1.c + ${CMAKE_CURRENT_LIST_DIR}/src/rng/hwrng.c + ${CMAKE_CURRENT_LIST_DIR}/src/eac.c + ${CMAKE_CURRENT_LIST_DIR}/src/crypto_utils.c + ${CMAKE_CURRENT_LIST_DIR}/src/asn1.c + ${CMAKE_CURRENT_LIST_DIR}/src/apdu.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 +if (${HSM_DRIVER} STREQUAL "ccid") +target_sources(pico_hsm_sdk PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/src/usb/ccid/usb_descriptors.c + ${CMAKE_CURRENT_LIST_DIR}/src/usb/ccid/ccid.c + ) + +target_include_directories(pico_hsm_sdk PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/src/usb/ccid + ) +elseif (${HSM_DRIVER} STREQUAL "hid") +target_sources(pico_hsm_sdk PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/src/usb/hid/hid.c + ${CMAKE_CURRENT_LIST_DIR}/src/usb/hid/usb_descriptors.c + ) +target_include_directories(pico_hsm_sdk PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/src/usb/hid + ) +endif() + +target_include_directories(pico_hsm_sdk PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/src ${CMAKE_CURRENT_LIST_DIR}/src/usb + ${CMAKE_CURRENT_LIST_DIR}/src/fs + ${CMAKE_CURRENT_LIST_DIR}/src/rng ${CMAKE_CURRENT_LIST_DIR}/mbedtls/include ${CMAKE_CURRENT_LIST_DIR}/mbedtls/library ) -target_compile_options(pico_ccid PUBLIC +target_compile_options(pico_hsm_sdk PUBLIC -Wall -Werror ) -pico_add_extra_outputs(pico_ccid) +pico_add_extra_outputs(pico_hsm_sdk) -target_link_libraries(pico_ccid PRIVATE pico_stdlib pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id hardware_rtc tinyusb_device tinyusb_board) +target_link_libraries(pico_hsm_sdk PRIVATE pico_stdlib pico_multicore hardware_flash hardware_sync hardware_adc pico_unique_id hardware_rtc tinyusb_device tinyusb_board) diff --git a/src/apdu.c b/src/apdu.c new file mode 100644 index 0000000..58ac39a --- /dev/null +++ b/src/apdu.c @@ -0,0 +1,151 @@ +/* + * This file is part of the Pico HSM distribution (https://github.com/polhenarejos/pico-hsm). + * 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 "apdu.h" +#include "hsm.h" +#include "usb.h" + +uint8_t *rdata_gr = NULL; +uint16_t rdata_bk = 0x0; +extern uint32_t timeout; + +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); +} + +size_t apdu_process(const uint8_t *buffer, size_t buffer_size) { + apdu.header = (uint8_t *)buffer; + apdu.nc = apdu.ne = 0; + if (buffer_size == 4) { + apdu.nc = apdu.ne = 0; + if (apdu.ne == 0) + apdu.ne = 256; + } + else if (buffer_size == 5) { + apdu.nc = 0; + apdu.ne = apdu.header[4]; + if (apdu.ne == 0) + apdu.ne = 256; + } + else if (apdu.header[4] == 0x0 && buffer_size >= 7) { + if (buffer_size == 7) { + apdu.ne = (apdu.header[5] << 8) | apdu.header[6]; + if (apdu.ne == 0) + apdu.ne = 65536; + } + else { + apdu.ne = 0; + apdu.nc = (apdu.header[5] << 8) | apdu.header[6]; + apdu.data = apdu.header+7; + if (apdu.nc+7+2 == buffer_size) { + apdu.ne = (apdu.header[buffer_size-2] << 8) | apdu.header[buffer_size-1]; + if (apdu.ne == 0) + apdu.ne = 65536; + } + } + } + else { + apdu.nc = apdu.header[4]; + apdu.data = apdu.header+5; + apdu.ne = 0; + if (apdu.nc+5+1 == buffer_size) { + apdu.ne = apdu.header[buffer_size-1]; + if (apdu.ne == 0) + apdu.ne = 256; + } + } + //printf("apdu.nc %ld, apdu.ne %ld\r\n",apdu.nc,apdu.ne); + if (apdu.header[1] == 0xc0) { + //printf("apdu.ne %ld, apdu.rlen %d, bk %x\r\n",apdu.ne,apdu.rlen,rdata_bk); + timeout_stop(); + *(uint16_t *)rdata_gr = rdata_bk; + if (apdu.rlen <= apdu.ne) { + driver_exec_finished_cont(apdu.rlen+2, rdata_gr-usb_get_tx()); + } + else { + rdata_gr += apdu.ne; + rdata_bk = *rdata_gr; + rdata_gr[0] = 0x61; + if (apdu.rlen - apdu.ne >= 256) + rdata_gr[1] = 0; + else + rdata_gr[1] = apdu.rlen - apdu.ne; + driver_exec_finished_cont(apdu.ne+2, rdata_gr-apdu.ne-usb_get_tx()); + apdu.rlen -= apdu.ne; + } + //Prepare next RAPDU + apdu.sw = 0; + apdu.rlen = 0; + usb_prepare_response(); + return 0; + } + else { + apdu.sw = 0; + apdu.rlen = 0; + apdu.rdata = usb_prepare_response(); + rdata_gr = apdu.rdata; + return apdu.nc; + } + return 0; +} + +uint16_t set_res_sw(uint8_t sw1, uint8_t sw2) { + apdu.sw = (sw1 << 8) | sw2; + if (sw1 != 0x90) + res_APDU_size = 0; + return make_uint16_t(sw1, sw2); +} + +void apdu_finish() { + apdu.rdata[apdu.rlen] = apdu.sw >> 8; + apdu.rdata[apdu.rlen+1] = apdu.sw & 0xff; + timeout_stop(); + if ((apdu.rlen + 2 + 10) % 64 == 0) + { // FIX for strange behaviour with PSCS and multiple of 64 + apdu.ne = apdu.rlen - 2; + } +} + +size_t apdu_next() { + if (apdu.rlen <= apdu.ne) + return apdu.rlen + 2; + else { + rdata_gr = apdu.rdata+apdu.ne; + rdata_bk = *(uint16_t *)rdata_gr; + rdata_gr[0] = 0x61; + if (apdu.rlen - apdu.ne >= 256) + rdata_gr[1] = 0; + else + rdata_gr[1] = apdu.rlen - apdu.ne; + apdu.rlen -= apdu.ne; + } + return apdu.ne + 2; +} + diff --git a/src/apdu.h b/src/apdu.h new file mode 100644 index 0000000..c9a8911 --- /dev/null +++ b/src/apdu.h @@ -0,0 +1,84 @@ +/* + * This file is part of the Pico HSM distribution (https://github.com/polhenarejos/pico-hsm). + * 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 _APDU_H_ +#define _APDU_H_ + +#include +#include "pico/stdlib.h" + +typedef struct app { + const uint8_t *aid; + int (*process_apdu)(); + struct app* (*select_aid)(); + int (*unload)(); +} app_t; + +extern int register_app(app_t * (*)()); + +#ifdef DEBUG_APDU +#define DEBUG_PAYLOAD(_p,_s) { \ + printf("Payload %s (%d bytes):\r\n", #_p,_s);\ + for (int _i = 0; _i < _s; _i += 16) {\ + printf("%07Xh : ",(unsigned int)(_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"); \ + } +#else +#define DEBUG_PAYLOAD(_p,_s) +#endif + +extern uint8_t num_apps; +extern app_t apps[4]; +extern app_t *current_app; + +struct apdu { + uint8_t *header; + uint32_t nc; + uint32_t ne; + uint8_t *data; + uint16_t sw; + uint8_t *rdata; + uint16_t rlen; +} __packed; + +#define CLA(a) a.header[0] +#define INS(a) a.header[1] +#define P1(a) a.header[2] +#define P2(a) a.header[3] + +#define res_APDU apdu.rdata +#define res_APDU_size apdu.rlen + +extern struct apdu apdu; + +extern uint16_t set_res_sw (uint8_t sw1, uint8_t sw2); +extern int process_apdu(); +extern size_t apdu_process(const uint8_t *buffer, size_t buffer_size); +extern void apdu_finish(); +extern size_t apdu_next(); + +#endif diff --git a/src/ccid/asn1.c b/src/asn1.c similarity index 92% rename from src/ccid/asn1.c rename to src/asn1.c index 25aa3ca..ea461c8 100644 --- a/src/ccid/asn1.c +++ b/src/asn1.c @@ -1,5 +1,5 @@ /* - * This file is part of the Pico HSM distribution (https://github.com/polhenarejos/pico-hsm). + * This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). * Copyright (c) 2022 Pol Henarejos. * * This program is free software: you can redistribute it and/or modify diff --git a/src/ccid/asn1.h b/src/asn1.h similarity index 89% rename from src/ccid/asn1.h rename to src/asn1.h index 89ca2ec..28b65f3 100644 --- a/src/ccid/asn1.h +++ b/src/asn1.h @@ -1,5 +1,5 @@ /* - * This file is part of the Pico HSM distribution (https://github.com/polhenarejos/pico-hsm). + * This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). * Copyright (c) 2022 Pol Henarejos. * * This program is free software: you can redistribute it and/or modify diff --git a/src/ccid/crypto_utils.c b/src/crypto_utils.c similarity index 98% rename from src/ccid/crypto_utils.c rename to src/crypto_utils.c index 44a8b49..3e962b3 100644 --- a/src/ccid/crypto_utils.c +++ b/src/crypto_utils.c @@ -1,5 +1,5 @@ /* - * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid). + * This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). * Copyright (c) 2022 Pol Henarejos. * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ #include "mbedtls/sha256.h" #include "mbedtls/aes.h" #include "crypto_utils.h" -#include "ccid2040.h" +#include "hsm.h" void double_hash_pin(const uint8_t *pin, size_t len, uint8_t output[32]) { uint8_t o1[32]; diff --git a/src/ccid/crypto_utils.h b/src/crypto_utils.h similarity index 95% rename from src/ccid/crypto_utils.h rename to src/crypto_utils.h index b1e3b60..b3f91ae 100644 --- a/src/ccid/crypto_utils.h +++ b/src/crypto_utils.h @@ -1,5 +1,5 @@ /* - * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid). + * This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). * Copyright (c) 2022 Pol Henarejos. * * This program is free software: you can redistribute it and/or modify diff --git a/src/ccid/eac.c b/src/eac.c similarity index 98% rename from src/ccid/eac.c rename to src/eac.c index 78c3331..322e815 100644 --- a/src/ccid/eac.c +++ b/src/eac.c @@ -1,5 +1,5 @@ /* - * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid). + * This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). * Copyright (c) 2022 Pol Henarejos. * * This program is free software: you can redistribute it and/or modify @@ -20,6 +20,7 @@ #include "random.h" #include "mbedtls/cmac.h" #include "asn1.h" +#include "apdu.h" static uint8_t nonce[8]; static uint8_t sm_kmac[16]; diff --git a/src/ccid/eac.h b/src/eac.h similarity index 91% rename from src/ccid/eac.h rename to src/eac.h index babca93..a025ad4 100644 --- a/src/ccid/eac.h +++ b/src/eac.h @@ -1,5 +1,5 @@ /* - * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid). + * This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). * Copyright (c) 2022 Pol Henarejos. * * This program is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ #include #include "pico/stdlib.h" -#include "ccid2040.h" +#include "hsm.h" typedef enum MSE_protocol { MSE_AES = 0, diff --git a/src/fs/file.c b/src/fs/file.c index 4de7abb..390dc87 100644 --- a/src/fs/file.c +++ b/src/fs/file.c @@ -1,5 +1,5 @@ /* - * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid). + * This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). * Copyright (c) 2022 Pol Henarejos. * * This program is free software: you can redistribute it and/or modify @@ -16,10 +16,11 @@ */ #include "file.h" -#include "ccid2040.h" +#include "hsm.h" #include #include #include "asn1.h" +#include "apdu.h" extern const uintptr_t end_data_pool; extern const uintptr_t start_data_pool; diff --git a/src/fs/file.h b/src/fs/file.h index 4f1a45d..dc37485 100644 --- a/src/fs/file.h +++ b/src/fs/file.h @@ -1,5 +1,5 @@ /* - * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid). + * This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). * Copyright (c) 2022 Pol Henarejos. * * This program is free software: you can redistribute it and/or modify diff --git a/src/fs/flash.c b/src/fs/flash.c index 8a9c968..bae64be 100644 --- a/src/fs/flash.c +++ b/src/fs/flash.c @@ -1,5 +1,5 @@ /* - * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid). + * This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). * Copyright (c) 2022 Pol Henarejos. * * This program is free software: you can redistribute it and/or modify @@ -21,7 +21,7 @@ #include "pico/stdlib.h" #include "hardware/flash.h" -#include "ccid2040.h" +#include "hsm.h" #include "file.h" /* diff --git a/src/fs/low_flash.c b/src/fs/low_flash.c index 76bf9fe..7ad9095 100644 --- a/src/fs/low_flash.c +++ b/src/fs/low_flash.c @@ -1,5 +1,5 @@ /* - * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid). + * This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). * Copyright (c) 2022 Pol Henarejos. * * This program is free software: you can redistribute it and/or modify @@ -26,7 +26,7 @@ #include "pico/mutex.h" #include "pico/sem.h" #include "pico/multicore.h" -#include "ccid2040.h" +#include "hsm.h" #include #define TOTAL_FLASH_PAGES 4 @@ -82,7 +82,7 @@ void do_flash() } flash_available = false; if (ready_pages != 0) { - DEBUG_INFO("ERROR: DO FLASH DOES NOT HAVE ZERO PAGES"); + printf("ERROR: DO FLASH DOES NOT HAVE ZERO PAGES\n"); } } mutex_exit(&mtx_flash); @@ -146,13 +146,13 @@ int flash_program_block(uintptr_t addr, const uint8_t *data, size_t len) { mutex_enter_blocking(&mtx_flash); if (ready_pages == TOTAL_FLASH_PAGES) { mutex_exit(&mtx_flash); - DEBUG_INFO("ERROR: ALL FLASH PAGES CACHED\r\n"); + printf("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"); + printf("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); @@ -217,11 +217,11 @@ int flash_erase_page (uintptr_t addr, size_t page_size) { mutex_enter_blocking(&mtx_flash); if (ready_pages == TOTAL_FLASH_PAGES) { mutex_exit(&mtx_flash); - DEBUG_INFO("ERROR: ALL FLASH PAGES CACHED\r\n"); + printf("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"); + printf("ERROR: FLASH CANNOT FIND A PAGE (rare error)\r\n"); mutex_exit(&mtx_flash); return CCID_ERR_MEMORY_FATAL; } diff --git a/src/ccid/ccid2040.h b/src/hsm.h similarity index 59% rename from src/ccid/ccid2040.h rename to src/hsm.h index 28cdd07..c2776dc 100644 --- a/src/ccid/ccid2040.h +++ b/src/hsm.h @@ -1,5 +1,5 @@ /* - * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid). + * This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). * Copyright (c) 2022 Pol Henarejos. * * This program is free software: you can redistribute it and/or modify @@ -15,104 +15,23 @@ * along with this program. If not, see . */ -#ifndef _CCID2040_H_ -#define _CCID2040_H_ +#ifndef _HSM_H_ +#define _HSM_H_ #include "file.h" #include "pico/unique_id.h" -#include "pico/util/queue.h" #include -#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 driver_init(); +extern void driver_task(); +extern bool wait_button(); -extern int register_app(app_t * (*)()); - -extern const uint8_t historical_bytes[]; - -#ifdef DEBUG_APDU -#define DEBUG_PAYLOAD(_p,_s) { \ - printf("Payload %s (%d bytes):\r\n", #_p,_s);\ - for (int _i = 0; _i < _s; _i += 16) {\ - printf("%07Xh : ",(unsigned int)(_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"); \ - } -#else -#define DEBUG_PAYLOAD(_p,_s) -#endif - -struct apdu { - uint8_t *header; - uint32_t nc; - uint32_t ne; - uint8_t *data; - uint16_t sw; - uint8_t *rdata; - uint16_t rlen; -} __packed; - -#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_TIMEOUT 16 -#define EV_BUTTON_PRESSED 32 - -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.header[0] -#define INS(a) a.header[1] -#define P1(a) a.header[2] -#define P2(a) a.header[3] - -#define res_APDU apdu.rdata -#define res_APDU_size apdu.rlen - -extern struct apdu apdu; - -uint16_t set_res_sw (uint8_t sw1, uint8_t sw2); +extern void low_flash_init_core1(); +extern int driver_write(const uint8_t *, size_t); +extern size_t driver_read(uint8_t *, size_t); +extern size_t usb_rx(const uint8_t *buffer, size_t len); static inline const uint16_t make_uint16_t(uint8_t b1, uint8_t b2) { return (b1 << 8) | b2; @@ -125,39 +44,6 @@ static inline const void put_uint16_t(uint16_t n, uint8_t *b) { *b = n & 0xff; } -extern const uint8_t *ccid_atr; - -extern queue_t ccid_to_card_q; -extern queue_t card_to_ccid_q; - - -#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); @@ -175,7 +61,6 @@ enum { }; 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) @@ -247,4 +132,4 @@ extern void led_set_blink(uint32_t mode); #define CCID_WRONG_PADDING -1011 #define CCID_VERIFICATION_FAILED -1012 -#endif \ No newline at end of file +#endif diff --git a/src/ccid/ccid_version.h b/src/hsm_version.h similarity index 73% rename from src/ccid/ccid_version.h rename to src/hsm_version.h index 4588b0e..b8ef5df 100644 --- a/src/ccid/ccid_version.h +++ b/src/hsm_version.h @@ -1,5 +1,5 @@ /* - * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid). + * This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). * Copyright (c) 2022 Pol Henarejos. * * This program is free software: you can redistribute it and/or modify @@ -18,10 +18,10 @@ #ifndef __VERSION_H_ #define __VERSION_H_ -#define CCID_VERSION 0x0202 +#define HSM_SDK_VERSION 0x0202 -#define CCID_VERSION_MAJOR ((CCID_VERSION >> 8) & 0xff) -#define CCID_VERSION_MINOR (CCID_VERSION & 0xff) +#define HSM_SDK_VERSION_MAJOR ((HSM_SDK_VERSION >> 8) & 0xff) +#define HSM_SDK_VERSION_MINOR (HSM_SDK_VERSION & 0xff) #endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..44c9d25 --- /dev/null +++ b/src/main.c @@ -0,0 +1,199 @@ +/* + * This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). + * 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" + +// For memcpy +#include + +// Include descriptor struct definitions +//#include "usb_common.h" +// USB register definitions from pico-sdk +#include "hardware/regs/usb.h" +// USB hardware struct definitions from pico-sdk +#include "hardware/structs/usb.h" +// For interrupt enable and numbers +#include "hardware/irq.h" +// For resetting the USB controller +#include "hardware/resets.h" + +#include "pico/multicore.h" +#include "random.h" +#include "hsm.h" +#include "apdu.h" +#include "usb.h" +#include "hardware/rtc.h" +#include "bsp/board.h" + +extern void do_flash(); +extern void low_flash_init(); + +app_t apps[4]; +uint8_t num_apps = 0; + +app_t *current_app = NULL; + +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; +} + +static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; + +void led_set_blink(uint32_t mode) { + blink_interval_ms = mode; +} + +void execute_tasks(); + +bool wait_button() { + uint32_t start_button = board_millis(); + bool timeout = false; + led_set_blink((1000 << 16) | 100); + + while (board_button_read() == false) { + execute_tasks(); + //sleep_ms(10); + if (start_button + 15000 < board_millis()) { /* timeout */ + timeout = true; + break; + } + } + if (!timeout) { + while (board_button_read() == true) { + execute_tasks(); + //sleep_ms(10); + if (start_button + 15000 < board_millis()) { /* timeout */ + timeout = true; + break; + } + } + } + led_set_blink(BLINK_PROCESSING); + return timeout; +} + +struct apdu apdu; + +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() { + usb_task(); + tud_task(); // tinyusb device task + led_blinking_task(); +} + +int main(void) { + usb_init(); + + board_init(); + stdio_init_all(); + +#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(); + + //ccid_prepare_receive(&ccid); + + while (1) { + execute_tasks(); + neug_task(); + do_flash(); + } + + return 0; +} \ No newline at end of file diff --git a/src/rng/neug.c b/src/rng/hwrng.c similarity index 95% rename from src/rng/neug.c rename to src/rng/hwrng.c index c9fbeef..f0296a6 100644 --- a/src/rng/neug.c +++ b/src/rng/hwrng.c @@ -1,5 +1,5 @@ /* - * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid). + * This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). * Copyright (c) 2022 Pol Henarejos. * * This program is free software: you can redistribute it and/or modify @@ -15,15 +15,12 @@ * 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 "hwrng.h" #include "hardware/structs/rosc.h" #include "hardware/gpio.h" #include "hardware/adc.h" diff --git a/src/rng/neug.h b/src/rng/hwrng.h similarity index 88% rename from src/rng/neug.h rename to src/rng/hwrng.h index f023ba6..a2d8b6e 100644 --- a/src/rng/neug.h +++ b/src/rng/hwrng.h @@ -1,5 +1,5 @@ /* - * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid). + * This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). * Copyright (c) 2022 Pol Henarejos. * * This program is free software: you can redistribute it and/or modify diff --git a/src/rng/random.c b/src/rng/random.c index b04af1f..0ce3b94 100644 --- a/src/rng/random.c +++ b/src/rng/random.c @@ -1,5 +1,5 @@ /* - * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid). + * This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). * Copyright (c) 2022 Pol Henarejos. * * This program is free software: you can redistribute it and/or modify @@ -19,7 +19,7 @@ #include #include -#include "neug.h" +#include "hwrng.h" #define RANDOM_BYTES_LENGTH 32 static uint32_t random_word[RANDOM_BYTES_LENGTH/sizeof (uint32_t)]; diff --git a/src/rng/random.h b/src/rng/random.h index ee7c960..b6dc6bf 100644 --- a/src/rng/random.h +++ b/src/rng/random.h @@ -1,5 +1,5 @@ /* - * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid). + * This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). * Copyright (c) 2022 Pol Henarejos. * * This program is free software: you can redistribute it and/or modify diff --git a/src/usb/ccid/ccid.c b/src/usb/ccid/ccid.c new file mode 100644 index 0000000..5a8c136 --- /dev/null +++ b/src/usb/ccid/ccid.c @@ -0,0 +1,350 @@ +/* + * This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). + * 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" + +// For memcpy +#include + +// Include descriptor struct definitions +//#include "usb_common.h" +// USB register definitions from pico-sdk +#include "hardware/regs/usb.h" +// USB hardware struct definitions from pico-sdk +#include "hardware/structs/usb.h" +// For interrupt enable and numbers +#include "hardware/irq.h" +// For resetting the USB controller +#include "hardware/resets.h" + +#include "random.h" +#include "hsm.h" +#include "hardware/rtc.h" +#include "tusb.h" +#include "ccid.h" +#include "device/usbd_pvt.h" +#include "usb_descriptors.h" +#include "apdu.h" +#include "usb.h" + +const uint8_t *ccid_atr = NULL; + +#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 + +#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 + +#define CCID_THREAD_TERMINATED 0xffff +#define CCID_ACK_TIMEOUT 0x6600 + +struct ccid_header { + uint8_t bMessageType; + uint32_t dwLength; + uint8_t bSlot; + uint8_t bSeq; + uint8_t abRFU0; + uint16_t abRFU1; + uint8_t *apdu; +} __packed; + +uint8_t ccid_status = 1; +static uint8_t itf_num; + +void ccid_write_offset(uint16_t size, uint16_t offset) { + if (*usb_get_tx() != 0x81) + DEBUG_PAYLOAD(usb_get_tx()+offset,size+10); + usb_write_offset(size+10, offset); +} + +void ccid_write(uint16_t size) { + ccid_write_offset(size, 0); +} + +struct ccid_header *ccid_response; +struct ccid_header *ccid_header; + +int driver_init() { + ccid_header = (struct ccid_header *)usb_get_rx(); + ccid_header->apdu = usb_get_rx()+10; + apdu.header = ccid_header->apdu; + + ccid_response = (struct ccid_header *)usb_get_tx(); + ccid_response->apdu = usb_get_tx()+10; + apdu.rdata = ccid_response->apdu; + + return CCID_OK; +} + +void tud_vendor_rx_cb(uint8_t itf) { + (void) itf; + + uint32_t len = tud_vendor_available(); + usb_rx(NULL, len); +} + +void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes) { + //printf("written %ld\n",sent_bytes); + usb_write_flush(); +} + +int driver_write(const uint8_t *buffer, size_t buffer_size) { + return tud_vendor_write(buffer, buffer_size); +} + +size_t driver_read(uint8_t *buffer, size_t buffer_size) { + return tud_vendor_read(buffer, buffer_size); +} + +int driver_process_usb_packet(uint16_t rx_read) { + if (rx_read >= 10) + { + //printf("%d %d %x\r\n",tccid->dwLength,rx_read-10,tccid->bMessageType); + if (ccid_header->dwLength <= rx_read-10) { + size_t apdu_size = 0; + if (ccid_header->bMessageType != 0x65) + DEBUG_PAYLOAD(usb_get_rx(),usb_read_available()); + if (ccid_header->bMessageType == 0x65) { + ccid_response->bMessageType = CCID_SLOT_STATUS_RET; + ccid_response->dwLength = 0; + ccid_response->bSlot = 0; + ccid_response->bSeq = ccid_header->bSeq; + ccid_response->abRFU0 = ccid_status; + ccid_response->abRFU1 = 0; + ccid_write(0); + } + else if (ccid_header->bMessageType == 0x62) { + size_t size_atr = (ccid_atr ? ccid_atr[0] : 0); + ccid_response->bMessageType = 0x80; + ccid_response->dwLength = size_atr; + ccid_response->bSlot = 0; + ccid_response->bSeq = ccid_header->bSeq; + ccid_response->abRFU0 = 0; + ccid_response->abRFU1 = 0; + //printf("1 %x %x %x || %x %x %x\r\n",ccid_response->apdu,apdu.rdata,ccid_response,ccid_header,ccid_header->apdu,apdu.data); + memcpy(apdu.rdata, ccid_atr+1, size_atr); + card_start(); + ccid_status = 0; + ccid_write(size_atr); + } + else if (ccid_header->bMessageType == 0x63) { + ccid_status = 1; + ccid_response->bMessageType = CCID_SLOT_STATUS_RET; + ccid_response->dwLength = 0; + ccid_response->bSlot = 0; + ccid_response->bSeq = ccid_header->bSeq; + ccid_response->abRFU0 = ccid_status; + ccid_response->abRFU1 = 0; + card_exit(); + ccid_write(0); + } + else if (ccid_header->bMessageType == 0x6F) { + apdu_size = apdu_process(ccid_header->apdu, ccid_header->dwLength); + } + usb_clear_rx(); + return apdu_size; + } + } + /* + if (usb_read_available() && c->epo->ready) { + if () + uint32_t count = usb_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; +} + +bool driver_mounted() { + return tud_vendor_mounted(); +} + +void driver_exec_timeout() { + ccid_response->bMessageType = CCID_DATA_BLOCK_RET; + ccid_response->dwLength = 0; + ccid_response->bSlot = 0; + ccid_response->bSeq = ccid_header->bSeq; + ccid_response->abRFU0 = CCID_CMD_STATUS_TIMEEXT; + ccid_response->abRFU1 = 0; + ccid_write(0); +} + +void driver_exec_finished(size_t size_next) { + ccid_response->bMessageType = CCID_DATA_BLOCK_RET; + ccid_response->dwLength = size_next; + ccid_response->bSlot = 0; + ccid_response->bSeq = ccid_header->bSeq; + ccid_response->abRFU0 = ccid_status; + ccid_response->abRFU1 = 0; + ccid_write(size_next); +} + +void driver_exec_finished_cont(size_t size_next, size_t offset) { + + ccid_response = (struct ccid_header *)(usb_get_tx()+offset-10); + ccid_response->bMessageType = CCID_DATA_BLOCK_RET; + ccid_response->dwLength = size_next; + ccid_response->bSlot = 0; + ccid_response->bSeq = ccid_header->bSeq; + ccid_response->abRFU0 = ccid_status; + ccid_response->abRFU1 = 0; + ccid_write_offset(size_next, offset-10); +} + +uint8_t *driver_prepare_response() { + ccid_response = (struct ccid_header *)usb_get_tx(); + ccid_response->apdu = usb_get_tx()+10; + return ccid_response->apdu; +} +#define USB_CONFIG_ATT_ONE TU_BIT(7) + +#define MAX_USB_POWER 1 + +static void ccid_init_cb(void) { + 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) { + //printf("------ CALLED XFER_CB\r\n"); + return vendord_xfer_cb(rhport, ep_addr, result, xferred_bytes); + //return true; +} + +static const usbd_class_driver_t 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; +} diff --git a/src/usb/ccid/ccid.h b/src/usb/ccid/ccid.h new file mode 100644 index 0000000..136f81c --- /dev/null +++ b/src/usb/ccid/ccid.h @@ -0,0 +1,49 @@ +/* + * This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). + * 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_ + +extern const uint8_t historical_bytes[]; + +#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 + +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 */ +}; + +extern const uint8_t *ccid_atr; + +extern uint8_t *usb_get_rx(); +extern uint8_t *usb_get_tx(); +extern uint32_t usb_write_offset(uint16_t len, uint16_t offset); +extern uint16_t usb_read_available(); +extern void usb_clear_rx(); +extern uint32_t usb_write_flush(); +#endif //_CCID_H_ diff --git a/src/usb/tusb_config.h b/src/usb/ccid/tusb_config.h similarity index 100% rename from src/usb/tusb_config.h rename to src/usb/ccid/tusb_config.h diff --git a/src/usb/usb_common.h b/src/usb/ccid/usb_common.h.notused similarity index 100% rename from src/usb/usb_common.h rename to src/usb/ccid/usb_common.h.notused diff --git a/src/usb/usb_descriptors.c b/src/usb/ccid/usb_descriptors.c similarity index 97% rename from src/usb/usb_descriptors.c rename to src/usb/ccid/usb_descriptors.c index 7c2d517..8f391a9 100644 --- a/src/usb/usb_descriptors.c +++ b/src/usb/ccid/usb_descriptors.c @@ -1,5 +1,5 @@ /* - * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid). + * This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). * Copyright (c) 2022 Pol Henarejos. * * This program is free software: you can redistribute it and/or modify @@ -18,7 +18,7 @@ #include "tusb.h" #include "usb_descriptors.h" #include "pico/unique_id.h" -#include "ccid_version.h" +#include "hsm_version.h" #ifndef USB_VID #define USB_VID 0xFEFF @@ -76,7 +76,7 @@ tusb_desc_device_t const desc_device = .idVendor = (USB_VID), .idProduct = (USB_PID), - .bcdDevice = CCID_VERSION, + .bcdDevice = HSM_SDK_VERSION, .iManufacturer = 1, .iProduct = 2, diff --git a/src/usb/usb_descriptors.h b/src/usb/ccid/usb_descriptors.h similarity index 92% rename from src/usb/usb_descriptors.h rename to src/usb/ccid/usb_descriptors.h index c9d482a..4adaa48 100644 --- a/src/usb/usb_descriptors.h +++ b/src/usb/ccid/usb_descriptors.h @@ -1,5 +1,5 @@ /* - * This file is part of the Pico CCID distribution (https://github.com/polhenarejos/pico-ccid). + * This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). * Copyright (c) 2022 Pol Henarejos. * * This program is free software: you can redistribute it and/or modify diff --git a/src/usb/hid/hid.c b/src/usb/hid/hid.c new file mode 100644 index 0000000..07b1a76 --- /dev/null +++ b/src/usb/hid/hid.c @@ -0,0 +1,141 @@ +/* + * This file is part of the Pico HSM SDK distribution (https://github.com/polhenarejos/pico-hsm-sdk). + * 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 "u2f_hid.h" +#include "hsm.h" +#include "hsm_version.h" +#include "apdu.h" +#include "usb.h" + +static bool mounted = false; + +void tud_mount_cb() +{ + mounted = true; +} + +bool driver_mounted() { + return mounted; +} + +U2FHID_FRAME *u2f_req, *u2f_resp; + +int driver_init() { + tud_init(BOARD_TUD_RHPORT); + u2f_req = (U2FHID_FRAME *)usb_get_rx(); + apdu.header = u2f_req->init.data; + + u2f_resp = (U2FHID_FRAME *)usb_get_tx(); + apdu.rdata = u2f_resp->init.data; + + return 0; +} +void driver_task() { + tud_task(); // tinyusb device task +} +//--------------------------------------------------------------------+ +// USB HID +//--------------------------------------------------------------------+ + +// Invoked when received GET_REPORT control request +// Application must fill buffer report's content and return its length. +// Return zero will cause the stack to STALL request +uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) +{ + // TODO not Implemented + (void) itf; + (void) report_id; + (void) report_type; + (void) buffer; + (void) reqlen; + printf("get_report\n"); + DEBUG_PAYLOAD(buffer, reqlen); + + return 0; +} + +int driver_write(const uint8_t *buffer, size_t buffer_size) { + return tud_hid_report(0, buffer, buffer_size); +} + +size_t driver_read(uint8_t *buffer, size_t buffer_size) { + return 0; +} + +// Invoked when received SET_REPORT control request or +// received data on OUT endpoint ( Report ID = 0, Type = 0 ) +void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) +{ + // This example doesn't use multiple report and report ID + (void) itf; + (void) report_id; + (void) report_type; + printf("set report\n"); + usb_rx(buffer, bufsize); +} + +void hid_write_offset(uint16_t size, uint16_t offset) { + if (*usb_get_tx() != 0x81) + DEBUG_PAYLOAD(usb_get_tx()+offset,size+10); + usb_write_offset(size, offset); +} + +void hid_write(uint16_t size) { + hid_write_offset(size, 0); +} + +int driver_process_usb_packet(uint16_t read) { + if (read >= 10) + { + if (FRAME_TYPE(u2f_req) == TYPE_INIT) { + printf("command %x\n", FRAME_CMD(u2f_req)); + printf("len %d\n", MSG_LEN(u2f_req)); + DEBUG_PAYLOAD(u2f_req->init.data, MSG_LEN(u2f_req)); + } + if (u2f_req->init.cmd == U2FHID_INIT) { + U2FHID_INIT_REQ *req = (U2FHID_INIT_REQ *)u2f_req->init.data; + U2FHID_INIT_RESP *resp = (U2FHID_INIT_RESP *)u2f_resp->init.data; + memcpy(resp->nonce, req->nonce, sizeof(resp->nonce)); + resp->cid = 0x01000000; + resp->versionInterface = U2FHID_IF_VERSION; + resp->versionMajor = HSM_SDK_VERSION_MAJOR; + resp->versionMinor = HSM_SDK_VERSION_MINOR; + resp->capFlags = CAPFLAG_WINK; + + u2f_resp->cid = CID_BROADCAST; + u2f_resp->init.cmd = U2FHID_INIT; + u2f_resp->init.bcntl = 17; + u2f_resp->init.bcnth = 0; + hid_write(64); + DEBUG_PAYLOAD((uint8_t *)u2f_resp, u2f_resp->init.bcntl+7); + } + // echo back anything we received from host + //tud_hid_report(0, buffer, bufsize); + printf("END\n"); + usb_clear_rx(); + } + return 0; +} + +void driver_exec_timeout() { + +} + +void driver_exec_finished(size_t size_next) { + +} diff --git a/src/usb/hid/tusb_config.h b/src/usb/hid/tusb_config.h new file mode 100644 index 0000000..56743c1 --- /dev/null +++ b/src/usb/hid/tusb_config.h @@ -0,0 +1,115 @@ +/* + * 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 + +//--------------------------------------------------------------------+ +// Board Specific Configuration +//--------------------------------------------------------------------+ + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_TUD_RHPORT +#define BOARD_TUD_RHPORT 0 +#endif + +// RHPort max operational speed can defined by board.mk +#ifndef BOARD_TUD_MAX_SPEED +#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU +#error CFG_TUSB_MCU must be defined +#endif + +#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 +#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) +#else +#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_PICO +#endif + +#ifndef CFG_TUSB_DEBUG +#define CFG_TUSB_DEBUG 1 +#endif + +// Enable Device stack +#define CFG_TUD_ENABLED 1 + +// Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED + +/* 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_CDC 0 +#define CFG_TUD_MSC 0 +#define CFG_TUD_HID 1 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 + +// HID buffer size Should be sufficient to hold ID (if any) + Data +#define CFG_TUD_HID_EP_BUFSIZE 64 + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ + diff --git a/src/usb/hid/u2f.h b/src/usb/hid/u2f.h new file mode 100644 index 0000000..0ca7f2e --- /dev/null +++ b/src/usb/hid/u2f.h @@ -0,0 +1,107 @@ +// Common U2F raw message format header - Review Draft +// 2014-10-08 +// Editor: Jakob Ehrensvard, Yubico, jakob@yubico.com + +#ifndef __U2F_H_INCLUDED__ +#define __U2F_H_INCLUDED__ + +#ifdef _MSC_VER // Windows +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long int uint64_t; +#else +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// General constants + +#define U2F_EC_KEY_SIZE 32 // EC key size in bytes +#define U2F_EC_POINT_SIZE ((U2F_EC_KEY_SIZE * 2) + 1) // Size of EC point +#define U2F_MAX_KH_SIZE 128 // Max size of key handle +#define U2F_MAX_ATT_CERT_SIZE 2048 // Max size of attestation certificate +#define U2F_MAX_EC_SIG_SIZE 72 // Max size of DER coded EC signature +#define U2F_CTR_SIZE 4 // Size of counter field +#define U2F_APPID_SIZE 32 // Size of application id +#define U2F_CHAL_SIZE 32 // Size of challenge + +#define ENC_SIZE(x) ((x + 7) & 0xfff8) + +// EC (uncompressed) point + +#define U2F_POINT_UNCOMPRESSED 0x04 // Uncompressed point format + +typedef struct { + uint8_t pointFormat; // Point type + uint8_t x[U2F_EC_KEY_SIZE]; // X-value + uint8_t y[U2F_EC_KEY_SIZE]; // Y-value +} U2F_EC_POINT; + +// U2F native commands + +#define U2F_REGISTER 0x01 // Registration command +#define U2F_AUTHENTICATE 0x02 // Authenticate/sign command +#define U2F_VERSION 0x03 // Read version string command + +#define U2F_VENDOR_FIRST 0x40 // First vendor defined command +#define U2F_VENDOR_LAST 0xbf // Last vendor defined command + +// U2F_CMD_REGISTER command defines + +#define U2F_REGISTER_ID 0x05 // Version 2 registration identifier +#define U2F_REGISTER_HASH_ID 0x00 // Version 2 hash identintifier + +typedef struct { + uint8_t chal[U2F_CHAL_SIZE]; // Challenge + uint8_t appId[U2F_APPID_SIZE]; // Application id +} U2F_REGISTER_REQ; + +typedef struct { + uint8_t registerId; // Registration identifier (U2F_REGISTER_ID_V2) + U2F_EC_POINT pubKey; // Generated public key + uint8_t keyHandleLen; // Length of key handle + uint8_t keyHandleCertSig[ + U2F_MAX_KH_SIZE + // Key handle + U2F_MAX_ATT_CERT_SIZE + // Attestation certificate + U2F_MAX_EC_SIG_SIZE]; // Registration signature +} U2F_REGISTER_RESP; + +// U2F_CMD_AUTHENTICATE command defines + +// Authentication control byte + +#define U2F_AUTH_ENFORCE 0x03 // Enforce user presence and sign +#define U2F_AUTH_CHECK_ONLY 0x07 // Check only +#define U2F_AUTH_FLAG_TUP 0x01 // Test of user presence set + +typedef struct { + uint8_t chal[U2F_CHAL_SIZE]; // Challenge + uint8_t appId[U2F_APPID_SIZE]; // Application id + uint8_t keyHandleLen; // Length of key handle + uint8_t keyHandle[U2F_MAX_KH_SIZE]; // Key handle +} U2F_AUTHENTICATE_REQ; + +typedef struct { + uint8_t flags; // U2F_AUTH_FLAG_ values + uint8_t ctr[U2F_CTR_SIZE]; // Counter field (big-endian) + uint8_t sig[U2F_MAX_EC_SIG_SIZE]; // Signature +} U2F_AUTHENTICATE_RESP; + +// Command status responses + +#define U2F_SW_NO_ERROR 0x9000 // SW_NO_ERROR +#define U2F_SW_WRONG_DATA 0x6A80 // SW_WRONG_DATA +#define U2F_SW_CONDITIONS_NOT_SATISFIED 0x6985 // SW_CONDITIONS_NOT_SATISFIED +#define U2F_SW_COMMAND_NOT_ALLOWED 0x6986 // SW_COMMAND_NOT_ALLOWED +#define U2F_SW_INS_NOT_SUPPORTED 0x6D00 // SW_INS_NOT_SUPPORTED + +#ifdef __cplusplus +} +#endif + +#endif // __U2F_H_INCLUDED__ + diff --git a/src/usb/hid/u2f_hid.h b/src/usb/hid/u2f_hid.h new file mode 100644 index 0000000..bd26374 --- /dev/null +++ b/src/usb/hid/u2f_hid.h @@ -0,0 +1,127 @@ +// Common U2F HID transport header - Review Draft +// 2014-10-08 +// Editor: Jakob Ehrensvard, Yubico, jakob@yubico.com + +#ifndef __U2FHID_H_INCLUDED__ +#define __U2FHID_H_INCLUDED__ + +#ifdef _MSC_VER // Windows +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long int uint64_t; +#else +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// Size of HID reports + +#define HID_RPT_SIZE 64 // Default size of raw HID report + +// Frame layout - command- and continuation frames + +#define CID_BROADCAST 0xffffffff // Broadcast channel id + +#define TYPE_MASK 0x80 // Frame type mask +#define TYPE_INIT 0x80 // Initial frame identifier +#define TYPE_CONT 0x00 // Continuation frame identifier + +typedef struct { + uint32_t cid; // Channel identifier + union { + uint8_t type; // Frame type - b7 defines type + struct { + uint8_t cmd; // Command - b7 set + uint8_t bcnth; // Message byte count - high part + uint8_t bcntl; // Message byte count - low part + uint8_t data[HID_RPT_SIZE - 7]; // Data payload + } init; + struct { + uint8_t seq; // Sequence number - b7 cleared + uint8_t data[HID_RPT_SIZE - 5]; // Data payload + } cont; + }; +}__packed U2FHID_FRAME; + +#define FRAME_TYPE(f) ((f)->type & TYPE_MASK) +#define FRAME_CMD(f) ((f)->init.cmd & ~TYPE_MASK) +#define MSG_LEN(f) ((f)->init.bcnth*256 + (f)->init.bcntl) +#define FRAME_SEQ(f) ((f)->cont.seq & ~TYPE_MASK) + +// HID usage- and usage-page definitions + +#define FIDO_USAGE_PAGE 0xf1d0 // FIDO alliance HID usage page +#define FIDO_USAGE_U2FHID 0x01 // U2FHID usage for top-level collection +#define FIDO_USAGE_DATA_IN 0x20 // Raw IN data report +#define FIDO_USAGE_DATA_OUT 0x21 // Raw OUT data report + +// General constants + +#define U2FHID_IF_VERSION 2 // Current interface implementation version +#define U2FHID_TRANS_TIMEOUT 3000 // Default message timeout in ms + +// U2FHID native commands + +#define U2FHID_PING (TYPE_INIT | 0x01) // Echo data through local processor only +#define U2FHID_MSG (TYPE_INIT | 0x03) // Send U2F message frame +#define U2FHID_LOCK (TYPE_INIT | 0x04) // Send lock channel command +#define U2FHID_INIT (TYPE_INIT | 0x06) // Channel initialization +#define U2FHID_WINK (TYPE_INIT | 0x08) // Send device identification wink +#define U2FHID_SYNC (TYPE_INIT | 0x3c) // Protocol resync command +#define U2FHID_ERROR (TYPE_INIT | 0x3f) // Error response + +#define U2FHID_VENDOR_FIRST (TYPE_INIT | 0x40) // First vendor defined command +#define U2FHID_VENDOR_LAST (TYPE_INIT | 0x7f) // Last vendor defined command + +// U2FHID_INIT command defines + +#define INIT_NONCE_SIZE 8 // Size of channel initialization challenge +#define CAPFLAG_WINK 0x01 // Device supports WINK command + +typedef struct { + uint8_t nonce[INIT_NONCE_SIZE]; // Client application nonce +}__packed U2FHID_INIT_REQ; + +typedef struct { + uint8_t nonce[INIT_NONCE_SIZE]; // Client application nonce + uint32_t cid; // Channel identifier + uint8_t versionInterface; // Interface version + uint8_t versionMajor; // Major version number + uint8_t versionMinor; // Minor version number + uint8_t versionBuild; // Build version number + uint8_t capFlags; // Capabilities flags +}__packed U2FHID_INIT_RESP; + +// U2FHID_SYNC command defines + +typedef struct { + uint8_t nonce; // Client application nonce +} U2FHID_SYNC_REQ; + +typedef struct { + uint8_t nonce; // Client application nonce +} U2FHID_SYNC_RESP; + +// Low-level error codes. Return as negatives. + +#define ERR_NONE 0x00 // No error +#define ERR_INVALID_CMD 0x01 // Invalid command +#define ERR_INVALID_PAR 0x02 // Invalid parameter +#define ERR_INVALID_LEN 0x03 // Invalid message length +#define ERR_INVALID_SEQ 0x04 // Invalid message sequencing +#define ERR_MSG_TIMEOUT 0x05 // Message has timed out +#define ERR_CHANNEL_BUSY 0x06 // Channel busy +#define ERR_LOCK_REQUIRED 0x0a // Command requires channel lock +#define ERR_SYNC_FAIL 0x0b // SYNC command failed +#define ERR_OTHER 0x7f // Other unspecified error + +#ifdef __cplusplus +} +#endif + +#endif // __U2FHID_H_INCLUDED__ + diff --git a/src/usb/hid/usb_descriptors.c b/src/usb/hid/usb_descriptors.c new file mode 100644 index 0000000..d49d470 --- /dev/null +++ b/src/usb/hid/usb_descriptors.c @@ -0,0 +1,192 @@ +/* + * 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. + * + */ + +#include "tusb.h" +#include "u2f_hid.h" + +/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. + * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. + * + * Auto ProductID layout's Bitmap: + * [MSB] HID | MSC | CDC [LSB] + */ + + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ +tusb_desc_device_t const desc_device = +{ + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = 0xCafe, + .idProduct = 0x4231, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01 +}; + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const * tud_descriptor_device_cb(void) +{ + return (uint8_t const *) &desc_device; +} + +//--------------------------------------------------------------------+ +// HID Report Descriptor +//--------------------------------------------------------------------+ + +#define TUD_HID_REPORT_DESC_U2F(report_size, ...) \ + HID_USAGE_PAGE_N ( FIDO_USAGE_PAGE, 2 ),\ + HID_USAGE ( FIDO_USAGE_U2FHID ),\ + HID_COLLECTION ( HID_COLLECTION_APPLICATION ),\ + /* Report ID if any */\ + __VA_ARGS__ \ + /* Input */ \ + HID_USAGE ( FIDO_USAGE_DATA_IN ),\ + HID_LOGICAL_MIN ( 0x00 ),\ + HID_LOGICAL_MAX_N ( 0xff, 2 ),\ + HID_REPORT_SIZE ( 8 ),\ + HID_REPORT_COUNT( report_size ),\ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\ + /* Output */ \ + HID_USAGE ( FIDO_USAGE_DATA_OUT ),\ + HID_LOGICAL_MIN ( 0x00 ),\ + HID_LOGICAL_MAX_N ( 0xff, 2 ),\ + HID_REPORT_SIZE ( 8 ),\ + HID_REPORT_COUNT( report_size ),\ + HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\ + HID_COLLECTION_END \ + +uint8_t const desc_hid_report[] = +{ + TUD_HID_REPORT_DESC_U2F(CFG_TUD_HID_EP_BUFSIZE) +}; + +// Invoked when received GET HID REPORT DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf) +{ + printf("report_cb %d\n", itf); + return desc_hid_report; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +enum +{ + ITF_NUM_HID, + ITF_NUM_TOTAL +}; + +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_INOUT_DESC_LEN) + +#define EPNUM_HID 0x01 + +uint8_t const desc_configuration[] = +{ + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, protocol, report descriptor len, EP In & Out address, size & polling interval + TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, 0x80 | EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 10) +}; + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const * tud_descriptor_configuration_cb(uint8_t index) +{ + (void) index; // for multiple configurations + return desc_configuration; +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// array of pointer to string descriptors +char const* string_desc_arr [] = +{ + (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + "123456", // 3: Serials, should use chip ID +}; + +static uint16_t _desc_str[32]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +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]; + + // Cap at max char + chr_count = (uint8_t) strlen(str); + if ( chr_count > 31 ) chr_count = 31; + + // Convert ASCII string into UTF-16 + for(uint8_t i=0; i // Pico #include "pico/stdlib.h" +#include "pico/multicore.h" #include "tusb.h" -#include "device/usbd_pvt.h" -#include "usb_descriptors.h" +#include "hsm.h" +#include "usb.h" +#include "apdu.h" + +#include "bsp/board.h" // For memcpy #include @@ -34,7 +37,6 @@ // Device specific functions static uint8_t rx_buffer[4096], tx_buffer[4096]; static uint16_t w_offset = 0, r_offset = 0; -static uint8_t itf_num; static uint16_t w_len = 0, tx_r_offset = 0; uint32_t usb_write_offset(uint16_t len, uint16_t offset) { @@ -43,16 +45,27 @@ uint32_t usb_write_offset(uint16_t len, uint16_t offset) { len = sizeof(tx_buffer); w_len = len; tx_r_offset = offset; - tud_vendor_write(tx_buffer+offset, MIN(len, pkt_max)); + driver_write(tx_buffer+offset, MIN(len, pkt_max)); w_len -= MIN(len, pkt_max); tx_r_offset += MIN(len, pkt_max); return MIN(w_len, pkt_max); } +size_t usb_rx(const uint8_t *buffer, size_t len) { + uint16_t size = MIN(sizeof(rx_buffer) - w_offset, len); + if (size > 0) { + if (buffer == NULL) + size = driver_read(rx_buffer + w_offset, size); + else + memcpy(rx_buffer + w_offset, buffer, size); + w_offset += size; + } + return size; +} + uint32_t usb_write_flush() { - if (w_len > 0 && tud_vendor_write_available() > 0) { - //printf("w_len %d %d %ld\r\n",w_len,tx_r_offset,tud_vendor_write_available()); - tud_vendor_write(tx_buffer+tx_r_offset, MIN(w_len, 64)); + if (w_len > 0) { + driver_write(tx_buffer+tx_r_offset, MIN(w_len, 64)); tx_r_offset += MIN(w_len, 64); w_len -= MIN(w_len, 64); } @@ -74,6 +87,7 @@ uint16_t usb_write_available() { uint8_t *usb_get_rx() { return rx_buffer; } + uint8_t *usb_get_tx() { return tx_buffer; } @@ -95,21 +109,6 @@ uint16_t usb_read(uint8_t *buffer, size_t buffer_size) { return 0; } -void tud_vendor_rx_cb(uint8_t itf) { - (void) itf; - uint32_t len = tud_vendor_available(); - uint16_t size = MIN(sizeof(rx_buffer)-w_offset, len); - if (size > 0) { - size = tud_vendor_read(rx_buffer+w_offset, size); - w_offset += size; - } -} - -void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes) { - //printf("written %ld\n",sent_bytes); - usb_write_flush(); -} - #ifndef USB_VID #define USB_VID 0xFEFF #endif @@ -119,99 +118,167 @@ void tud_vendor_tx_cb(uint8_t itf, uint32_t sent_bytes) { #define USB_BCD 0x0200 -#define USB_CONFIG_ATT_ONE TU_BIT(7) +uint32_t timeout = 0; -#define MAX_USB_POWER 1 +queue_t usb_to_card_q; +queue_t card_to_usb_q; -static void ccid_init_cb(void) { - TU_LOG1("-------- CCID INIT\r\n"); - vendord_init(); - - //ccid_notify_slot_change(c); +void usb_init() { + queue_init(&card_to_usb_q, sizeof(uint32_t), 64); + queue_init(&usb_to_card_q, sizeof(uint32_t), 64); + driver_init(); } -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; +static int usb_event_handle() { + uint16_t rx_read = usb_read_available(); + if (driver_process_usb_packet(rx_read) > 0) { + uint32_t flag = EV_CMD_AVAILABLE; + queue_add_blocking(&usb_to_card_q, &flag); + timeout_start(); } - return false; + + return 0; } -static bool ccid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { - //printf("------ CALLED XFER_CB\r\n"); - return vendord_xfer_cb(rhport, ep_addr, result, xferred_bytes); - //return true; +static void card_init_core1(void) { + //gpg_data_scan (flash_do_start, flash_do_end); + low_flash_init_core1(); } -static const usbd_class_driver_t 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 -}; +void card_thread() { + card_init_core1(); -// 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; + while (1) { + uint32_t m; + queue_remove_blocking(&usb_to_card_q, &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(&card_to_usb_q, &flag); + } + //printf("EXIT !!!!!!\r\n"); + if (current_app && current_app->unload) + current_app->unload(); } + +void card_thread(); +void card_start() +{ + multicore_reset_core1(); + multicore_launch_core1(card_thread); + led_set_blink(BLINK_MOUNTED); +} + +void card_exit() { + uint32_t flag = EV_EXIT; + queue_try_add(&usb_to_card_q, &flag); + led_set_blink(BLINK_SUSPENDED); +} + +void usb_task() { + if (driver_mounted()) { + if (usb_event_handle() != 0) { + + } + usb_write_flush(); + uint32_t m = 0x0; + bool has_m = queue_try_remove(&card_to_usb_q, &m); + //if (m != 0) + // printf("\r\n ------ M = %lu\r\n",m); + if (has_m) { + if (m == EV_EXEC_FINISHED) { + apdu_finish(); + size_t size_next = apdu_next(); + driver_exec_finished(size_next); + led_set_blink(BLINK_MOUNTED); + } + else if (m == EV_PRESS_BUTTON) { + uint32_t flag = wait_button() ? EV_BUTTON_TIMEOUT : EV_BUTTON_PRESSED; + queue_try_add(&usb_to_card_q, &flag); + } + /* + 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 == CCID_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 (timeout > 0) { + if (timeout + 1500 < board_millis()) { + driver_exec_timeout(); + timeout = board_millis(); + } + } + } + } +} + +void timeout_stop() { + timeout = 0; +} + +void timeout_start() { + timeout = board_millis(); +} + +uint8_t *usb_prepare_response() { + return driver_prepare_response(); +} \ No newline at end of file diff --git a/src/usb/usb.h b/src/usb/usb.h new file mode 100644 index 0000000..7604784 --- /dev/null +++ b/src/usb/usb.h @@ -0,0 +1,59 @@ +/* + * This file is part of the Pico HSM distribution (https://github.com/polhenarejos/pico-hsm). + * 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 _USB_H_ +#define _USB_H_ + +#include "pico/util/queue.h" + +/* USB thread */ +#define EV_CARD_CHANGE 1 +#define EV_TX_FINISHED 2 +#define EV_EXEC_ACK_REQUIRED 4 +#define EV_EXEC_FINISHED 8 +#define EV_RX_DATA_READY 16 +#define EV_PRESS_BUTTON 32 + +/* Card 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_TIMEOUT 16 +#define EV_BUTTON_PRESSED 32 + +extern void usb_task(); +extern queue_t usb_to_card_q; +extern queue_t card_to_usb_q; +extern int driver_process_usb_packet(uint16_t rx_read); +extern void driver_exec_finished(size_t size_next); +extern void driver_exec_finished_cont(size_t size_next, size_t offset); +extern void driver_exec_timeout(); +extern bool driver_mounted(); +extern uint8_t *driver_prepare_response(); + +extern void card_start(); +extern void card_exit(); +extern void usb_init(); +extern uint8_t *usb_prepare_response(); +extern void timeout_stop(); +extern void timeout_start(); +extern uint8_t *usb_get_rx(); +extern uint8_t *usb_get_tx(); +extern uint32_t usb_write_offset(uint16_t len, uint16_t offset); +extern void usb_clear_rx(); +#endif